/**
* RaceInfoTab.java Copyright James Dempsey, 2010
*
* 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 29/09/2010 7:16:42 PM
*
* $Id: RaceInfoTab.java 14578 2011-02-16 20:20:14Z cpmeister $
*/
package pcgen.gui2.tabs;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import pcgen.facade.core.CharacterFacade;
import pcgen.facade.core.InfoFactory;
import pcgen.facade.core.RaceFacade;
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.treeview.CachedDataView;
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;
/**
* The Class {@code RaceInfoTab} is the component used in the Race tab.
* <br>
* -0800 (Wed, 16 Feb 2011) $
*
* @author James Dempsey <jdempsey@users.sourceforge.net>
*/
@SuppressWarnings("serial")
public class RaceInfoTab extends FlippingSplitPane implements CharacterInfoTab
{
private static final TabTitle title = new TabTitle(Tab.RACE);
private final FilteredTreeViewTable<Object, RaceFacade> raceTable;
private final FilteredTreeViewTable<Object, RaceFacade> selectedTable;
private final InfoPane infoPane;
private final JButton selectRaceButton;
private final JButton removeButton;
private final FilterButton<Object, RaceFacade> qFilterButton;
private final FilterButton<Object, RaceFacade> noRacialHdFilterButton;
private final QualifiedTreeCellRenderer qualifiedRenderer;
public RaceInfoTab()
{
super("Race");
this.raceTable = new FilteredTreeViewTable<>();
this.selectedTable = new FilteredTreeViewTable<>();
this.infoPane = new InfoPane(LanguageBundle.getString("in_irRaceInfo")); //$NON-NLS-1$
this.selectRaceButton = new JButton();
this.removeButton = new JButton();
this.qFilterButton = new FilterButton<>("RaceQualified");
this.noRacialHdFilterButton = new FilterButton<>("RaceNoHD");
this.qualifiedRenderer = new QualifiedTreeCellRenderer();
initComponents();
}
private void initComponents()
{
FlippingSplitPane topPane = new FlippingSplitPane("RaceTop");
setTopComponent(topPane);
setOrientation(VERTICAL_SPLIT);
JPanel availPanel = new JPanel(new BorderLayout());
FilterBar<Object, RaceFacade> bar = new FilterBar<>();
bar.addDisplayableFilter(new SearchFilterPanel());
noRacialHdFilterButton.setText(LanguageBundle.getString("in_irNoRacialHd")); //$NON-NLS-1$
noRacialHdFilterButton.setToolTipText(LanguageBundle.getString("in_irNoRacialHdTip")); //$NON-NLS-1$
bar.addDisplayableFilter(noRacialHdFilterButton);
qFilterButton.setText(LanguageBundle.getString("in_igQualFilter")); //$NON-NLS-1$
bar.addDisplayableFilter(qFilterButton);
raceTable.setDisplayableFilter(bar);
availPanel.add(bar, BorderLayout.NORTH);
raceTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
raceTable.setTreeCellRenderer(qualifiedRenderer);
availPanel.add(new JScrollPane(raceTable), BorderLayout.CENTER);
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalGlue());
selectRaceButton.setHorizontalTextPosition(SwingConstants.LEADING);
box.add(selectRaceButton);
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());
FilterBar<Object, RaceFacade> filterBar = new FilterBar<>();
filterBar.addDisplayableFilter(new SearchFilterPanel());
selectedTable.setDisplayableFilter(filterBar);
selectedTable.setTreeCellRenderer(qualifiedRenderer);
JScrollPane scrollPane = new JScrollPane(selectedTable);
selPanel.add(scrollPane, BorderLayout.CENTER);
scrollPane.setPreferredSize(new Dimension(0, 0));
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);
topPane.setRightComponent(selPanel);
topPane.setResizeWeight(0.75);
setBottomComponent(infoPane);
setResizeWeight(0.75);
}
@Override
public ModelMap createModels(CharacterFacade character)
{
ModelMap models = new ModelMap();
models.put(SelectRaceAction.class, new SelectRaceAction(character));
models.put(RemoveRaceAction.class, new RemoveRaceAction(character));
models.put(TreeViewModelHandler.class, new TreeViewModelHandler(character));
models.put(InfoHandler.class, new InfoHandler(character));
models.put(Handler.class, qualifiedRenderer.createHandler(character));
models.put(QualifiedFilterHandler.class, new QualifiedFilterHandler(character));
models.put(NoRacialHdFilterHandler.class, new NoRacialHdFilterHandler(character));
return models;
}
@Override
public void restoreModels(ModelMap models)
{
models.get(QualifiedFilterHandler.class).install();
models.get(NoRacialHdFilterHandler.class).install();
models.get(Handler.class).install();
models.get(TreeViewModelHandler.class).install();
models.get(InfoHandler.class).install();
models.get(SelectRaceAction.class).install();
models.get(RemoveRaceAction.class).install();
}
@Override
public void storeModels(ModelMap models)
{
models.get(TreeViewModelHandler.class).uninstall();
models.get(InfoHandler.class).uninstall();
models.get(SelectRaceAction.class).uninstall();
models.get(RemoveRaceAction.class).uninstall();
models.get(Handler.class).uninstall();
}
@Override
public TabTitle getTabTitle()
{
return title;
}
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()
{
raceTable.getSelectionModel().addListSelectionListener(this);
selectedTable.getSelectionModel().addListSelectionListener(this);
infoPane.setText(text);
}
public void uninstall()
{
raceTable.getSelectionModel().removeListSelectionListener(this);
selectedTable.getSelectionModel().removeListSelectionListener(this);
}
@Override
public void valueChanged(ListSelectionEvent e)
{
if (!e.getValueIsAdjusting())
{
Object obj = null;
if (e.getSource() == raceTable.getSelectionModel())
{
int selectedRow = raceTable.getSelectedRow();
if (selectedRow != -1)
{
obj = raceTable.getModel().getValueAt(selectedRow, 0);
}
}
else
{
int selectedRow = selectedTable.getSelectedRow();
if (selectedRow != -1)
{
obj = selectedTable.getModel().getValueAt(selectedRow, 0);
}
}
if (obj instanceof RaceFacade)
{
text = character.getInfoFactory().getHTMLInfo((RaceFacade) obj);
infoPane.setText(text);
}
}
}
}
private class SelectRaceAction extends AbstractAction
{
private final CharacterFacade character;
public SelectRaceAction(CharacterFacade character)
{
super(LanguageBundle.getString("in_irSelectRace")); //$NON-NLS-1$
this.character = character;
putValue(SMALL_ICON, Icons.Forward16.getImageIcon());
}
@Override
public void actionPerformed(ActionEvent e)
{
Object obj = raceTable.getSelectedObject();
if (obj instanceof RaceFacade)
{
character.setRace((RaceFacade) obj);
}
}
public void install()
{
raceTable.addActionListener(this);
selectRaceButton.setAction(this);
}
public void uninstall()
{
raceTable.removeActionListener(this);
}
}
private class RemoveRaceAction extends AbstractAction
{
private final CharacterFacade character;
public RemoveRaceAction(CharacterFacade character)
{
super(LanguageBundle.getString("in_irUnselectRace")); //$NON-NLS-1$
this.character = character;
putValue(SMALL_ICON, Icons.Back16.getImageIcon());
}
@Override
public void actionPerformed(ActionEvent e)
{
character.setRace(null);
}
public void install()
{
selectedTable.addActionListener(this);
removeButton.setAction(this);
}
public void uninstall()
{
selectedTable.removeActionListener(this);
}
}
/**
* The Class {@code NoRacialHdFilterHandler} provides the filter
* backing the No Racial HD filter button.
*/
private class NoRacialHdFilterHandler implements Filter<Object, RaceFacade>
{
private final InfoFactory infoFactory;
public NoRacialHdFilterHandler(CharacterFacade character)
{
this.infoFactory = character.getInfoFactory();
}
public void install()
{
noRacialHdFilterButton.setFilter(this);
}
@Override
public boolean accept(Object context, RaceFacade element)
{
return infoFactory.getNumMonsterClassLevels(element) == 0;
}
}
/**
* The Class {@code QualifiedFilterHandler} provides the filter backing
* the Qualified filter button.
*/
private class QualifiedFilterHandler implements Filter<Object, RaceFacade>
{
private final CharacterFacade character;
public QualifiedFilterHandler(CharacterFacade character)
{
this.character = character;
}
public void install()
{
qFilterButton.setFilter(this);
}
@Override
public boolean accept(Object context, RaceFacade element)
{
return character.isQualifiedFor(element);
}
}
private class TreeViewModelHandler
{
private final RaceDataView availableView;
private final RaceDataView selectedView;
private final RaceTreeViewModel availableModel;
private final RaceTreeViewModel selectedModel;
public TreeViewModelHandler(CharacterFacade character)
{
availableView = new RaceDataView(character, true);
selectedView = new RaceDataView(character, false);
availableModel = new RaceTreeViewModel(character, true, availableView);
selectedModel = new RaceTreeViewModel(character, false, selectedView);
}
public void install()
{
raceTable.setTreeViewModel(availableModel);
selectedTable.setTreeViewModel(selectedModel);
}
public void uninstall()
{
}
}
private class RaceDataView extends CachedDataView<RaceFacade>
{
private final List<DefaultDataViewColumn> columns;
private final InfoFactory infoFactory;
private final boolean isAvailModel;
public RaceDataView(CharacterFacade character, boolean isAvailModel)
{
this.infoFactory = character.getInfoFactory();
this.isAvailModel = isAvailModel;
if (isAvailModel)
{
columns = Arrays.asList(new DefaultDataViewColumn("in_irTableStat", String.class, true), //$NON-NLS-1$
new DefaultDataViewColumn("in_preReqs", String.class), //$NON-NLS-1$
new DefaultDataViewColumn("in_size", String.class, true), //$NON-NLS-1$
new DefaultDataViewColumn("in_movement", String.class, true), //$NON-NLS-1$
new DefaultDataViewColumn("in_vision", String.class), //$NON-NLS-1$
new DefaultDataViewColumn("in_favoredClass", String.class, true), //$NON-NLS-1$
new DefaultDataViewColumn("in_lvlAdj", String.class, true), //$NON-NLS-1$
new DefaultDataViewColumn("in_descrip", String.class, false), //$NON-NLS-1$
new DefaultDataViewColumn("in_source", String.class, false)); //$NON-NLS-1$
}
else
{
columns = Arrays.asList(new DefaultDataViewColumn("in_irTableStat", String.class, false), //$NON-NLS-1$
new DefaultDataViewColumn("in_preReqs", String.class, false), //$NON-NLS-1$
new DefaultDataViewColumn("in_size", String.class, false), //$NON-NLS-1$
new DefaultDataViewColumn("in_movement", String.class, false), //$NON-NLS-1$
new DefaultDataViewColumn("in_vision", String.class, false), //$NON-NLS-1$
new DefaultDataViewColumn("in_favoredClass", String.class, false), //$NON-NLS-1$
new DefaultDataViewColumn("in_lvlAdj", String.class, false), //$NON-NLS-1$
new DefaultDataViewColumn("in_descrip", String.class, false), //$NON-NLS-1$
new DefaultDataViewColumn("in_source", String.class, false)); //$NON-NLS-1$
}
}
@Override
public List<? extends DataViewColumn> getDataColumns()
{
return columns;
}
@Override
public String getPrefsKey()
{
return isAvailModel ? "RaceTreeAvail" : "RaceTreeSelected"; //$NON-NLS-1$//$NON-NLS-2$
}
@Override
public Object getDataInternal(RaceFacade obj, int column)
{
switch(column){
case 0:
return infoFactory.getStatAdjustments(obj);
case 1:
return infoFactory.getPreReqHTML(obj);
case 2:
return obj.getSize();
case 3:
return infoFactory.getMovement(obj);
case 4:
return infoFactory.getVision(obj);
case 5:
return infoFactory.getFavoredClass(obj);
case 6:
return infoFactory.getLevelAdjustment(obj);
case 7:
return infoFactory.getDescription(obj);
case 8:
return obj.getSource();
default:
return null;
}
}
@Override
public void setData(Object value, RaceFacade element, int column)
{
}
// @Override
// protected void refreshTableData()
// {
// if (isAvailModel)
// {
// raceTable.refreshModelData();
// }
// else
// {
// selectedTable.refreshModelData();
// }
// }
}
private static class RaceTreeViewModel implements TreeViewModel<RaceFacade>
{
private static final DefaultListFacade<? extends TreeView<RaceFacade>> treeViews
= new DefaultListFacade<TreeView<RaceFacade>>(Arrays.asList(RaceTreeView.values()));
private final CharacterFacade character;
private final boolean isAvailModel;
private final DataView<RaceFacade> dataView;
public RaceTreeViewModel(CharacterFacade character, boolean isAvailModel, DataView<RaceFacade> dataView)
{
this.character = character;
this.isAvailModel = isAvailModel;
this.dataView = dataView;
}
@Override
public ListFacade<? extends TreeView<RaceFacade>> getTreeViews()
{
return treeViews;
}
@Override
public int getDefaultTreeViewIndex()
{
return 0;
}
@Override
public DataView<RaceFacade> getDataView()
{
return dataView;
}
@Override
public ListFacade<RaceFacade> getDataModel()
{
if (isAvailModel)
{
return character.getDataSet().getRaces();
}
else
{
return character.getRaceAsList();
}
}
}
private enum RaceTreeView implements TreeView<RaceFacade>
{
NAME(LanguageBundle.getString("in_nameLabel")), //$NON-NLS-1$
TYPE_NAME(LanguageBundle.getString("in_typeName")), //$NON-NLS-1$
RACETYPE_NAME(LanguageBundle.getString("in_racetypeName")), //$NON-NLS-1$
RACETYPE_RACE_SUBTYPE_NAME(
LanguageBundle.getString("in_racetypeSubtypeName")), //$NON-NLS-1$
SOURCE_NAME(LanguageBundle.getString("in_sourceName")); //$NON-NLS-1$
private final String name;
private RaceTreeView(String name)
{
this.name = name;
}
@Override
public String getViewName()
{
return name;
}
@Override
public List<TreeViewPath<RaceFacade>> getPaths(RaceFacade pobj)
{
switch (this)
{
case NAME:
return Collections.singletonList(new TreeViewPath<>(pobj));
case TYPE_NAME:
return Collections.singletonList(new TreeViewPath<>(pobj,
pobj.getType()));
case RACETYPE_RACE_SUBTYPE_NAME:
List<String> subtypes = pobj.getRaceSubTypes();
if (!subtypes.isEmpty())
{
List<TreeViewPath<RaceFacade>> paths
= new ArrayList<>();
String raceType = pobj.getRaceType();
for (String subtype : subtypes)
{
paths.add(new TreeViewPath<>(pobj,
raceType, subtype));
}
return paths;
}
// No subtypes, fall through to treat it as a type tree.
return Collections.singletonList(new TreeViewPath<>(pobj,
pobj.getRaceType()));
case RACETYPE_NAME:
return Collections.singletonList(new TreeViewPath<>(pobj,
pobj.getRaceType()));
case SOURCE_NAME:
return Collections.singletonList(new TreeViewPath<>(pobj,
pobj.getSourceForNodeDisplay()));
default:
throw new InternalError();
}
}
}
}