/* * AbilityChooserTab.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 29, 2008, 10:30:57 PM */ package pcgen.gui2.tabs; import java.awt.BorderLayout; import java.awt.Component; import java.awt.event.ActionEvent; import java.util.Arrays; import java.util.Hashtable; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Box; import javax.swing.DefaultListSelectionModel; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTable; import javax.swing.JTree; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import javax.swing.undo.StateEditable; import pcgen.cdom.enumeration.Nature; import pcgen.facade.core.AbilityCategoryFacade; import pcgen.facade.core.AbilityFacade; import pcgen.facade.core.CharacterFacade; import pcgen.facade.core.InfoFactory; import pcgen.facade.util.DefaultListFacade; import pcgen.facade.util.DelegatingListFacade; import pcgen.facade.util.ListFacade; import pcgen.gui2.UIPropertyContext; import pcgen.gui2.filter.Filter; import pcgen.gui2.filter.FilterBar; import pcgen.gui2.filter.FilterButton; import pcgen.gui2.filter.FilterHandler; import pcgen.gui2.filter.FilterUtilities; import pcgen.gui2.filter.FilteredTreeViewTable; import pcgen.gui2.filter.SearchFilterPanel; import pcgen.gui2.tabs.ability.AbilityTreeTableModel; import pcgen.gui2.tabs.ability.AbilityTreeViews; import pcgen.gui2.tabs.ability.CategoryTableModel; import pcgen.gui2.tabs.models.CharacterTreeCellRenderer; 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.JTreeTable; 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.system.LanguageBundle; /** * This component allows the user to select abilities for a character. The * AbilityChooserTab is a subtab of the AbilitiesInfoTab and is used to display * the available and selected abilities for a particular ability category. Its * up to the AbilitiesInfoTab to manage this tab's state which more or less * follows the state management guidelines of a {@code CharacterInfoTab}. * * @see pcgen.gui2.tabs.CharacterInfoTab * @author Connor Petty <cpmeister@users.sourceforge.net> */ @SuppressWarnings("serial") public class AbilityChooserTab extends FlippingSplitPane implements StateEditable, TodoHandler { private final FilteredTreeViewTable<CharacterFacade, AbilityFacade> availableTreeViewPanel; private final JTreeTable selectedTreeViewPanel; private final JTable categoryTable; private final InfoPane infoPane; private final JButton addButton; private final JButton removeButton; private final FilterBar<CharacterFacade, AbilityCategoryFacade> categoryBar; private final FilterButton<CharacterFacade, AbilityFacade> qFilterButton; private final QualifiedTreeCellRenderer qualifiedRenderer; private final AbilityRenderer abilityRenderer; public AbilityChooserTab() { super("ability"); this.availableTreeViewPanel = new FilteredTreeViewTable<>(); this.selectedTreeViewPanel = new JTreeTable(); this.categoryTable = new JTable(); this.infoPane = new InfoPane(); this.addButton = new JButton(); this.removeButton = new JButton(); this.categoryBar = new FilterBar<>(); this.qFilterButton = new FilterButton<>("AbilityQualified"); this.qualifiedRenderer = new QualifiedTreeCellRenderer(); this.abilityRenderer = new AbilityRenderer(); initComponents(); } private void initComponents() { setOrientation(VERTICAL_SPLIT); availableTreeViewPanel.setDefaultRenderer(Boolean.class, new BooleanRenderer()); availableTreeViewPanel.setTreeCellRenderer(qualifiedRenderer); selectedTreeViewPanel.setTreeCellRenderer(abilityRenderer); FilterBar<CharacterFacade, AbilityFacade> filterBar = new FilterBar<>(); filterBar.addDisplayableFilter(new SearchFilterPanel()); qFilterButton.setText(LanguageBundle.getString("in_igQualFilter")); //$NON-NLS-1$ filterBar.addDisplayableFilter(qFilterButton); JPanel availPanel = FilterUtilities.configureFilteredTreeViewPane(availableTreeViewPanel, filterBar); Box box = Box.createHorizontalBox(); box.add(Box.createHorizontalGlue()); 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); JPanel selPanel = new JPanel(new BorderLayout()); selPanel.add(new JScrollPane(selectedTreeViewPanel), BorderLayout.CENTER); AbilityTreeTableModel.initializeTreeTable(selectedTreeViewPanel); 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); FlippingSplitPane topPane = new FlippingSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, availPanel, selPanel, "abilityTop"); setTopComponent(topPane); FilterButton<CharacterFacade, AbilityCategoryFacade> gainedFilterButton = new FilterButton<>("AbilityGained", true); gainedFilterButton.setText(LanguageBundle.getString("in_gained")); //$NON-NLS-1$ gainedFilterButton.setEnabled(true); gainedFilterButton.setFilter(new Filter<CharacterFacade, AbilityCategoryFacade>() { @Override public boolean accept(CharacterFacade context, AbilityCategoryFacade element) { return context.getActiveAbilityCategories().containsElement(element); } }); categoryBar.addDisplayableFilter(gainedFilterButton); JPanel filterPanel = new JPanel(new BorderLayout()); filterPanel.add(categoryBar, BorderLayout.NORTH); filterPanel.add(new JScrollPane(categoryTable), BorderLayout.CENTER); FlippingSplitPane bottomPane = new FlippingSplitPane(JSplitPane.HORIZONTAL_SPLIT, "abilityBottom"); bottomPane.setLeftComponent(filterPanel); bottomPane.setRightComponent(infoPane); setBottomComponent(bottomPane); } private static final class BooleanRenderer extends DefaultTableCellRenderer { public BooleanRenderer() { setHorizontalAlignment(SwingConstants.CENTER); } @Override protected void setValue(Object value) { if (value == Boolean.TRUE) { setText(LanguageBundle.getString("in_yes")); //$NON-NLS-1$ } else if (value == Boolean.FALSE) { setText(LanguageBundle.getString("in_no")); //$NON-NLS-1$ } else { setText(""); } } } private class AvailableAbilityTreeViewModel extends CachedDataView<AbilityFacade> implements TreeViewModel<AbilityFacade>, ListSelectionListener, DataView<AbilityFacade> { private final ListFacade<? extends TreeView<AbilityFacade>> treeviews; private final CharacterFacade character; private final ListFacade<AbilityCategoryFacade> categories; private final ListSelectionModel selectionModel; private final List<? extends DataViewColumn> dataColumns; private final InfoFactory infoFactory; private final String title; private final DelegatingListFacade<AbilityFacade> delegate; public AvailableAbilityTreeViewModel(CharacterFacade character, ListFacade<AbilityCategoryFacade> categories, ListSelectionModel selectionModel, String tableTitle) { this.character = character; this.title = tableTitle; this.treeviews = new DefaultListFacade<>(AbilityTreeViews.createTreeViewList(character)); this.categories = categories; this.selectionModel = selectionModel; this.infoFactory = character.getInfoFactory(); this.delegate = new DelegatingListFacade<>(); delegate.setDelegate(new DefaultListFacade<>()); selectionModel.addListSelectionListener(this); dataColumns = Arrays.asList(new DefaultDataViewColumn("in_type", String.class), //$NON-NLS-1$ new DefaultDataViewColumn("in_abColumnsMultiples", Boolean.class), //$NON-NLS-1$ new DefaultDataViewColumn("in_abColumnsStacks", Boolean.class), //$NON-NLS-1$ new DefaultDataViewColumn("in_abColumnsDescription", String.class), //$NON-NLS-1$ new DefaultDataViewColumn("in_abColumnsCost", Float.class), //$NON-NLS-1$ new DefaultDataViewColumn("in_abColumnsSource", String.class)); //$NON-NLS-1$ } @Override public ListFacade<? extends TreeView<AbilityFacade>> getTreeViews() { return treeviews; } @Override public int getDefaultTreeViewIndex() { return 0; } @Override public DataView<AbilityFacade> getDataView() { return this; } @Override public ListFacade<AbilityFacade> getDataModel() { return delegate; } @Override public List<? extends DataViewColumn> getDataColumns() { return dataColumns; } private String getTypes(List<String> types) { if (types.isEmpty()) { return ""; } String ret = types.get(0); for (int x = 1; x < types.size(); x++) { ret += ", " + types.get(x); } return ret; } public void install() { availableTreeViewPanel.setTreeViewModel(this); selectedTreeViewPanel.getSelectionModel().addListSelectionListener(this); } public void uninstall() { selectedTreeViewPanel.getSelectionModel().removeListSelectionListener(this); } @Override public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) { return; } if (e.getSource() == selectionModel) { int index = selectionModel.getMinSelectionIndex(); if (index != -1) { delegate.setDelegate(character.getDataSet().getAbilities().getValue((AbilityCategoryFacade) categoryTable.getValueAt(index, 0))); } } else { int index = selectedTreeViewPanel.getSelectedRow(); if (index != -1 && index < selectedTreeViewPanel.getRowCount()) { Object data = selectedTreeViewPanel.getValueAt(index, 0); if (data instanceof AbilityCategoryFacade) { delegate.setDelegate(character.getDataSet().getAbilities().getValue((AbilityCategoryFacade) data)); // Select the appropriate row in the category table for (int i = 0; i < categoryTable.getRowCount(); i++) { Object catData = categoryTable.getValueAt(i, 0); if (catData == data) { categoryTable.setRowSelectionInterval(i, i); } } } } } } @Override public String getPrefsKey() { return title; } // @Override // protected void refreshTableData() // { // availableTreeViewPanel.refreshModelData(); // } @Override public Object getDataInternal(AbilityFacade obj, int column) { switch(column){ case 0: return getTypes(obj.getTypes()); case 1: return obj.isMult(); case 2: return obj.isStackable(); case 3: return infoFactory.getDescription(obj); case 4: return (int) obj.getCost(); case 5: return obj.getSource(); default: return null; } } @Override public void setData(Object value, AbilityFacade element, int column) { } } private class InfoHandler implements ListSelectionListener { private CharacterFacade character; private final ListFacade<AbilityCategoryFacade> categories; private String text; private String title; public InfoHandler(CharacterFacade character, ListFacade<AbilityCategoryFacade> categories) { this.character = character; this.categories = categories; this.text = ""; //$NON-NLS-1$ this.title = LanguageBundle.getString("in_abInfo"); //$NON-NLS-1$ } public void install() { availableTreeViewPanel.getSelectionModel().addListSelectionListener(this); selectedTreeViewPanel.getSelectionModel().addListSelectionListener(this); categoryTable.getSelectionModel().addListSelectionListener(this); infoPane.setTitle(title); infoPane.setText(text); } public void uninstall() { availableTreeViewPanel.getSelectionModel().removeListSelectionListener(this); selectedTreeViewPanel.getSelectionModel().removeListSelectionListener(this); categoryTable.getSelectionModel().removeListSelectionListener(this); } @Override public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { Object data = null; if (e.getSource() == availableTreeViewPanel.getSelectionModel()) { data = availableTreeViewPanel.getSelectedObject(); } else if (e.getSource() == selectedTreeViewPanel.getSelectionModel()) { int index = selectedTreeViewPanel.getSelectedRow(); if (index != -1) { data = selectedTreeViewPanel.getModel().getValueAt(index, 0); } } else { int index = categoryTable.getSelectionModel().getMinSelectionIndex(); if (index != -1) { data = categoryTable.getValueAt(index, 0); } } if (data != null) { if (data instanceof AbilityFacade) { text = character.getInfoFactory().getHTMLInfo((AbilityFacade) data); infoPane.setText(text); } if (data instanceof AbilityCategoryFacade) { title = LanguageBundle.getFormattedString( "in_abCatInfo", //$NON-NLS-1$ ((AbilityCategoryFacade) data).getName()); infoPane.setTitle(title); } } else { text = ""; //$NON-NLS-1$ infoPane.setText(""); //$NON-NLS-1$ } } } } // // private final class AbilityTransferHandler extends TransferHandler // { // // private CharacterFacade character; // // public AbilityTransferHandler(CharacterFacade character) // { // this.character = character; // } // // private final DataFlavor abilityFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + // ";class=" + // AbilityFacade.class.getName(), // null); // // @Override // public int getSourceActions(JComponent c) // { // if (selectedAbility != null) // { // if (selectedTreeViewPanel.isAncestorOf(c)) // { // return MOVE; // } // if (selectedAbility.isMult()) // { // return COPY; // } // if (!character.hasAbility(selectedCatagory, selectedAbility)) // { // return MOVE; // } // } // return NONE; // } // // @Override // protected Transferable createTransferable(JComponent c) // { // final AbilityFacade transferAbility = selectedAbility; // return new Transferable() // { // // public DataFlavor[] getTransferDataFlavors() // { // return new DataFlavor[] // { // abilityFlavor // }; // } // // public boolean isDataFlavorSupported(DataFlavor flavor) // { // return abilityFlavor == flavor; // } // // public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException // { // if (!isDataFlavorSupported(flavor)) // { // throw new UnsupportedFlavorException(flavor); // } // return transferAbility; // } // // }; // } // // @Override // public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) // { // return transferFlavors[0] == abilityFlavor; // } // // @Override // public boolean importData(JComponent comp, Transferable t) // { // if (selectedTreeViewPanel.isAncestorOf(comp)) // { // try // { // AbilityFacade ability = (AbilityFacade) t.getTransferData(abilityFlavor); // // TODO: add some extra logic // character.addAbility(selectedCatagory, ability); // return true; // } // catch (UnsupportedFlavorException ex) // { // Logger.getLogger(AbilityChooserTab.class.getName()).log(Level.SEVERE, // null, // ex); // } // catch (IOException ex) // { // Logger.getLogger(AbilityChooserTab.class.getName()).log(Level.SEVERE, // null, // ex); // } // return false; // } // return true; // } // // @Override // protected void exportDone(JComponent source, Transferable data, // int action) // { // if (action == COPY) // { // return; // } // } // // } public Hashtable<Object, Object> createState(CharacterFacade character, ListFacade<AbilityCategoryFacade> categories, ListFacade<AbilityCategoryFacade> fullCategoryList, String title) { Hashtable<Object, Object> state = new Hashtable<>(); CategoryTableModel categoryTableModel = new CategoryTableModel(character, fullCategoryList, categoryBar, categoryTable); state.put(CategoryTableModel.class, categoryTableModel); ListSelectionModel listModel = new DefaultListSelectionModel(); listModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); state.put(ListSelectionModel.class, listModel); state.put(AbilityTreeTableModel.class, new AbilityTreeTableModel(character, categories)); state.put(AvailableAbilityTreeViewModel.class, new AvailableAbilityTreeViewModel(character, fullCategoryList, listModel, title)); //state.put(AbilityTransferHandler.class, new AbilityTransferHandler(character)); state.put(InfoHandler.class, new InfoHandler(character, categories)); state.put(TreeRendererHandler.class, new TreeRendererHandler(character)); state.put(AddAction.class, new AddAction(character)); state.put(RemoveAction.class, new RemoveAction(character)); state.put(AbilityFilterHandler.class, new AbilityFilterHandler(character)); state.put(CategoryFilterHandler.class, new CategoryFilterHandler(categoryTableModel)); return state; } @Override public void storeState(Hashtable<Object, Object> state) { ((InfoHandler) state.get(InfoHandler.class)).uninstall(); ((AvailableAbilityTreeViewModel) state.get(AvailableAbilityTreeViewModel.class)).uninstall(); categoryTable.setSelectionModel(new DefaultListSelectionModel()); ((CategoryTableModel) state.get(CategoryTableModel.class)).uninstall(); ((AddAction) state.get(AddAction.class)).uninstall(); ((RemoveAction) state.get(RemoveAction.class)).uninstall(); ((TreeRendererHandler) state.get(TreeRendererHandler.class)).uninstall(); } @Override public void restoreState(Hashtable<?, ?> state) { //AbilityTransferHandler handler = (AbilityTransferHandler) state.get(AbilityTransferHandler.class); ((CategoryFilterHandler) state.get(CategoryFilterHandler.class)).install(); ((AbilityFilterHandler) state.get(AbilityFilterHandler.class)).install(); categoryTable.setModel((CategoryTableModel) state.get(CategoryTableModel.class)); categoryTable.setSelectionModel((ListSelectionModel) state.get(ListSelectionModel.class)); //selectedTreeViewPanel.setTransferHandler(handler); //availableTreeViewPanel.setTransferHandler(handler); ((TreeRendererHandler) state.get(TreeRendererHandler.class)).install(); selectedTreeViewPanel.setTreeTableModel((AbilityTreeTableModel) state.get(AbilityTreeTableModel.class)); // selectedTreeViewPanel.sortModel(); ((AvailableAbilityTreeViewModel) state.get(AvailableAbilityTreeViewModel.class)).install(); addButton.setAction((AddAction) state.get(AddAction.class)); removeButton.setAction((RemoveAction) state.get(RemoveAction.class)); ((InfoHandler) state.get(InfoHandler.class)).install(); ((CategoryTableModel) state.get(CategoryTableModel.class)).install(); ((AddAction) state.get(AddAction.class)).install(); ((RemoveAction) state.get(RemoveAction.class)).install(); ensureCategorySelected(); } /** * Ensure that when the tab is displayed a category is selected if any are * available. */ private void ensureCategorySelected() { if (categoryTable.getSelectedRowCount() == 0) { CategoryTableModel model = (CategoryTableModel) categoryTable.getModel(); model.refilter(); if (model.getRowCount() > 0) { categoryTable.getSelectionModel().setSelectionInterval(0, 0); } } } @Override public void adviseTodo(String fieldName) { CategoryTableModel model = (CategoryTableModel) categoryTable.getModel(); model.refilter(); for (int i = 0; i < model.getRowCount(); i++) { AbilityCategoryFacade category = model.getCategory(i); if (category.getName().equals(fieldName)) { categoryTable.getSelectionModel().setSelectionInterval(i, i); return; } } } private class AddAction extends AbstractAction implements ListSelectionListener { private CharacterFacade character; private AbilityCategoryFacade abilityCat; public AddAction(CharacterFacade character) { super(LanguageBundle.getString("in_addSelected")); //$NON-NLS-1$ this.character = character; this.putValue(SMALL_ICON, Icons.Forward16.getImageIcon()); } @Override public void actionPerformed(ActionEvent e) { if (!abilityCat.isEditable()) { return; } Object data = availableTreeViewPanel.getSelectedObject(); int index = categoryTable.getSelectedRow(); if (data != null && data instanceof AbilityFacade && index != -1) { AbilityCategoryFacade category = (AbilityCategoryFacade) categoryTable.getValueAt(index, 0); character.addAbility(category, (AbilityFacade) data); availableTreeViewPanel.refilter(); JTree tree = selectedTreeViewPanel.getTree(); for (int i = 0; i < tree.getRowCount(); i++) { TreePath pathForRow = tree.getPathForRow(i); if (category.toString().equals(pathForRow.getLastPathComponent().toString())) { tree.expandRow(i); } } } } public void install() { availableTreeViewPanel.addActionListener(this); categoryTable.getSelectionModel().addListSelectionListener(this); } public void uninstall() { availableTreeViewPanel.removeActionListener(this); categoryTable.getSelectionModel().removeListSelectionListener(this); } @Override public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { int index = categoryTable.getSelectionModel().getMinSelectionIndex(); if (index != -1) { abilityCat = (AbilityCategoryFacade) categoryTable.getValueAt( index, 0); this.setEnabled(abilityCat.isEditable()); this.putValue(SHORT_DESCRIPTION, abilityCat.isEditable() ? null : LanguageBundle.getString("in_abCatNotEditable")); } } } } private class RemoveAction extends AbstractAction implements ListSelectionListener { private CharacterFacade character; private AbilityCategoryFacade abilityCat; public RemoveAction(CharacterFacade character) { super(LanguageBundle.getString("in_removeSelected")); //$NON-NLS-1$ this.character = character; this.putValue(SMALL_ICON, Icons.Back16.getImageIcon()); } @Override public void actionPerformed(ActionEvent e) { if (!abilityCat.isEditable()) { return; } int selectedRow = selectedTreeViewPanel.getSelectedRow(); if (selectedRow == -1) { return; } Object data = selectedTreeViewPanel.getModel().getValueAt(selectedRow, 0); if (data != null && data instanceof AbilityFacade) { TreePath path = selectedTreeViewPanel.getTree().getPathForRow(selectedRow); DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getParentPath().getLastPathComponent(); Object category = node.getUserObject(); if (category instanceof AbilityCategoryFacade) { character.removeAbility((AbilityCategoryFacade) category, (AbilityFacade) data); availableTreeViewPanel.refilter(); } } } public void install() { selectedTreeViewPanel.addActionListener(this); categoryTable.getSelectionModel().addListSelectionListener(this); } public void uninstall() { selectedTreeViewPanel.removeActionListener(this); categoryTable.getSelectionModel().removeListSelectionListener(this); } @Override public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { int index = categoryTable.getSelectionModel().getMinSelectionIndex(); if (index != -1) { abilityCat = (AbilityCategoryFacade) categoryTable.getValueAt( index, 0); this.setEnabled(abilityCat.isEditable()); this.putValue(SHORT_DESCRIPTION, abilityCat.isEditable() ? null : LanguageBundle.getString("in_abCatNotEditable")); } } } } private class AbilityFilterHandler { private final Filter<CharacterFacade, AbilityFacade> qFilter = new Filter<CharacterFacade, AbilityFacade>() { @Override public boolean accept(CharacterFacade context, AbilityFacade element) { return character.isQualifiedFor(element); } }; private final CharacterFacade character; public AbilityFilterHandler(CharacterFacade character) { this.character = character; } public void install() { qFilterButton.setFilter(qFilter); } } private class CategoryFilterHandler implements FilterHandler { private final CategoryTableModel model; public CategoryFilterHandler(CategoryTableModel model) { this.model = model; } public void install() { categoryBar.setFilterHandler(this); refilter(); } @Override public void refilter() { model.refilter(); } @Override public void scrollToTop() { // do nothing } @Override public void setSearchEnabled(boolean enable) { //do nothing as there is no search bar } } private class TreeRendererHandler { private final CharacterFacade character; public TreeRendererHandler(CharacterFacade character) { this.character = character; } public void install() { abilityRenderer.setCharacter(character); qualifiedRenderer.setCharacter(character); } public void uninstall() { abilityRenderer.setCharacter(null); qualifiedRenderer.setCharacter(null); } } /** * The Class {@code AbilityRenderer} displays the tree cells of the * available and selected ability tables. */ private static class AbilityRenderer extends CharacterTreeCellRenderer { @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean focus) { super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, focus); Object abilityObj = ((DefaultMutableTreeNode) value).getUserObject(); if (abilityObj instanceof AbilityFacade) { AbilityFacade ability = (AbilityFacade) abilityObj; Nature nature = character.getAbilityNature(ability); if (nature == Nature.VIRTUAL) { setForeground(UIPropertyContext.getVirtualColor()); } else if (!character.isQualifiedFor(ability)) { setForeground(UIPropertyContext.getNotQualifiedColor()); } else if (nature == Nature.AUTOMATIC) { setForeground(UIPropertyContext.getAutomaticColor()); } } return this; } } }