/*
* ClassInfoTab.java
* Copyright 2008 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 Jun 27, 2008, 1:36:26 PM
*/
package pcgen.gui2.tabs;
import java.awt.BorderLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.TransferHandler;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import pcgen.facade.core.CharacterFacade;
import pcgen.facade.core.CharacterLevelFacade;
import pcgen.facade.core.CharacterLevelsFacade;
import pcgen.facade.core.ClassFacade;
import pcgen.facade.util.event.ListEvent;
import pcgen.facade.util.event.ListListener;
import pcgen.facade.util.DefaultListFacade;
import pcgen.facade.util.ListFacade;
import pcgen.gui2.filter.Filter;
import pcgen.gui2.filter.FilterBar;
import pcgen.gui2.filter.FilterButton;
import pcgen.gui2.filter.FilteredTreeViewTable;
import pcgen.gui2.filter.SearchFilterPanel;
import pcgen.gui2.tabs.models.CharacterTreeCellRenderer.Handler;
import pcgen.gui2.tabs.models.QualifiedTreeCellRenderer;
import pcgen.gui2.tools.FlippingSplitPane;
import pcgen.gui2.tools.Icons;
import pcgen.gui2.tools.InfoPane;
import pcgen.gui2.util.table.TableUtils;
import pcgen.gui2.util.treeview.DataView;
import pcgen.gui2.util.treeview.DataViewColumn;
import pcgen.gui2.util.treeview.DefaultDataViewColumn;
import pcgen.gui2.util.treeview.TreeView;
import pcgen.gui2.util.treeview.TreeViewModel;
import pcgen.gui2.util.treeview.TreeViewPath;
import pcgen.system.LanguageBundle;
import pcgen.util.enumeration.Tab;
/**
*
* @author Connor Petty <cpmeister@users.sourceforge.net>
*/
@SuppressWarnings("serial")
public class ClassInfoTab extends FlippingSplitPane implements CharacterInfoTab
{
/**
* The table of available classes
*/
private final FilteredTreeViewTable<Object, ClassFacade> availableTable;
/**
* The table of the character's classes
*/
private final JTable classTable;
private final JButton addButton;
private final JButton removeButton;
private final TabTitle tabTitle;
private final InfoPane infoPane;
private final JSpinner spinner;
private final FilterButton<Object, ClassFacade> qFilterButton;
private final QualifiedTreeCellRenderer qualifiedRenderer;
private final ClassTransferHandler classTransferHandler;
private int spinnerValue;
public ClassInfoTab()
{
super("Class");
this.availableTable = new FilteredTreeViewTable<>();
this.classTable = TableUtils.createDefaultTable();
this.addButton = new JButton();
this.removeButton = new JButton();
this.tabTitle = new TabTitle(Tab.CLASSES);
this.infoPane = new InfoPane(LanguageBundle.getString("in_clInfo")); //$NON-NLS-1$
this.spinner = new JSpinner(new SpinnerNumberModel(1, 1, 50, 1));
this.qFilterButton = new FilterButton<>("ClassQualified");
this.qualifiedRenderer = new QualifiedTreeCellRenderer();
this.classTransferHandler = new ClassTransferHandler();
initComponents();
}
private void initComponents()
{
FlippingSplitPane topPane = new FlippingSplitPane("ClassTop");
setTopComponent(topPane);
setOrientation(VERTICAL_SPLIT);
JPanel availPanel = new JPanel(new BorderLayout());
FilterBar<Object, ClassFacade> bar = new FilterBar<>();
bar.addDisplayableFilter(new SearchFilterPanel());
qFilterButton.setText(LanguageBundle.getString("in_igQualFilter")); //$NON-NLS-1$
bar.addDisplayableFilter(qFilterButton);
availPanel.add(bar, BorderLayout.NORTH);
availableTable.setTreeCellRenderer(qualifiedRenderer);
availableTable.setDisplayableFilter(bar);
availPanel.add(new JScrollPane(availableTable), BorderLayout.CENTER);
{
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalGlue());
spinner.setMaximumSize(spinner.getPreferredSize());
box.add(spinner);
box.add(Box.createHorizontalStrut(5));
addButton.setHorizontalTextPosition(SwingConstants.LEADING);
box.add(addButton);
box.add(Box.createHorizontalStrut(5));
box.setBorder(new EmptyBorder(0, 0, 5, 0));
availPanel.add(box, BorderLayout.SOUTH);
}
topPane.setLeftComponent(availPanel);
JPanel selPanel = new JPanel(new BorderLayout());
JScrollPane tablePane = new JScrollPane(classTable);
selPanel.add(tablePane, BorderLayout.CENTER);
{
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalStrut(5));
box.add(removeButton);
box.add(Box.createHorizontalGlue());
box.setBorder(new EmptyBorder(0, 0, 5, 0));
selPanel.add(box, BorderLayout.SOUTH);
}
{
classTable.setDragEnabled(true);
classTable.setTransferHandler(classTransferHandler);
availableTable.setDragEnabled(true);
availableTable.setTransferHandler(classTransferHandler);
}
initListeners();
topPane.setRightComponent(selPanel);
setBottomComponent(infoPane);
setResizeWeight(0.75);
}
private void initListeners()
{
spinner.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
spinnerValue = (Integer) spinner.getValue();
}
});
spinnerValue = (Integer) spinner.getValue();
classTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
availableTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
@Override
public ModelMap createModels(CharacterFacade character)
{
ModelMap models = new ModelMap();
models.put(ClassTransferHandler.TransHandler.class, classTransferHandler.createHandler(character));
models.put(ClassTreeViewModel.class, new ClassTreeViewModel(character));
models.put(ClassTableModel.class, new ClassTableModel(character));
models.put(AddClassAction.class, new AddClassAction(character));
models.put(RemoveClassAction.class, new RemoveClassAction(character));
models.put(InfoHandler.class, new InfoHandler(character));
models.put(Handler.class, qualifiedRenderer.createHandler(character));
models.put(QualifiedFilterHandler.class, new QualifiedFilterHandler(character));
return models;
}
@Override
public void storeModels(ModelMap models)
{
models.get(InfoHandler.class).uninstall();
models.get(AddClassAction.class).uninstall();
models.get(Handler.class).uninstall();
models.get(ClassTransferHandler.TransHandler.class).uninstall();
}
@Override
public void restoreModels(ModelMap models)
{
models.get(Handler.class).install();
models.get(QualifiedFilterHandler.class).install();
classTable.setModel(models.get(ClassTableModel.class));
availableTable.setTreeViewModel(models.get(ClassTreeViewModel.class));
models.get(AddClassAction.class).install();
models.get(RemoveClassAction.class).install();
models.get(InfoHandler.class).install();
models.get(ClassTransferHandler.TransHandler.class).install();
}
@Override
public TabTitle getTabTitle()
{
return tabTitle;
}
private ClassFacade getSelectedClass(Object eventSource)
{
Object data = null;
if (eventSource == availableTable.getSelectionModel())
{
data = availableTable.getSelectedObject();
}
else
{
int selectedRow = classTable.getSelectedRow();
if (selectedRow != -1)
{
data = classTable.getModel().getValueAt(selectedRow, 1);
}
}
if (data != null && data instanceof ClassFacade)
{
return (ClassFacade) data;
}
return null;
}
public void addCharacterLevels(CharacterFacade character, ClassFacade clazz)
{
if (clazz != null)
{
ClassFacade[] classes = new ClassFacade[spinnerValue];
for (int x = 0; x < spinnerValue; x++)
{
classes[x] = clazz;
}
character.addCharacterLevels(classes);
}
}
private class AddClassAction extends AbstractAction implements ListSelectionListener
{
private final CharacterFacade character;
private ClassFacade selectedClass = null;
public AddClassAction(CharacterFacade character)
{
super(LanguageBundle.getString("in_cl_addlevels"));
this.character = character;
putValue(SMALL_ICON, Icons.Forward16.getImageIcon());
setEnabled(false);
}
@Override
public void actionPerformed(ActionEvent e)
{
addCharacterLevels(character, selectedClass);
}
public void install()
{
addButton.setAction(this);
availableTable.addActionListener(this);
availableTable.getSelectionModel().addListSelectionListener(this);
classTable.getSelectionModel().addListSelectionListener(this);
}
public void uninstall()
{
availableTable.removeActionListener(this);
availableTable.getSelectionModel().removeListSelectionListener(this);
classTable.getSelectionModel().removeListSelectionListener(this);
}
@Override
public void valueChanged(ListSelectionEvent e)
{
if (!e.getValueIsAdjusting())
{
selectedClass = getSelectedClass(e.getSource());
if (selectedClass == null)
{
setEnabled(false);
}
setEnabled(selectedClass != null);
}
}
}
private class RemoveClassAction extends AbstractAction implements ListListener<CharacterLevelFacade>
{
private final CharacterFacade character;
public RemoveClassAction(CharacterFacade character)
{
super(LanguageBundle.getString("in_cl_removelevels"));
this.character = character;
putValue(SMALL_ICON, Icons.Back16.getImageIcon());
setEnabled(false);
character.getCharacterLevelsFacade().addListListener(this);
}
@Override
public void actionPerformed(ActionEvent e)
{
character.removeCharacterLevels(1);
}
public void install()
{
removeButton.setAction(this);
}
private void checkEnabled()
{
setEnabled(!character.getCharacterLevelsFacade().isEmpty());
}
@Override
public void elementAdded(ListEvent<CharacterLevelFacade> e)
{
checkEnabled();
}
@Override
public void elementRemoved(ListEvent<CharacterLevelFacade> e)
{
checkEnabled();
}
@Override
public void elementsChanged(ListEvent<CharacterLevelFacade> e)
{
checkEnabled();
}
@Override
public void elementModified(ListEvent<CharacterLevelFacade> e)
{
//Do nothing
}
}
private final class ClassTransferHandler extends TransferHandler
{
private final DataFlavor classFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType
+ ";class=" + ClassFacade.class.getName(), null); //$NON-NLS-1$
private CharacterFacade character = null;
public void setCharacter(CharacterFacade character)
{
this.character = character;
}
public TransHandler createHandler(CharacterFacade character)
{
return new TransHandler(character);
}
private class TransHandler
{
private final CharacterFacade character;
public TransHandler(CharacterFacade character)
{
this.character = character;
}
public void install()
{
setCharacter(character);
}
public void uninstall()
{
setCharacter(null);
}
}
@Override
public int getSourceActions(JComponent c)
{
if (c == classTable)
{
return NONE;
}
else
{
return COPY;
}
}
@Override
protected Transferable createTransferable(JComponent c)
{
final ClassFacade selClass = getSelectedClass(availableTable);
if (selClass == null)
{
return null;
}
return new Transferable()
{
@Override
public DataFlavor[] getTransferDataFlavors()
{
return new DataFlavor[]
{
classFlavor
};
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return classFlavor == flavor;
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException
{
if (!isDataFlavorSupported(flavor))
{
throw new UnsupportedFlavorException(flavor);
}
return selClass;
}
};
}
@Override
public boolean canImport(JComponent comp, DataFlavor[] transferFlavors)
{
return transferFlavors[0] == classFlavor;
}
@Override
public boolean importData(JComponent comp, Transferable t)
{
if (comp == classTable)
{
try
{
ClassFacade c = (ClassFacade) t.getTransferData(classFlavor);
addCharacterLevels(character, c);
return true;
}
catch (UnsupportedFlavorException | IOException ex)
{
Logger.getLogger(ClassInfoTab.class.getName()).log(Level.SEVERE,
null,
ex);
}
return false;
}
return true;
}
}
private class QualifiedFilterHandler implements Filter<Object, ClassFacade>
{
private final CharacterFacade character;
public QualifiedFilterHandler(CharacterFacade character)
{
this.character = character;
}
public void install()
{
qFilterButton.setFilter(this);
}
@Override
public boolean accept(Object context, ClassFacade element)
{
return character.isQualifiedFor(element);
}
}
private static class ClassTreeViewModel implements TreeViewModel<ClassFacade>,
DataView<ClassFacade>
{
private static final List<DefaultDataViewColumn> columns
= Arrays.asList(new DefaultDataViewColumn("in_clInfoHD", String.class), //$NON-NLS-1$
new DefaultDataViewColumn("in_clInfoType", String.class, true), //$NON-NLS-1$
new DefaultDataViewColumn("in_descrip", String.class, false), //$NON-NLS-1$
new DefaultDataViewColumn("in_baseStat", String.class), //$NON-NLS-1$
new DefaultDataViewColumn("in_spellType", String.class), //$NON-NLS-1$
new DefaultDataViewColumn("in_source", String.class)); //$NON-NLS-1$
private static final ListFacade<? extends TreeView<ClassFacade>> treeviews
= new DefaultListFacade<TreeView<ClassFacade>>(Arrays.asList(ClassTreeView.values()));
private final CharacterFacade character;
public ClassTreeViewModel(CharacterFacade character)
{
this.character = character;
}
@Override
public String getPrefsKey()
{
return "ClassTree"; //$NON-NLS-1$
}
@Override
public ListFacade<? extends TreeView<ClassFacade>> getTreeViews()
{
return treeviews;
}
@Override
public int getDefaultTreeViewIndex()
{
return 0;
}
@Override
public DataView<ClassFacade> getDataView()
{
return this;
}
@Override
public ListFacade<ClassFacade> getDataModel()
{
return character.getDataSet().getClasses();
}
@Override
public Object getData(ClassFacade obj, int column)
{
switch(column){
case 0:
return obj.getHD();
case 1:
return getTypes(obj);
case 2:
return character.getInfoFactory().getDescription(obj);
case 3:
return "None".equals(obj.getBaseStat()) ? "" : obj.getBaseStat(); //$NON-NLS-1$ //$NON-NLS-2$
case 4:
return obj.getSpellType();
case 5:
return obj.getSource();
default:
return null;
}
}
@Override
public void setData(Object value, ClassFacade element, int column)
{
}
private String getTypes(ClassFacade obj)
{
String ret = ""; //$NON-NLS-1$
String[] types = obj.getTypes();
if (types != null && types.length > 0)
{
ret += types[0];
for (int x = 1; x < types.length; x++)
{
ret += ", " + types[x]; //$NON-NLS-1$
}
}
return ret;
}
@Override
public List<? extends DataViewColumn> getDataColumns()
{
return columns;
}
private static enum ClassTreeView implements TreeView<ClassFacade>
{
NAME("in_nameLabel"), //$NON-NLS-1$
TYPE_NAME("in_typeName"), //$NON-NLS-1$
SOURCE_NAME("in_sourceName"); //$NON-NLS-1$
private final String name;
private ClassTreeView(String nameKey)
{
this.name = LanguageBundle.getString(nameKey);
}
@Override
public String getViewName()
{
return name;
}
@Override
public List<TreeViewPath<ClassFacade>> getPaths(ClassFacade pobj)
{
switch (this)
{
case TYPE_NAME:
String[] types = pobj.getTypes();
if (types != null && types.length > 0)
{
List<TreeViewPath<ClassFacade>> paths
= new ArrayList<>(types.length);
for (String type : types)
{
paths.add(new TreeViewPath<>(pobj, type));
}
return paths;
}
case NAME:
return Collections.singletonList(new TreeViewPath<>(pobj));
case SOURCE_NAME:
return Collections.singletonList(
new TreeViewPath<>(pobj,
pobj.getSourceForNodeDisplay()));
default:
throw new InternalError();
}
}
}
}
private static class ClassTableModel extends AbstractTableModel implements ListListener<CharacterLevelFacade>
{
private static final String[] columns =
{
LanguageBundle.getString("in_level"), //$NON-NLS-1$
LanguageBundle.getString("in_class"), //$NON-NLS-1$
LanguageBundle.getString("in_source") //$NON-NLS-1$
};
private final CharacterLevelsFacade model;
public ClassTableModel(CharacterFacade character)
{
this.model = character.getCharacterLevelsFacade();
model.addListListener(this);
}
@Override
public int getRowCount()
{
return model.getSize();
}
@Override
public int getColumnCount()
{
return 3;
}
@Override
public String getColumnName(int column)
{
return columns[column];
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
switch (columnIndex)
{
case 0:
return Integer.class;
case 1:
return Object.class;
case 2:
return String.class;
}
return null;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
if (columnIndex == 0)
{
return rowIndex + 1;
}
ClassFacade c = model.getClassTaken(model.getElementAt(rowIndex));
switch (columnIndex)
{
case 1:
return c;
case 2:
return c.getSource();
default:
return null;
}
}
@Override
public void elementAdded(ListEvent<CharacterLevelFacade> e)
{
fireTableRowsInserted(e.getIndex(), e.getIndex());
}
@Override
public void elementRemoved(ListEvent<CharacterLevelFacade> e)
{
fireTableRowsDeleted(e.getIndex(), e.getIndex());
}
@Override
public void elementsChanged(ListEvent<CharacterLevelFacade> e)
{
fireTableRowsUpdated(0, getRowCount() - 1);
}
@Override
public void elementModified(ListEvent<CharacterLevelFacade> e)
{
fireTableRowsUpdated(e.getIndex(), e.getIndex());
}
}
private class InfoHandler implements ListSelectionListener
{
private final CharacterFacade character;
private String text;
public InfoHandler(CharacterFacade character)
{
this.character = character;
this.text = ""; //$NON-NLS-1$
}
public void install()
{
classTable.getSelectionModel().addListSelectionListener(this);
availableTable.getSelectionModel().addListSelectionListener(this);
infoPane.setText(text);
}
public void uninstall()
{
classTable.getSelectionModel().removeListSelectionListener(this);
availableTable.getSelectionModel().removeListSelectionListener(this);
}
@Override
public void valueChanged(ListSelectionEvent e)
{
if (!e.getValueIsAdjusting())
{
ClassFacade data = getSelectedClass(e.getSource());
if (data != null)
{
text = character.getInfoFactory().getHTMLInfo(
data, null);
infoPane.setText(text);
}
else
{
text = ""; //$NON-NLS-1$
infoPane.setText(""); //$NON-NLS-1$
}
}
}
}
}