/* * EquipCustomPanel.java * Copyright James Dempsey, 2013 * * 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 01/03/2012 8:01:51 AM * * $Id$ */ package pcgen.gui2.equip; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingConstants; import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import pcgen.cdom.base.Constants; import pcgen.facade.core.CharacterFacade; import pcgen.facade.util.DefaultReferenceFacade; import pcgen.facade.core.EquipModFacade; import pcgen.facade.core.EquipmentBuilderFacade; import pcgen.facade.core.EquipmentBuilderFacade.EquipmentHead; import pcgen.facade.core.EquipmentFacade; import pcgen.facade.core.SizeAdjustmentFacade; import pcgen.facade.util.event.ListEvent; import pcgen.facade.util.event.ListListener; import pcgen.facade.util.event.ReferenceEvent; import pcgen.facade.util.DefaultListFacade; import pcgen.facade.util.ListFacade; import pcgen.gui2.filter.Filter; import pcgen.gui2.filter.FilterBar; import pcgen.gui2.filter.FilteredListFacade; import pcgen.gui2.filter.FilteredTreeViewTable; import pcgen.gui2.filter.SearchFilterPanel; import pcgen.gui2.tabs.models.CharacterComboBoxModel; import pcgen.gui2.tools.FlippingSplitPane; import pcgen.gui2.tools.Icons; import pcgen.gui2.tools.InfoPane; import pcgen.gui2.util.FontManipulation; import pcgen.gui2.util.TreeColumnCellRenderer; 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; /** * The Class {@code EquipCustomPanel} displays an available/selected table * pair to allow the creation of a custom piece of equipment.. * * <br> * * @author James Dempsey <jdempsey@users.sourceforge.net> */ @SuppressWarnings("serial") public class EquipCustomPanel extends FlippingSplitPane { private final FilteredTreeViewTable<Object, EquipModFacade> availableTable; private final FilteredTreeViewTable<Object, EquipModFacade> selectedTable; private final JButton nameButton; private final JButton spropButton; private final JButton costButton; private final JButton weightButton; private final JButton damageButton; private final JComboBox headCombo; private final JComboBox sizeCombo; private final JButton addButton; private final JButton removeButton; private final InfoPane equipModInfoPane; private final InfoPane equipInfoPane; private final CharacterFacade character; private TreeColumnCellRenderer renderer; private NameAction nameAction; private SPropAction spropAction; private CostAction costAction; private WeightAction weightAction; private DamageAction damageAction; private AddEqmodAction addAction; private RemoveEqmodAction removeAction; private EquipmentBuilderFacade builder; private EquipInfoHandler equipInfoHandler; private ListFacade<EquipmentHead> validHeads; private HeadBoxModel headBoxModel; private SizeBoxModel sizeBoxModel; private EquipmentHead currentHead = EquipmentHead.PRIMARY; private Map<EquipmentHead, EquipModTreeViewModel> availEqmodModelMap; private Map<EquipmentHead, EquipModTreeViewModel> selectedEqmodModelMap; /** * Create a new instance of EquipCustomPanel for a character. * @param character The character being displayed. * @param builder The equipment builder to be used for creating the item. */ public EquipCustomPanel(CharacterFacade character, EquipmentBuilderFacade builder) { super("customEquip"); this.character = character; this.builder = builder; validHeads = new DefaultListFacade<>( builder.getEquipmentHeads()); this.availableTable = new FilteredTreeViewTable<>(); this.selectedTable = new FilteredTreeViewTable<>(); this.nameButton = new JButton(); this.spropButton = new JButton(); this.costButton = new JButton(); this.weightButton = new JButton(); this.damageButton = new JButton(); this.headCombo = new JComboBox(); this.sizeCombo = new JComboBox(); this.addButton = new JButton(); this.removeButton = new JButton(); this.equipModInfoPane = new InfoPane(LanguageBundle.getString("in_igEqModInfo")); //$NON-NLS-1$ this.equipInfoPane = new InfoPane(LanguageBundle.getString("in_igEqInfo")); //$NON-NLS-1$ this.renderer = new EquipQualifiedTreeCellRenderer(character, builder.getEquipment()); this.nameAction = new NameAction(); this.spropAction = new SPropAction(); this.costAction = new CostAction(); this.weightAction = new WeightAction(); this.damageAction = new DamageAction(); this.addAction = new AddEqmodAction(); this.removeAction = new RemoveEqmodAction(); initHeadMaps(); initComponents(); initDefaults(); } /** * Setup any data related to multiple equipment heads. */ private void initHeadMaps() { availEqmodModelMap = new HashMap<>(); selectedEqmodModelMap = new HashMap<>(); for (EquipmentHead head : validHeads) { availEqmodModelMap.put(head, new EquipModTreeViewModel(character, builder, head, true)); selectedEqmodModelMap.put(head, new EquipModTreeViewModel(character, builder, head, false)); } } private void initComponents() { JPanel upperPanel = new JPanel(new BorderLayout()); setTopComponent(upperPanel); setOrientation(VERTICAL_SPLIT); Box bannerBox = Box.createHorizontalBox(); bannerBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); bannerBox.add(Box.createHorizontalGlue()); JLabel baseItemLabel = new JLabel(LanguageBundle.getString("in_EqBuilder_BaseItem")); FontManipulation.large(baseItemLabel); bannerBox.add(baseItemLabel); bannerBox.add(Box.createHorizontalStrut(5)); JLabel baseItemName = new JLabel(builder.getBaseItemName()); FontManipulation.large(baseItemName); FontManipulation.title(baseItemName); bannerBox.add(baseItemName); if (validHeads.getSize() > 1) { bannerBox.add(Box.createHorizontalStrut(45)); JLabel headLabel = new JLabel(LanguageBundle.getString("in_EqBuilder_Head")); FontManipulation.large(headLabel); bannerBox.add(headLabel); bannerBox.add(Box.createHorizontalStrut(5)); Dimension prefDim = headCombo.getPreferredSize(); prefDim.width += 15; headCombo.setMaximumSize(prefDim); bannerBox.add(headCombo); } bannerBox.add(Box.createHorizontalGlue()); upperPanel.add(bannerBox, BorderLayout.NORTH); FlippingSplitPane topPane = new FlippingSplitPane("equipCustTop"); upperPanel.add(topPane, BorderLayout.CENTER); JPanel availPanel = new JPanel(new BorderLayout()); FilterBar<Object, EquipModFacade> bar = new FilterBar<>(); bar.addDisplayableFilter(new SearchFilterPanel()); availPanel.add(bar, BorderLayout.NORTH); availableTable.setDisplayableFilter(bar); availableTable.setTreeViewModel(availEqmodModelMap.get(currentHead)); availableTable.setTreeCellRenderer(renderer); availPanel.add(new JScrollPane(availableTable), BorderLayout.CENTER); Box box = Box.createHorizontalBox(); box.add(Box.createHorizontalGlue()); addButton.setHorizontalTextPosition(SwingConstants.LEADING); addButton.setAction(addAction); 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()); Box equipButtonBox = Box.createHorizontalBox(); equipButtonBox.add(Box.createHorizontalGlue()); nameButton.setHorizontalTextPosition(SwingConstants.LEADING); nameButton.setAction(nameAction); equipButtonBox.add(nameButton); equipButtonBox.add(Box.createHorizontalStrut(5)); spropButton.setHorizontalTextPosition(SwingConstants.LEADING); spropButton.setAction(spropAction); equipButtonBox.add(spropButton); equipButtonBox.add(Box.createHorizontalStrut(5)); costButton.setHorizontalTextPosition(SwingConstants.LEADING); costButton.setAction(costAction); equipButtonBox.add(costButton); equipButtonBox.add(Box.createHorizontalStrut(5)); weightButton.setHorizontalTextPosition(SwingConstants.LEADING); weightButton.setAction(weightAction); equipButtonBox.add(weightButton); if (builder.isWeapon()) { equipButtonBox.add(Box.createHorizontalStrut(5)); damageButton.setHorizontalTextPosition(SwingConstants.LEADING); damageButton.setAction(damageAction); equipButtonBox.add(damageButton); } // Only show size if it can be used if (builder.isResizable()) { JPanel sizePanel = new JPanel(); JLabel sizeLabel = new JLabel(LanguageBundle.getString("in_EqBuilder_Size")); sizePanel.add(sizeLabel); sizePanel.add(sizeCombo); equipButtonBox.add(Box.createHorizontalStrut(5)); equipButtonBox.add(sizePanel); } equipButtonBox.add(Box.createHorizontalGlue()); equipButtonBox.setBorder(new EmptyBorder(5, 0, 0, 0)); selPanel.add(equipButtonBox, BorderLayout.NORTH); selectedTable.setTreeViewModel(selectedEqmodModelMap.get(currentHead)); selectedTable.setTreeCellRenderer(renderer); selPanel.add(new JScrollPane(selectedTable), BorderLayout.CENTER); box = Box.createHorizontalBox(); removeButton.setHorizontalTextPosition(SwingConstants.TRAILING); removeButton.setAction(removeAction); 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); FlippingSplitPane bottomPane = new FlippingSplitPane("equipCustBottom"); bottomPane.setLeftComponent(equipModInfoPane); bottomPane.setRightComponent(equipInfoPane); setBottomComponent(bottomPane); setResizeWeight(0.75); } /** * */ private void initDefaults() { equipInfoHandler = new EquipInfoHandler(character, builder); selectedTable.getSelectionModel().addListSelectionListener(equipInfoHandler); EquipModInfoHandler eqModInfoHandler = new EquipModInfoHandler(character, builder); availableTable.getSelectionModel().addListSelectionListener(eqModInfoHandler); selectedTable.getSelectionModel().addListSelectionListener(eqModInfoHandler); availableTable.addActionListener(addAction); sizeBoxModel = new SizeBoxModel(); sizeCombo.setModel(sizeBoxModel); headBoxModel = new HeadBoxModel(); headCombo.setModel(headBoxModel); } private class EquipInfoHandler implements ListSelectionListener { private CharacterFacade character; private EquipmentBuilderFacade builder2; public EquipInfoHandler(CharacterFacade character, EquipmentBuilderFacade builder) { this.character = character; builder2 = builder; refreshInfo(); } private void refreshInfo() { EquipmentFacade equip = builder2.getEquipment(); equipInfoPane.setText(character.getInfoFactory().getHTMLInfo(equip)); } @Override public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { refreshInfo(); } } } private class EquipModInfoHandler implements ListSelectionListener { private CharacterFacade character; private EquipmentBuilderFacade builder; private EquipModFacade currObj; public EquipModInfoHandler(CharacterFacade character, EquipmentBuilderFacade builder) { this.character = character; this.builder = builder; } @Override public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { Object obj = null; if (e.getSource() == availableTable.getSelectionModel()) { int selectedRow = availableTable.getSelectedRow(); if (selectedRow != -1) { obj = availableTable.getModel().getValueAt(selectedRow, 0); } } else { int selectedRow = selectedTable.getSelectedRow(); if (selectedRow != -1) { obj = selectedTable.getModel().getValueAt(selectedRow, 0); } } if (obj instanceof EquipModFacade && obj != currObj) { currObj = (EquipModFacade) obj; equipModInfoPane.setText(character.getInfoFactory() .getHTMLInfo((EquipModFacade) obj, builder .getEquipment())); } } } } private class AddEqmodAction extends AbstractAction { public AddEqmodAction() { super(LanguageBundle.getString("in_eqCust_AddPrimary")); //$NON-NLS-1$ putValue(SMALL_ICON, Icons.Forward16.getImageIcon()); } @Override public void actionPerformed(ActionEvent e) { List<Object> data = availableTable.getSelectedData(); for (Object eqMod : data) { if (eqMod instanceof EquipModFacade) { builder.addModToEquipment((EquipModFacade) eqMod, currentHead); } } equipInfoHandler.refreshInfo(); availableTable.refilter(); } } private class RemoveEqmodAction extends AbstractAction { public RemoveEqmodAction() { super(LanguageBundle.getString("in_eqCust_RemovePrimary")); //$NON-NLS-1$ putValue(SMALL_ICON, Icons.Back16.getImageIcon()); } @Override public void actionPerformed(ActionEvent e) { List<Object> data = selectedTable.getSelectedData(); for (Object eqMod : data) { if (eqMod instanceof EquipModFacade) { builder.removeModFromEquipment((EquipModFacade) eqMod, currentHead); } } equipInfoHandler.refreshInfo(); availableTable.refilter(); } } private class NameAction extends AbstractAction { public NameAction() { super(LanguageBundle.getString("in_nameLabel")); //$NON-NLS-1$ } @Override public void actionPerformed(ActionEvent e) { Object result = JOptionPane.showInputDialog(EquipCustomPanel.this, LanguageBundle.getString("in_eqCust_NewName"), Constants.APPLICATION_NAME, JOptionPane.QUESTION_MESSAGE, null, null, builder .getEquipment().toString()); String selectedValue = result == null ? "" : result.toString(); builder.setName(selectedValue); equipInfoHandler.refreshInfo(); } } private class SPropAction extends AbstractAction { public SPropAction() { super(LanguageBundle.getString("in_eqCust_SProp")); //$NON-NLS-1$ } @Override public void actionPerformed(ActionEvent e) { Object result = JOptionPane.showInputDialog(EquipCustomPanel.this, LanguageBundle.getString("in_eqCust_NewSProp"), Constants.APPLICATION_NAME, JOptionPane.QUESTION_MESSAGE, null, null, builder .getEquipment().getRawSpecialProperties()); String selectedValue = result == null ? "" : result.toString(); builder.setSProp(selectedValue); equipInfoHandler.refreshInfo(); } } private class CostAction extends AbstractAction { public CostAction() { super(LanguageBundle.getString("in_igEqModelColCost")); //$NON-NLS-1$ } @Override public void actionPerformed(ActionEvent e) { Object result = JOptionPane.showInputDialog(EquipCustomPanel.this, LanguageBundle.getString("in_eqCust_NewCost"), Constants.APPLICATION_NAME, JOptionPane.QUESTION_MESSAGE, null, null, character .getInfoFactory().getCost(builder.getEquipment())); String selectedValue = result == null ? "" : result.toString(); builder.setCost(selectedValue); equipInfoHandler.refreshInfo(); } } private class WeightAction extends AbstractAction { public WeightAction() { super(LanguageBundle.getString("in_igEqModelColWeight")); //$NON-NLS-1$ } @Override public void actionPerformed(ActionEvent e) { Object result = JOptionPane .showInputDialog( EquipCustomPanel.this, LanguageBundle.getString("in_eqCust_NewWeight"), Constants.APPLICATION_NAME, JOptionPane.QUESTION_MESSAGE, null, null, character.getInfoFactory().getWeight( builder.getEquipment())); String selectedValue = result == null ? "" : result.toString(); builder.setWeight(selectedValue); equipInfoHandler.refreshInfo(); } } private class DamageAction extends AbstractAction { public DamageAction() { super(LanguageBundle.getString("in_igInfoLabelTextDamage")); //$NON-NLS-1$ } @Override public void actionPerformed(ActionEvent e) { Object result = JOptionPane .showInputDialog( EquipCustomPanel.this, LanguageBundle.getString("in_eqCust_NewDamage"), Constants.APPLICATION_NAME, JOptionPane.QUESTION_MESSAGE, null, null, builder.getDamage()); String selectedValue = result == null ? "" : result.toString(); builder.setDamage(selectedValue); equipInfoHandler.refreshInfo(); } } private static class EquipModTreeViewModel implements TreeViewModel<EquipModFacade>, DataView<EquipModFacade>, Filter<EquipmentBuilderFacade, EquipModFacade>, ListListener<EquipModFacade> { private static final DefaultListFacade<? extends TreeView<EquipModFacade>> treeViews = new DefaultListFacade<TreeView<EquipModFacade>>(Arrays.asList(EquipModTreeView.values())); private final List<DefaultDataViewColumn> columns; private final boolean isAvailModel; private FilteredListFacade<EquipmentBuilderFacade, EquipModFacade> equipMods; private EquipmentBuilderFacade builder; private EquipmentHead head; public EquipModTreeViewModel(CharacterFacade character, EquipmentBuilderFacade builder, EquipmentHead head, boolean isAvailModel) { this.builder = builder; this.head = head; this.isAvailModel = isAvailModel; equipMods = new FilteredListFacade<>(); equipMods.setContext(builder); equipMods.setFilter(this); if (isAvailModel) { ListFacade<EquipModFacade> eqModList = builder.getAvailList(head); equipMods.setDelegate(eqModList); builder.getAvailList(head).addListListener(this); columns = Arrays.asList(new DefaultDataViewColumn("in_source", String.class, false)); //$NON-NLS-1$ } else { columns = Arrays.asList(new DefaultDataViewColumn("in_source", String.class, false)); //$NON-NLS-1$ } } @Override public ListFacade<? extends TreeView<EquipModFacade>> getTreeViews() { return treeViews; } @Override public int getDefaultTreeViewIndex() { return 0; } @Override public DataView<EquipModFacade> getDataView() { return this; } @Override public ListFacade<EquipModFacade> getDataModel() { if (isAvailModel) { return equipMods; } return builder.getSelectedList(head); } @Override public Object getData(EquipModFacade element, int column) { switch(column){ case 0: return element.getSource(); default: return null; } } @Override public void setData(Object value, EquipModFacade element, int column) { } @Override public List<? extends DataViewColumn> getDataColumns() { return columns; } @Override public void elementAdded(ListEvent<EquipModFacade> e) { //equipMods.elementAdded(e); } @Override public void elementRemoved(ListEvent<EquipModFacade> e) { //equipMods.elementRemoved(e); } @Override public void elementsChanged(ListEvent<EquipModFacade> e) { //equipMods.refilter(); } @Override public void elementModified(ListEvent<EquipModFacade> e) { //equipMods.refilter(); } @Override public boolean accept(EquipmentBuilderFacade context, EquipModFacade element) { return true; } @Override public String getPrefsKey() { return isAvailModel ? "EqModTreeAvail" : "EqModTreeSelected"; //$NON-NLS-1$//$NON-NLS-2$ } } private enum EquipModTreeView implements TreeView<EquipModFacade> { NAME(LanguageBundle.getString("in_nameLabel")), //$NON-NLS-1$ TYPE_NAME(LanguageBundle.getString("in_typeName")), //$NON-NLS-1$ SOURCE_NAME(LanguageBundle.getString("in_sourceName")); //$NON-NLS-1$ private final String name; private EquipModTreeView(String name) { this.name = name; } @Override public String getViewName() { return name; } @SuppressWarnings("unchecked") @Override public List<TreeViewPath<EquipModFacade>> getPaths(EquipModFacade pobj) { switch (this) { case NAME: return Collections.singletonList(new TreeViewPath<>(pobj)); case TYPE_NAME: TreeViewPath<EquipModFacade> path = createTreeViewPath(pobj, (Object[]) pobj .getDisplayType().split("\\.")); //$NON-NLS-1$ return Arrays.asList(path); case SOURCE_NAME: return Collections.singletonList(new TreeViewPath<>(pobj, pobj.getSourceForNodeDisplay())); default: throw new InternalError(); } } /** * Create a TreeViewPath for the equipment modifier and paths. * @param pobj The equipment modifier * @param path The paths under which the equipment modifier should be shown. * @return The TreeViewPath. */ private static TreeViewPath<EquipModFacade> createTreeViewPath(EquipModFacade pobj, Object... path) { if (path.length == 0) { return new TreeViewPath<>(pobj); } if (path.length > 2) { return new TreeViewPath<>(pobj, path[0], path[1]); } return new TreeViewPath<>(pobj, path); } } private class HeadBoxModel extends CharacterComboBoxModel<EquipmentHead> { private DefaultReferenceFacade<EquipmentHead> headRef; public HeadBoxModel() { setListFacade(validHeads); headRef = new DefaultReferenceFacade<>( currentHead); setReference(headRef); } @Override public void setSelectedItem(Object anItem) { EquipmentHead head = (EquipmentHead) anItem; currentHead = head; headRef.set(head); availableTable.setTreeViewModel(availEqmodModelMap.get(currentHead)); selectedTable.setTreeViewModel(selectedEqmodModelMap.get(currentHead)); } @Override public void referenceChanged(ReferenceEvent<EquipmentHead> e) { super.referenceChanged(e); } } private class SizeBoxModel extends CharacterComboBoxModel<SizeAdjustmentFacade> { public SizeBoxModel() { setListFacade(character.getDataSet().getSizes()); setReference(builder.getSizeRef()); } @Override public void setSelectedItem(Object anItem) { builder.setSize((SizeAdjustmentFacade) anItem); } @Override public void referenceChanged(ReferenceEvent<SizeAdjustmentFacade> e) { super.referenceChanged(e); equipInfoHandler.refreshInfo(); } } }