/*
* ClassLevelTableModel.java
* Copyright 2010 (C) Connor Petty <cpmeister@users.sourceforge.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Created on May 11, 2010, 2:01:06 PM
*/
package pcgen.gui2.tabs.summary;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Map;
import javax.swing.AbstractCellEditor;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import pcgen.facade.core.CharacterFacade;
import pcgen.facade.core.CharacterLevelFacade;
import pcgen.facade.core.CharacterLevelsFacade;
import pcgen.facade.core.CharacterLevelsFacade.CharacterLevelEvent;
import pcgen.facade.core.CharacterLevelsFacade.ClassListener;
import pcgen.facade.core.CharacterLevelsFacade.HitPointListener;
import pcgen.facade.core.ClassFacade;
import pcgen.facade.util.event.ListEvent;
import pcgen.facade.util.event.ListListener;
import pcgen.gui2.tabs.Utilities;
import pcgen.gui2.util.SignIcon.Sign;
import pcgen.gui2.util.table.TableCellUtilities;
public class ClassLevelTableModel extends AbstractTableModel
implements ListListener<CharacterLevelFacade>, ItemListener, PropertyChangeListener, HitPointListener, ClassListener
{
private CharacterLevelsFacade levels;
private Map<String, Integer> finalLevelMap;
private CharacterFacade character;
private Editor editor = new Editor();
private Editor renderer = new Editor();
private JComboBox classComboBox;
private JTable classTable;
public ClassLevelTableModel(CharacterFacade character, JTable table, JComboBox comboBox)
{
super();
this.character = character;
this.levels = character.getCharacterLevelsFacade();
this.finalLevelMap = new HashMap<>();
resetLevelMap();
levels.addListListener(this);
levels.addClassListener(this);
levels.addHitPointListener(this);
this.classComboBox = comboBox;
this.classTable = table;
}
public static void initializeTable(JTable classLevelTable)
{
JTableHeader tableHeader = classLevelTable.getTableHeader();
tableHeader.setResizingAllowed(false);
tableHeader.setReorderingAllowed(false);
TableColumnModel columnModel = new DefaultTableColumnModel();
TableCellRenderer headerRenderer = tableHeader.getDefaultRenderer();
columnModel.addColumn(Utilities.createTableColumn(0, "Level", headerRenderer, false));
columnModel.addColumn(Utilities.createTableColumn(1, "HP", headerRenderer, false));
columnModel.addColumn(Utilities.createTableColumn(2, "Class (All Levels In Class)", headerRenderer, true));
classLevelTable.setColumnModel(columnModel);
classLevelTable.setAutoCreateColumnsFromModel(false);
classLevelTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
classLevelTable.setFocusable(false);
classLevelTable.setCellSelectionEnabled(false);
classLevelTable.setRowHeight(20);
}
public void install()
{
classTable.setModel(this);
classTable.setDefaultRenderer(Object.class, renderer);
classTable.setDefaultRenderer(Integer.class, new TableCellUtilities.AlignRenderer(SwingConstants.CENTER));
classTable.setDefaultEditor(Object.class, editor);
classComboBox.addItemListener(this);
classComboBox.addPropertyChangeListener("model", this);
}
public void uninstall()
{
classComboBox.removeItemListener(this);
classComboBox.removePropertyChangeListener("model", this);
}
private void resetLevelMap()
{
finalLevelMap.clear();
for (int i = levels.getSize() - 1; i >= 0; i--)
{
String c = levels.getClassTaken(levels.getElementAt(i)).getKeyName();
if (!finalLevelMap.containsKey(c))
{
finalLevelMap.put(c, i);
}
}
}
@Override
public int getRowCount()
{
return levels.getSize() + 1;
}
@Override
public int getColumnCount()
{
return 3;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return rowIndex >= levels.getSize() - 1 && columnIndex == 2;
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
switch (columnIndex)
{
case 0:
case 1:
return Integer.class;
default:
return Object.class;
}
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
if (rowIndex == levels.getSize())
{
return null;
}
switch (columnIndex)
{
case 0:
return rowIndex + 1;
case 1:
return levels.getHPGained(levels.getElementAt(rowIndex));
case 2:
ClassFacade c = levels.getClassTaken(levels.getElementAt(rowIndex));
String classKey = c.getKeyName();
if (finalLevelMap.get(classKey) == rowIndex)
{
return c.toString() + " (" + character.getClassLevel(c) + ")";
}
return c.toString();
default:
return null;
}
}
@Override
public void elementAdded(ListEvent<CharacterLevelFacade> e)
{
editor.cancelCellEditing();
int i = e.getIndex();
String c = levels.getClassTaken(levels.getElementAt(i)).getKeyName();
finalLevelMap.put(c, i);
// Do a full refresh as the previous max class level row may be affected
fireTableDataChanged();
}
@Override
public void elementRemoved(ListEvent<CharacterLevelFacade> e)
{
editor.cancelCellEditing();
resetLevelMap();
// Do a full refresh as the new max class level row may be affected
fireTableDataChanged();
}
@Override
public void elementsChanged(ListEvent<CharacterLevelFacade> e)
{
editor.cancelCellEditing();
resetLevelMap();
fireTableDataChanged();
}
@Override
public void elementModified(ListEvent<CharacterLevelFacade> e)
{
// Do a full refresh as the new max class level row may be affected
fireTableDataChanged();
}
@Override
public void itemStateChanged(ItemEvent e)
{
if (e.getStateChange() == ItemEvent.SELECTED)
{
fireTableRowsUpdated(levels.getSize(), levels.getSize());
}
}
@Override
public void propertyChange(PropertyChangeEvent evt)
{
fireTableRowsUpdated(levels.getSize(), levels.getSize());
}
@Override
public void classChanged(CharacterLevelEvent e)
{
levelChanged(e);
}
@Override
public void hitPointsChanged(CharacterLevelEvent e)
{
levelChanged(e);
}
private void levelChanged(CharacterLevelEvent e)
{
int firstRow = e.getBaseLevelIndex();
int lastRow = e.affectsHigherLevels() ? levels.getSize() - 1 : firstRow;
fireTableRowsUpdated(firstRow, lastRow);
}
private class Editor extends AbstractCellEditor
implements TableCellEditor, TableCellRenderer, ActionListener
{
private JPanel cellPanel = new JPanel();
private JLabel cellLabel = new JLabel();
private JButton addLevelButton = Utilities.createSignButton(Sign.Plus);
private JButton removeLevelButton = Utilities.createSignButton(Sign.Minus);
public Editor()
{
super();
cellPanel.setLayout(new BoxLayout(cellPanel, BoxLayout.X_AXIS));
cellPanel.setOpaque(true);
addLevelButton.setFocusable(false);
removeLevelButton.setFocusable(false);
addLevelButton.addActionListener(this);
removeLevelButton.addActionListener(this);
}
@Override
public Object getCellEditorValue()
{
return null;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
return getTableCellEditorComponent(table, value, isSelected, row, column);
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
{
cellPanel.removeAll();
TableCellUtilities.setToRowBackground(cellPanel, table, row);
cellLabel.setForeground(table.getForeground());
cellLabel.setFont(table.getFont());
if (row == levels.getSize() - 1)
{
cellLabel.setText(value.toString());
cellPanel.add(cellLabel);
cellPanel.add(Box.createHorizontalGlue());
cellPanel.add(removeLevelButton);
}
else if (row == levels.getSize())
{
cellLabel.setText("Add Level");
cellPanel.add(Box.createHorizontalGlue());
cellPanel.add(cellLabel);
cellPanel.add(Box.createHorizontalStrut(3));
addLevelButton.setEnabled(classComboBox.getSelectedItem() != null);
cellPanel.add(addLevelButton);
}
else
{
cellLabel.setText(value.toString());
cellPanel.add(cellLabel);
}
return cellPanel;
}
@Override
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == addLevelButton)
{
ClassFacade c = (ClassFacade) classComboBox.getSelectedItem();
if (c != null)
{
character.addCharacterLevels(new ClassFacade[]
{
c
});
}
}
else
{
character.removeCharacterLevels(1);
}
cancelCellEditing();
}
}
}