/*
* CompanionInfoTab.java Copyright 2012 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 Mar 4, 2012, 5:01:02 PM
*/
package pcgen.gui2.tabs;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;
import javax.swing.AbstractAction;
import javax.swing.AbstractCellEditor;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.apache.commons.io.FileUtils;
import pcgen.facade.core.CharacterFacade;
import pcgen.facade.core.CompanionFacade;
import pcgen.facade.core.CompanionStubFacade;
import pcgen.facade.core.CompanionSupportFacade;
import pcgen.facade.util.DefaultListFacade;
import pcgen.facade.util.ListFacade;
import pcgen.facade.util.MapFacade;
import pcgen.facade.util.event.ListEvent;
import pcgen.facade.util.event.ListListener;
import pcgen.facade.util.event.MapEvent;
import pcgen.facade.util.event.MapListener;
import pcgen.facade.util.event.ReferenceEvent;
import pcgen.facade.util.event.ReferenceListener;
import pcgen.gui2.PCGenFrame;
import pcgen.gui2.filter.Filter;
import pcgen.gui2.filter.FilteredListFacade;
import pcgen.gui2.filter.FilteredTreeViewTable;
import pcgen.gui2.filter.SearchFilterPanel;
import pcgen.gui2.tabs.models.HtmlSheetSupport;
import pcgen.gui2.tools.FlippingSplitPane;
import pcgen.gui2.tools.Utility;
import pcgen.gui2.util.DisplayAwareTab;
import pcgen.gui2.util.JTreeTable;
import pcgen.gui2.util.treetable.AbstractTreeTableModel;
import pcgen.gui2.util.treetable.DefaultTreeTableNode;
import pcgen.gui2.util.treetable.TreeTableModel;
import pcgen.gui2.util.treeview.DataView;
import pcgen.gui2.util.treeview.DataViewColumn;
import pcgen.gui2.util.treeview.TreeView;
import pcgen.gui2.util.treeview.TreeViewModel;
import pcgen.gui2.util.treeview.TreeViewPath;
import pcgen.system.CharacterManager;
import pcgen.system.ConfigurationSettings;
import pcgen.system.LanguageBundle;
import pcgen.util.Comparators;
import pcgen.util.Logging;
import pcgen.util.enumeration.Tab;
/**
* This component allows a user to manage a character's companions (animal,
* familiar, cohort, mount, etc).
*
* @author Connor Petty <cpmeister@users.sourceforge.net>
*/
@SuppressWarnings("serial")
public class CompanionInfoTab extends FlippingSplitPane implements CharacterInfoTab, TodoHandler, DisplayAwareTab
{
private final JTreeTable companionsTable;
private final JEditorPane infoPane;
private final JButton loadButton;
private CompanionDialog companionDialog = null;
public CompanionInfoTab()
{
super("Companion");
this.companionsTable = new JTreeTable()
{
@Override
protected void configureEnclosingScrollPane()
{
//We do nothing so the table is displayed without a header
}
};
this.infoPane = new JEditorPane();
this.loadButton = new JButton();
initComponents();
}
private void initDialog()
{
if (companionDialog == null)
{
companionDialog = new CompanionDialog();
}
}
private void initComponents()
{
{
DefaultTableColumnModel model = new DefaultTableColumnModel();
TableColumn column = new TableColumn(0);
column.setResizable(true);
model.addColumn(column);
column = new TableColumn(1, 120, new ButtonCellRenderer(), null);
column.setMaxWidth(120);
column.setResizable(false);
model.addColumn(column);
companionsTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
companionsTable.getTableHeader().setResizingAllowed(false);
companionsTable.setAutoCreateColumnsFromModel(false);
companionsTable.setColumnModel(model);
}
companionsTable.setIntercellSpacing(new Dimension(0, 0));
companionsTable.setFocusable(false);
companionsTable.setRowHeight(23);
companionsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
setLeftComponent(new JScrollPane(companionsTable));
JPanel rightPane = new JPanel(new BorderLayout());
infoPane.setOpaque(false);
infoPane.setEditable(false);
infoPane.setFocusable(true);
infoPane.setContentType("text/html"); //$NON-NLS-1$
rightPane.add(new JScrollPane(infoPane), BorderLayout.CENTER);
JPanel buttonPane = new JPanel(new FlowLayout());
buttonPane.add(loadButton);
rightPane.add(buttonPane, BorderLayout.SOUTH);
setRightComponent(rightPane);
}
@Override
public ModelMap createModels(CharacterFacade character)
{
ModelMap models = new ModelMap();
models.put(CompanionsModel.class, new CompanionsModel(character));
models.put(ButtonCellEditor.class, new ButtonCellEditor(character));
models.put(LoadButtonAndSheetHandler.class, new LoadButtonAndSheetHandler());
models.put(TreeExpansionHandler.class, new TreeExpansionHandler());
return models;
}
@Override
public void restoreModels(ModelMap models)
{
companionsTable.setTreeTableModel(models.get(CompanionsModel.class));
companionsTable.setDefaultEditor(Object.class, models.get(ButtonCellEditor.class));
models.get(TreeExpansionHandler.class).install();
models.get(LoadButtonAndSheetHandler.class).install();
}
@Override
public void storeModels(ModelMap models)
{
models.get(TreeExpansionHandler.class).uninstall();
models.get(LoadButtonAndSheetHandler.class).uninstall();
}
@Override
public TabTitle getTabTitle()
{
return new TabTitle(Tab.COMPANIONS);
}
@Override
public void tabSelected()
{
// Refresh the character sheet as we have been displayed.
LoadButtonAndSheetHandler action = (LoadButtonAndSheetHandler) loadButton.getAction();
if (action != null)
{
action.showCompanion(false);
}
}
private void selectCompanion(CompanionFacade compFacade)
{
TreeTableModel treeTableModel = companionsTable.getTreeTableModel();
treeTableModel.getRoot();
TreePath path = null;
JTree tree = companionsTable.getTree();
String companionType = compFacade.getCompanionType();
for (int i = 0; i < tree.getRowCount(); i++)
{
TreePath pathForRow = tree.getPathForRow(i);
Object lastPathComponent = pathForRow.getLastPathComponent();
if (lastPathComponent.toString()
.startsWith(companionType))
{
tree.expandRow(i);
}
else if (lastPathComponent instanceof pcgen.gui2.tabs.CompanionInfoTab.CompanionsModel.CompanionNode)
{
CompanionFacade rowComp =
(CompanionFacade) ((pcgen.gui2.tabs.CompanionInfoTab.CompanionsModel.CompanionNode) lastPathComponent).getValueAt(0);
if (rowComp != null
&& rowComp.getFileRef().get() == compFacade
.getFileRef().get()
&& rowComp.getNameRef().get() == compFacade
.getNameRef().get()
&& rowComp.getRaceRef().get() == compFacade
.getRaceRef().get())
{
path = pathForRow;
}
}
}
if (path != null)
{
companionsTable.getTree().setSelectionPath(path);
companionsTable.getTree().scrollPathToVisible(path);
}
}
private class TreeExpansionHandler implements TreeExpansionListener
{
private final JTree tree;
private List<TreePath> expandedPaths = Collections.emptyList();
public TreeExpansionHandler()
{
this.tree = companionsTable.getTree();
}
public void install()
{
for (TreePath path : expandedPaths)
{
tree.expandPath(path);
}
tree.addTreeExpansionListener(this);
}
public void uninstall()
{
tree.removeTreeExpansionListener(this);
}
@Override
public void treeExpanded(TreeExpansionEvent event)
{
saveExpansionState();
}
@Override
public void treeCollapsed(TreeExpansionEvent event)
{
saveExpansionState();
}
private void saveExpansionState()
{
Object root = companionsTable.getTreeTableModel().getRoot();
Enumeration<TreePath> paths = tree.getExpandedDescendants(new TreePath(root));
expandedPaths = Collections.list(paths);
}
}
private class LoadButtonAndSheetHandler extends AbstractAction implements ListSelectionListener
{
private final HtmlSheetSupport sheetSupport;
private int selectedRow;
private ListSelectionModel selectionModel;
private PCGenFrame frame;
public LoadButtonAndSheetHandler()
{
File sheet =
FileUtils.getFile(ConfigurationSettings.getPreviewDir(),
"companions", "compact_companion.htm"); //$NON-NLS-1$ //$NON-NLS-2$
this.sheetSupport = new HtmlSheetSupport(infoPane, sheet.getAbsolutePath());
this.selectedRow = -1;
this.selectionModel = companionsTable.getSelectionModel();
this.frame = (PCGenFrame) JOptionPane.getFrameForComponent(CompanionInfoTab.this);
}
public void install()
{
configureButton();
loadButton.setAction(this);
if (selectedRow == -1)
{
selectionModel.clearSelection();
}
else
{
selectionModel.setSelectionInterval(selectedRow, selectedRow);
}
selectionModel.addListSelectionListener(this);
showCompanion(false);
sheetSupport.install();
}
public void uninstall()
{
selectionModel.removeListSelectionListener(this);
sheetSupport.uninstall();
}
private void configureButton()
{
CompanionFacade companion = getSelectedCompanion();
setEnabled(companion != null);
if (companion != null && isCompanionOpen(companion))
{
//configure action for show
this.putValue(Action.NAME,
LanguageBundle.getString("in_companionSwitchTo")); //$NON-NLS-1$
}
else
{
//configure action for load
this.putValue(Action.NAME,
LanguageBundle.getString("in_companionLoadComp")); //$NON-NLS-1$
}
}
void showCompanion(boolean switchTabs)
{
CompanionFacade companion = getSelectedCompanion();
if (companion == null)
{
if (!switchTabs)
{
infoPane.setText(""); //$NON-NLS-1$
}
return;
}
if (isCompanionOpen(companion))
{
CharacterFacade character = CharacterManager.getCharacterMatching(companion);
if (character != null)
{
if (switchTabs)
{
frame.setSelectedCharacter(character);
return;
}
else
{
sheetSupport.setCharacter(character);
sheetSupport.refresh();
}
}
//the companion was not found
//TODO: show error, complain?
}
else if (switchTabs)
{
frame.loadCharacterFromFile(companion.getFileRef().get());
}
else
{
// Display a message telling the user to open the companion.
infoPane.setText(LanguageBundle
.getString("in_companionLoadCompanionMessage")); //$NON-NLS-1$
}
}
@Override
public void actionPerformed(ActionEvent e)
{
showCompanion(true);
}
@Override
public void valueChanged(ListSelectionEvent e)
{
if (e.getValueIsAdjusting())
{
return;
}
selectedRow = selectionModel.getMinSelectionIndex();
configureButton();
showCompanion(false);
}
private CompanionFacade getSelectedCompanion()
{
if (selectedRow == -1)
{
return null;
}
Object value = companionsTable.getValueAt(selectedRow, 0);
if (value instanceof CompanionFacade)
{
return (CompanionFacade) value;
}
return null;
}
private boolean isCompanionOpen(CompanionFacade companion)
{
File compFile = companion.getFileRef().get();
if (compFile == null)
{
return true;
}
for (CharacterFacade character : CharacterManager.getCharacters())
{
File charFile = character.getFileRef().get();
if (compFile.equals(charFile))
{
return true;
}
}
return false;
}
}
private static class ButtonCellRenderer extends JPanel implements TableCellRenderer
{
private final JButton button = new JButton();
private final DefaultTableCellRenderer background = new DefaultTableCellRenderer();
public ButtonCellRenderer()
{
button.setMargin(new Insets(0, 0, 0, 0));
setOpaque(true);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
gbc.weighty = 1;
gbc.anchor = GridBagConstraints.EAST;
gbc.fill = GridBagConstraints.VERTICAL;
add(button, gbc);
}
@Override
public boolean isOpaque()
{
Color back = getBackground();
Component p = getParent();
if (p != null)
{
p = p.getParent();
}
// p should now be the JTable.
boolean colorMatch = (back != null) && (p != null) && back.equals(p.getBackground())
&& p.isOpaque();
return !colorMatch && super.isOpaque();
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
background.getTableCellRendererComponent(table, null, isSelected, hasFocus, row, column);
setBackground(background.getBackground());
value = table.getValueAt(row, 0);
if (value instanceof CompanionFacade)
{
button.setText(LanguageBundle.getString("in_companionRemove")); //$NON-NLS-1$
}
else
{
button.setText(LanguageBundle
.getString("in_companionCreateNew")); //$NON-NLS-1$
}
value = table.getValueAt(row, 1);
if (value instanceof Boolean)
{
button.setEnabled((Boolean) value);
}
else
{
button.setEnabled(true);
}
return this;
}
}
private class ButtonCellEditor extends AbstractCellEditor implements TableCellEditor,
ActionListener
{
private static final String CREATE_COMMAND = "New"; //$NON-NLS-1$
private static final String REMOVE_COMMAND = "Remove"; //$NON-NLS-1$
private final JButton button = new JButton();
private final JPanel container = new JPanel();
private final DefaultTableCellRenderer background = new DefaultTableCellRenderer();
private final CharacterFacade character;
public ButtonCellEditor(CharacterFacade character)
{
this.character = character;
button.addActionListener(this);
button.setMargin(new Insets(0, 0, 0, 0));
container.setOpaque(true);
container.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
gbc.weighty = 1;
gbc.anchor = GridBagConstraints.EAST;
gbc.fill = GridBagConstraints.VERTICAL;
container.add(button, gbc);
}
@Override
public Object getCellEditorValue()
{
return null;
}
private Object selectedElement;
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
{
background.getTableCellRendererComponent(table, null, true, false, row, column);
container.setBackground(background.getBackground());
selectedElement = table.getValueAt(row, 0);
if (selectedElement instanceof CompanionFacade)
{
button.setText(LanguageBundle.getString("in_companionRemove")); //$NON-NLS-1$
button.setActionCommand(REMOVE_COMMAND);
}
else
{
button.setText(LanguageBundle
.getString("in_companionCreateNew")); //$NON-NLS-1$
button.setActionCommand(CREATE_COMMAND);
}
return container;
}
@Override
public void actionPerformed(ActionEvent e)
{
CompanionSupportFacade support = character.getCompanionSupport();
if (REMOVE_COMMAND.equals(e.getActionCommand()))
{
CompanionFacade companion = (CompanionFacade) selectedElement;
int ret =
JOptionPane.showConfirmDialog(button, LanguageBundle
.getFormattedString(
"in_companionConfirmRemovalMsg", companion //$NON-NLS-1$
.getNameRef().get()),
LanguageBundle
.getString("in_companionConfirmRemoval"), //$NON-NLS-1$
JOptionPane.YES_NO_OPTION);
if (ret == JOptionPane.YES_OPTION)
{
support.removeCompanion(companion);
}
}
if (CREATE_COMMAND.equals(e.getActionCommand()))
{
initDialog();
String type = (String) selectedElement;
companionDialog.setCharacter(character);
companionDialog.setCompanionType(type);
Utility.setComponentRelativeLocation(CompanionInfoTab.this, companionDialog);
companionDialog.setVisible(true);
CharacterFacade comp = companionDialog.getNewCompanion();
if (comp != null)
{
selectCompanion(comp);
}
}
cancelCellEditing();
}
}
private static class FilteredCompanionList extends FilteredListFacade<String, CompanionStubFacade>
implements Filter<String, CompanionStubFacade>
{
public FilteredCompanionList()
{
setFilter(this);
}
public void setCompanionType(String type)
{
setContext(type);
}
@Override
public boolean accept(String context, CompanionStubFacade element)
{
if (context == null)
{
return true;
}
return context.equals(element.getCompanionType());
}
}
private class CompanionDialog extends JDialog implements TreeViewModel<CompanionStubFacade>,
DataView<CompanionStubFacade>, ActionListener
{
private final FilteredCompanionList model;
private final JButton selectButton;
private final FilteredTreeViewTable raceTable;
private CharacterFacade character;
private String companionType;
private CharacterFacade newCompanion;
private DefaultListFacade<CompanionTreeView> treeViews = new DefaultListFacade<>(
Arrays.asList(CompanionTreeView.values()));
public CompanionDialog()
{
super(JOptionPane.getFrameForComponent(CompanionInfoTab.this), true);
this.model = new FilteredCompanionList();
this.selectButton = new JButton();
this.raceTable = new FilteredTreeViewTable();
initComponents();
pack();
}
private void initComponents()
{
setTitle(LanguageBundle.getString("in_companionSelectRace")); //$NON-NLS-1$
setLayout(new BorderLayout());
Container container = getContentPane();
{
final ListSelectionModel selectionModel = raceTable.getSelectionModel();
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
selectionModel.addListSelectionListener(new ListSelectionListener()
{
@Override
public void valueChanged(ListSelectionEvent e)
{
if (!e.getValueIsAdjusting())
{
selectButton.setEnabled(!selectionModel.isSelectionEmpty());
}
}
});
}
SearchFilterPanel searchBar = new SearchFilterPanel();
container.add(searchBar, BorderLayout.NORTH);
raceTable.setDisplayableFilter(searchBar);
raceTable.addActionListener(this);
raceTable.setTreeViewModel(this);
container.add(new JScrollPane(raceTable), BorderLayout.CENTER);
JPanel buttonPane = new JPanel(new FlowLayout());
selectButton.addActionListener(this);
selectButton.setEnabled(false);
selectButton.setActionCommand("SELECT");
buttonPane.add(selectButton);
JButton cancelButton = new JButton(LanguageBundle.getString("in_cancel"));
cancelButton.addActionListener(this);
cancelButton.setActionCommand("CANCEL");
buttonPane.add(cancelButton);
container.add(buttonPane, BorderLayout.SOUTH);
Utility.installEscapeCloseOperation(this);
}
@Override
public void actionPerformed(ActionEvent e)
{
if (!"null".equals(e.getActionCommand()))
{
if ("SELECT".equals(e.getActionCommand()) || (JTreeTable.ACTION_DOUBLECLICK == e.getID()))
{
newCompanion = CharacterManager.createNewCharacter(character.getUIDelegate(), character.getDataSet());
CompanionStubFacade selected = (CompanionStubFacade) raceTable.getSelectedObject();
newCompanion.setRace(selected.getRaceRef().get());
character.getCompanionSupport().addCompanion(newCompanion, companionType);
setVisible(false);
}
else
{
newCompanion = null;
setVisible(false);
}
}
}
public void setCharacter(CharacterFacade character)
{
this.character = character;
model.setDelegate(character.getCompanionSupport().getAvailableCompanions());
}
public void setCompanionType(String type)
{
companionType = type;
model.setCompanionType(type);
selectButton.setText(LanguageBundle.getFormattedString(
"in_companionCreateType", type)); //$NON-NLS-1$
newCompanion = null;
}
@Override
public ListFacade<? extends TreeView<CompanionStubFacade>> getTreeViews()
{
return treeViews;
}
@Override
public int getDefaultTreeViewIndex()
{
return 0;
}
@Override
public DataView<CompanionStubFacade> getDataView()
{
return this;
}
@Override
public ListFacade<CompanionStubFacade> getDataModel()
{
return model;
}
@Override
public Object getData(CompanionStubFacade element, int column)
{
return null;
}
@Override
public void setData(Object value, CompanionStubFacade element, int column)
{
}
@Override
public List<? extends DataViewColumn> getDataColumns()
{
return Collections.emptyList();
}
@Override
public String getPrefsKey()
{
return "CompanionAvail"; //$NON-NLS-1$
}
/**
* @return the newCompanion
*/
public CharacterFacade getNewCompanion()
{
return newCompanion;
}
}
private enum CompanionTreeView implements TreeView<CompanionStubFacade>
{
NAME("in_race"); //$NON-NLS-1$
private final String name;
private CompanionTreeView(String name)
{
this.name = LanguageBundle.getString(name);
}
@Override
public String getViewName()
{
return name;
}
@Override
public List<TreeViewPath<CompanionStubFacade>> getPaths(CompanionStubFacade pobj)
{
switch (this)
{
case NAME:
return Collections.singletonList(new TreeViewPath<>(pobj));
default:
throw new InternalError();
}
}
}
private static class CompanionsModel extends AbstractTreeTableModel implements TreeTableModel
{
private CompanionSupportFacade support;
private MapFacade<String, Integer> maxMap;
public CompanionsModel(CharacterFacade character)
{
this.support = character.getCompanionSupport();
this.maxMap = support.getMaxCompanionsMap();
this.setRoot(new RootNode());
}
@Override
public boolean isCellEditable(Object node, int column)
{
if (column > 0)
{
Object value = getValueAt(node, column);
if (value instanceof Boolean)
{
return (Boolean) value;
}
return true;
}
else
{
return super.isCellEditable(node, column);
}
}
@Override
public int getColumnCount()
{
return 2;
}
private class CompanionNode extends DefaultTreeTableNode
{
private CompanionFacade companion;
public CompanionNode(CompanionFacade companion)
{
this.companion = companion;
}
@Override
public Object getValueAt(int column)
{
if (column == 0)
{
return companion;
}
return null;
}
@Override
public String toString()
{
return companion.getNameRef().get();
}
}
private class CompanionTypeNode extends DefaultTreeTableNode implements ReferenceListener<String>
{
private String type;
public CompanionTypeNode(String type)
{
super(Arrays.asList(type, null));
this.type = type;
}
@Override
public Object getValueAt(int column)
{
if (column > 0)
{
Integer max = maxMap.getValue(type);
if (max < 0)
{
return true;
}
return getChildCount() < max;
}
else
{
return super.getValueAt(column);
}
}
@SuppressWarnings("nls")
@Override
public String toString()
{
Integer max = maxMap.getValue(type);
String maxString = max == -1 ? "*" : max.toString();
return type + " (" + getChildCount() + "/" + maxString + ")";
}
private void addCompanion(CompanionFacade companion, boolean silently)
{
companion.getNameRef().addReferenceListener(this);
CompanionNode child = new CompanionNode(companion);
if (children == null)
{
children = new Vector();
}
@SuppressWarnings("unchecked")
int insertIndex = Collections.binarySearch(children, child, Comparators.toStringIgnoreCaseCollator());
if (insertIndex < 0)
{
if (silently)
{
insert(child, -(insertIndex + 1));
}
else
{
insertNodeInto(child, this, -(insertIndex + 1));
}
}
else
{
if (silently)
{
insert(child, insertIndex);
}
else
{
insertNodeInto(child, this, insertIndex);
}
}
if (!silently)
{
nodeChanged(this);
}
}
private void removeCompanion(CompanionFacade companion)
{
companion.getNameRef().removeReferenceListener(this);
//we create a dummy child for comparison
CompanionNode child = new CompanionNode(companion);
@SuppressWarnings("unchecked")
int index = Collections.binarySearch(children, child, Comparators.toStringIgnoreCaseCollator());
removeNodeFromParent((CompanionNode) getChildAt(index));
nodeChanged(this);
}
@Override
@SuppressWarnings("unchecked")
public void referenceChanged(ReferenceEvent<String> e)
{
children.sort(Comparators.toStringIgnoreCaseCollator());
int[] indexes = new int[getChildCount()];
for (int i = 0; i < indexes.length; i++)
{
indexes[i] = i;
}
nodesChanged(this, indexes);
}
@Override
public void setParent(MutableTreeNode newParent)
{
super.setParent(newParent);
if (newParent == null && children != null)
{
for (int i = 0; i < getChildCount(); i++)
{
CompanionNode child = (CompanionNode) getChildAt(i);
child.companion.getNameRef().removeReferenceListener(this);
}
}
}
}
class RootNode extends DefaultTreeTableNode implements MapListener<String, Integer>, ListListener<CompanionFacade>
{
private List<String> types;
private ListFacade<? extends CompanionFacade> companions;
public RootNode()
{
this.types = new ArrayList<>();
this.companions = support.getCompanions();
maxMap.addMapListener(this);
companions.addListListener(this);
initChildren();
}
private void initChildren()
{
types.clear();
types.addAll(maxMap.getKeys());
types.sort(Comparators.toStringIgnoreCaseCollator());
removeAllChildren();
for (String key : types)
{
CompanionTypeNode child = new CompanionTypeNode(key);
add(child);
}
for (CompanionFacade companion : companions)
{
addCompanion(companion, true);
}
}
private void addCompanion(CompanionFacade companion, boolean silently)
{
String type = companion.getCompanionType();
int index = Collections.binarySearch(types, type, Comparators.toStringIgnoreCaseCollator());
if (index < 0)
{
Logging.errorPrint("Unable to add companion " + companion
+ " as the type " + type + " could not be found.");
return;
}
CompanionTypeNode child = (CompanionTypeNode) getChildAt(index);
child.addCompanion(companion, silently);
}
@Override
public void keyAdded(MapEvent<String, Integer> e)
{
int insertIndex = Collections.binarySearch(types, e.getKey(), Comparators.toStringIgnoreCaseCollator());
if (insertIndex < 0)
{
insertIndex = -(insertIndex + 1);
}
types.add(insertIndex, e.getKey());
CompanionTypeNode child = new CompanionTypeNode(e.getKey());
insertNodeInto(child, this, insertIndex);
}
@Override
public void keyRemoved(MapEvent<String, Integer> e)
{
int index = types.indexOf(e.getKey());
types.remove(index);
removeNodeFromParent((MutableTreeNode) getChildAt(index));
}
@Override
public void keyModified(MapEvent<String, Integer> e)
{
//ignore this
}
@Override
public void valueChanged(MapEvent<String, Integer> e)
{
int index = types.indexOf(e.getKey());
nodeChanged(getChildAt(index));
}
@Override
public void valueModified(MapEvent<String, Integer> e)
{
//ignore this
}
@Override
public void keysChanged(MapEvent<String, Integer> e)
{
initChildren();
nodeStructureChanged(this);
}
@Override
public void elementAdded(ListEvent<CompanionFacade> e)
{
addCompanion(e.getElement(), false);
}
@Override
public void elementRemoved(ListEvent<CompanionFacade> e)
{
String type = e.getElement().getCompanionType();
int index = Collections.binarySearch(types, type, Comparators.toStringIgnoreCaseCollator());
CompanionTypeNode child = (CompanionTypeNode) getChildAt(index);
child.removeCompanion(e.getElement());
}
@Override
public void elementsChanged(ListEvent<CompanionFacade> e)
{
initChildren();
nodeStructureChanged(this);
}
@Override
public void elementModified(ListEvent<CompanionFacade> e)
{
//this is handled by the CompanionTypeNode
}
}
}
@Override
public void adviseTodo(String fieldName)
{
CompanionsModel model = (CompanionsModel) companionsTable.getTreeTableModel();
CompanionsModel.RootNode root = (CompanionsModel.RootNode) model.getRoot();
for (int i = 0; i < root.getChildCount(); i++)
{
TreeNode node = root.getChildAt(i);
if (node.toString().startsWith(fieldName))
{
companionsTable.getSelectionModel().setSelectionInterval(i, i);
return;
}
}
}
}