/* * EquipmentModels.java * Copyright 2011 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 Jan 25, 2011, 3:26:08 PM */ package pcgen.gui2.tabs.equip; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.ParseException; import java.util.ArrayList; import java.util.List; import javax.swing.AbstractAction; import javax.swing.AbstractCellEditor; 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.JSpinner; import javax.swing.JTable; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellEditor; import javax.swing.table.TableModel; import pcgen.base.util.HashMapToList; import pcgen.base.util.MapToList; import pcgen.facade.core.CharacterFacade; import pcgen.facade.core.EquipmentFacade; import pcgen.facade.core.EquipmentListFacade; import pcgen.facade.core.EquipmentSetFacade; import pcgen.facade.core.EquipmentSetFacade.EquipNode; import pcgen.facade.core.EquipmentSetFacade.EquipNode.NodeType; import pcgen.facade.util.ReferenceFacade; import pcgen.facade.util.event.ReferenceEvent; import pcgen.facade.util.event.ReferenceListener; import pcgen.gui2.filter.DisplayableFilter; import pcgen.gui2.filter.FilterHandler; import pcgen.gui2.tools.Icons; import pcgen.gui2.util.JTableEx; import pcgen.gui2.util.JTreeTable; import pcgen.gui2.util.table.TableCellUtilities; import pcgen.system.LanguageBundle; /** * The container for equipping data for a character. It holds references to the * models for both the left and right tables of gear. It also contains the * processing to manage equipping and unequipping actions. * * <br> * * @author Connor Petty <cpmeister@users.sourceforge.net> */ public class EquipmentModels { public enum EquipView { FULL, UNEQUIPPED, EQUIPPED; @Override public String toString() { switch (this) { case FULL: return LanguageBundle.getString("in_equipListFull"); //$NON-NLS-1$ case UNEQUIPPED: return LanguageBundle.getString("in_equipListUnequipped"); //$NON-NLS-1$ case EQUIPPED: return LanguageBundle.getString("in_equipListEquipped"); //$NON-NLS-1$ default: throw new InternalError(); } } } private final CharacterFacade character; private final EquipmentTableModel fullModel; private final EquipmentTableModel unequippedModel; private final EquipmentTableModel equippedModel; private final UnequippedList unequippedList; private final EquipViewHandler viewHandler; private final EquipAction equipAction; private final UnequipAction unequipAction; private final MoveUpAction moveUpAction; private final MoveDownAction moveDownAction; private final EquipFilterHandler filterHandler; private EquipView selectedView; private EquipmentTableModel selectedModel; private final JComboBox equipViewBox; private final JTableEx equipmentTable; private final JTreeTable equipmentSetTable; private final JButton equipButton; private final JButton unequipButton; private final JButton moveUpButton; private final JButton moveDownButton; private final DisplayableFilter<? super CharacterFacade, ? super EquipmentFacade> filter; public EquipmentModels(CharacterFacade character, JComboBox equipBox, JTableEx eqTable, DisplayableFilter<? super CharacterFacade, ? super EquipmentFacade> filter, JTreeTable eqSetTable, JButton equipButton, JButton unequipButton, JButton moveUpButton, JButton moveDownButton) { this.character = character; this.unequippedList = new UnequippedList(character); this.fullModel = new EquipmentTableModel(character); fullModel.setEquipmentList(character.getPurchasedEquipment()); fullModel.setEquipmentSet(character.getEquipmentSetRef().get()); this.unequippedModel = new EquipmentTableModel(character); unequippedModel.setEquipmentList(unequippedList); unequippedModel.setEquipmentSet(character.getEquipmentSetRef().get()); this.equippedModel = new EquippedTableModel(character); selectedModel = fullModel; selectedView = EquipView.UNEQUIPPED; this.viewHandler = new EquipViewHandler(); this.equipAction = new EquipAction(); this.unequipAction = new UnequipAction(); this.moveUpAction = new MoveUpAction(); this.moveDownAction = new MoveDownAction(); this.filterHandler = new EquipFilterHandler(); this.equipViewBox = equipBox; this.equipmentTable = eqTable; this.equipmentSetTable = eqSetTable; this.filter = filter; this.equipButton = equipButton; this.unequipButton = unequipButton; this.moveUpButton = moveUpButton; this.moveDownButton = moveDownButton; } public void install() { viewHandler.install(); equipButton.setAction(equipAction); unequipButton.setAction(unequipAction); moveUpButton.setAction(moveUpAction); moveDownButton.setAction(moveDownAction); equipAction.install(); unequipAction.install(); filter.setFilterHandler(filterHandler); fullModel.setFilter(filter); unequippedModel.setFilter(filter); equippedModel.setFilter(filter); } public void uninstall() { equipAction.uninstall(); unequipAction.uninstall(); } private List<EquipNode> getSelectedEquipmentSetNodes() { int[] rows = equipmentSetTable.getSelectedRows(); List<EquipNode> paths = new ArrayList<>(); for (int row : rows) { EquipNode path = (EquipNode) equipmentSetTable.getValueAt(row, 0); if (path.getNodeType() == NodeType.EQUIPMENT) { paths.add(path); } } return paths; } private void selectNodeInEquipmentSetTable(EquipNode nodeToSelect) { TableModel model = equipmentSetTable.getModel(); for (int i = 0; i < model.getRowCount(); i++) { if (nodeToSelect == model.getValueAt(i, 0)) { equipmentSetTable.getSelectionModel().setSelectionInterval(i, i); break; } } } private static JScrollPane prepareScrollPane(JTable table) { JScrollPane pane = new JScrollPane(table); Dimension size = table.getPreferredSize(); size.height += 30; // account for the header which has not been prepared yet final int decorationHeight = 80; final int decorationWidth = 70; Rectangle screenBounds = GraphicsEnvironment.getLocalGraphicsEnvironment() .getMaximumWindowBounds(); if (size.height > screenBounds.height - decorationHeight) { size.height = screenBounds.height - decorationHeight; } if (size.width > screenBounds.width - decorationWidth) { size.width = screenBounds.width - decorationWidth; } pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); pane.setPreferredSize(size); return pane; } private class EquipFilterHandler implements FilterHandler { @Override public void refilter() { selectedModel.refilter(); } @Override public void setSearchEnabled(boolean enable) { //do nothing } @Override public void scrollToTop() { // do nothing } } private class EquipViewHandler extends AbstractAction { public void install() { equipViewBox.setAction(this); equipViewBox.setSelectedItem(selectedView); equipmentTable.setModel(selectedModel); } @Override public void actionPerformed(ActionEvent e) { selectedView = (EquipView) equipViewBox.getSelectedItem(); switch (selectedView) { case FULL: selectedModel = fullModel; equipAction.setEnabled(true); break; case UNEQUIPPED: selectedModel = unequippedModel; equipAction.setEnabled(true); break; case EQUIPPED: selectedModel = equippedModel; equipAction.setEnabled(false); break; } equipmentTable.setModel(selectedModel); filterHandler.refilter(); } } private static class EquippedTableModel extends EquipmentTableModel implements ReferenceListener<EquipmentSetFacade> { public EquippedTableModel(CharacterFacade character) { super(character); ReferenceFacade<EquipmentSetFacade> ref = character.getEquipmentSetRef(); ref.addReferenceListener(this); setEquipmentList(ref.get().getEquippedItems()); setEquipmentSet(ref.get()); } @Override public void referenceChanged(ReferenceEvent<EquipmentSetFacade> e) { setEquipmentList(e.getNewReference().getEquippedItems()); setEquipmentSet(e.getNewReference()); //fireTableDataChanged(); } } private class UnequipAction extends AbstractAction { public UnequipAction() { super(LanguageBundle.getString("in_equipUnequipSel")); //$NON-NLS-1$ this.putValue(SMALL_ICON, Icons.Back16.getImageIcon()); } @Override public void actionPerformed(ActionEvent e) { EquipmentSetFacade equipSet = character.getEquipmentSetRef().get(); List<EquipNode> paths = getSelectedEquipmentSetNodes(); if (!paths.isEmpty()) { Object[][] data = new Object[paths.size()][3]; for (int i = 0; i < paths.size(); i++) { EquipNode path = paths.get(i); data[i][0] = path.getEquipment(); data[i][1] = equipSet.getQuantity(path); } Object[] columns = { LanguageBundle.getString("in_equipItem"), //$NON-NLS-1$ LanguageBundle.getString("in_equipQuantityAbbrev"), //$NON-NLS-1$ }; DefaultTableModel tableModel = new DefaultTableModel(data, columns) { @Override public Class<?> getColumnClass(int columnIndex) { if (columnIndex == 1) { return Integer.class; } return Object.class; } @Override public boolean isCellEditable(int row, int column) { return column != 0; } }; JTable table = new JTable(tableModel); table.setFocusable(false); table.setCellSelectionEnabled(false); table.setDefaultRenderer(Integer.class, new TableCellUtilities.SpinnerRenderer()); table.setDefaultEditor(Integer.class, new SpinnerEditor(equipSet.getEquippedItems())); table.setRowHeight(22); table.getColumnModel().getColumn(0).setPreferredWidth(140); table.getColumnModel().getColumn(1).setPreferredWidth(50); table.setPreferredScrollableViewportSize(table.getPreferredSize()); JTableHeader header = table.getTableHeader(); header.setReorderingAllowed(false); JScrollPane pane = EquipmentModels.prepareScrollPane(table); JPanel panel = new JPanel(new BorderLayout()); JLabel help = new JLabel(LanguageBundle.getString("in_equipSelectUnequipQty")); //$NON-NLS-1$ panel.add(help, BorderLayout.NORTH); panel.add(pane, BorderLayout.CENTER); int res = JOptionPane.showConfirmDialog( JOptionPane.getFrameForComponent(equipmentTable), panel, LanguageBundle.getString("in_equipUnequipSel"), //$NON-NLS-1$ JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); if (res == JOptionPane.OK_OPTION) { for (int i = 0; i < paths.size(); i++) { equipSet.removeEquipment(paths.get(i), (Integer) tableModel.getValueAt(i, 1)); } } } } public void install() { equipmentSetTable.addActionListener(this); } public void uninstall() { equipmentSetTable.removeActionListener(this); } } public class EquipAction extends AbstractAction { public EquipAction() { super(LanguageBundle.getString("in_equipEquipSel")); //$NON-NLS-1$ this.putValue(SMALL_ICON, Icons.Forward16.getImageIcon()); } @Override public void actionPerformed(ActionEvent e) { int[] selectedRows = equipmentTable.getSelectedRows(); MapToList<EquipmentFacade, EquipNode> equipMap = new HashMapToList<>(); EquipmentSetFacade equipSet = character.getEquipmentSetRef().get(); List<EquipmentFacade> equipment = new ArrayList<>(); for (int selectedRow : selectedRows) { EquipmentFacade equipmentFacade = selectedModel.getValue(selectedRow); for (EquipNode path : equipSet.getNodes()) { if (equipSet.canEquip(path, equipmentFacade)) { equipMap.addToListFor(equipmentFacade, path); } } if (equipMap.containsListFor(equipmentFacade)) { equipment.add(equipmentFacade); } } if (!equipment.isEmpty()) { Object[][] data = new Object[equipment.size()][3]; for (int i = 0; i < equipment.size(); i++) { EquipmentFacade equipmentFacade = equipment.get(i); data[i][0] = equipmentFacade; data[i][1] = unequippedList.getQuantity(equipmentFacade); data[i][2] = getInitialNode(equipMap, equipSet, equipmentFacade); } Object[] columns = { LanguageBundle.getString("in_equipItem"), //$NON-NLS-1$ LanguageBundle.getString("in_equipQuantityAbbrev"), //$NON-NLS-1$ LanguageBundle.getString("in_equipContainer") //$NON-NLS-1$ }; DefaultTableModel tableModel = new DefaultTableModel(data, columns) { @Override public Class<?> getColumnClass(int columnIndex) { if (columnIndex == 1) { return Integer.class; } return Object.class; } @Override public boolean isCellEditable(int row, int column) { return column != 0; } }; JTable table = new JTable(tableModel); table.setFocusable(false); table.setCellSelectionEnabled(false); table.setDefaultEditor(Object.class, new ComboEditor(equipMap)); table.setDefaultRenderer(Integer.class, new TableCellUtilities.SpinnerRenderer()); table.setDefaultEditor(Integer.class, new SpinnerEditor(unequippedList)); table.setRowHeight(22); table.getColumnModel().getColumn(0).setPreferredWidth(140); table.getColumnModel().getColumn(1).setPreferredWidth(50); table.getColumnModel().getColumn(2).setPreferredWidth(120); table.setPreferredScrollableViewportSize(table.getPreferredSize()); JTableHeader header = table.getTableHeader(); header.setReorderingAllowed(false); JScrollPane pane = EquipmentModels.prepareScrollPane(table); JPanel panel = new JPanel(new BorderLayout()); JLabel help = new JLabel(LanguageBundle.getString("in_equipSelectQtyLoc")); //$NON-NLS-1$ panel.add(help, BorderLayout.NORTH); panel.add(pane, BorderLayout.CENTER); int res = JOptionPane.showConfirmDialog( JOptionPane.getFrameForComponent(equipmentTable), panel, LanguageBundle.getString("in_equipEquipSel"), //$NON-NLS-1$ JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); if (res == JOptionPane.OK_OPTION) { for (int i = 0; i < equipment.size(); i++) { EquipNode path = (EquipNode) tableModel.getValueAt(i, 2); equipSet.addEquipment(path, equipment.get(i), (Integer) tableModel.getValueAt(i, 1)); } } } } private EquipNode getInitialNode(MapToList<EquipmentFacade, EquipNode> equipMap, EquipmentSetFacade equipSet, EquipmentFacade equipmentFacade) { // First see if the user has selected a suitable node in the equipped tree List<EquipNode> possibleNodeList = equipMap.getListFor(equipmentFacade); int[] rows = equipmentSetTable.getSelectedRows(); List<EquipNode> paths = new ArrayList<>(); for (int row : rows) { EquipNode path = (EquipNode) equipmentSetTable.getValueAt(row, 0); if (possibleNodeList.contains(path)) { return path; } } // Check if the preferred location can be found in the list String preferredNodeName = equipSet.getPreferredLoc(equipmentFacade); for (EquipNode node : possibleNodeList) { if (preferredNodeName.equals(node.toString())) { return node; } } // Fall back to the first item in the list return equipMap.getElementInList(equipmentFacade, 0); } public void install() { equipmentTable.addActionListener(this); } public void uninstall() { equipmentTable.removeActionListener(this); } } private static class SpinnerEditor extends AbstractCellEditor implements TableCellEditor, ChangeListener { private JSpinner spinner = new JSpinner(); private final EquipmentListFacade equipmentList; public SpinnerEditor(EquipmentListFacade equipmentList) { this.equipmentList = equipmentList; spinner.addChangeListener(this); } @Override public Object getCellEditorValue() { return spinner.getValue(); } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { EquipmentFacade equipment = (EquipmentFacade) table.getValueAt(row, 0); int maxQuantity = equipmentList.getQuantity(equipment); int minQuantity = 1; if (maxQuantity <= 0) { minQuantity = maxQuantity = 0; } SpinnerNumberModel model = new SpinnerNumberModel(((Integer) value).intValue(), minQuantity, maxQuantity, 1); spinner.setModel(model); return spinner; } @Override public void stateChanged(ChangeEvent e) { stopCellEditing(); } @Override public boolean stopCellEditing() { try { spinner.commitEdit(); } catch (ParseException ex) { // Fall through and cancel the edit. } return super.stopCellEditing(); } } private static class ComboEditor extends AbstractCellEditor implements TableCellEditor, ActionListener { private JComboBox comboBox = null; private MapToList<EquipmentFacade, EquipNode> equipMap; public ComboEditor(MapToList<EquipmentFacade, EquipNode> equipMap) { this.equipMap = equipMap; } @Override public Object getCellEditorValue() { return comboBox.getSelectedItem(); } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { EquipmentFacade equipment = (EquipmentFacade) table.getValueAt(row, 0); if (comboBox != null) { comboBox.removeActionListener(this); } comboBox = new JComboBox(equipMap.getListFor(equipment).toArray()); comboBox.setSelectedItem(value); comboBox.addActionListener(this); return comboBox; } @Override public void actionPerformed(ActionEvent e) { stopCellEditing(); } } private class MoveUpAction extends AbstractAction { public MoveUpAction() { super(LanguageBundle.getString("in_equipMoveUpMenuCommand")); //$NON-NLS-1$ this.putValue(SMALL_ICON, Icons.Up16.getImageIcon()); } @Override public void actionPerformed(ActionEvent e) { EquipmentSetFacade equipSet = character.getEquipmentSetRef().get(); List<EquipNode> paths = getSelectedEquipmentSetNodes(); if (!paths.isEmpty()) { for (EquipNode node : paths) { equipSet.moveEquipment(node, -1); } selectNodeInEquipmentSetTable(paths.get(0)); } } public void install() { equipmentSetTable.addActionListener(this); } public void uninstall() { equipmentSetTable.removeActionListener(this); } } private class MoveDownAction extends AbstractAction { public MoveDownAction() { super(LanguageBundle.getString("in_equipMoveDownMenuCommand")); //$NON-NLS-1$ this.putValue(SMALL_ICON, Icons.Down16.getImageIcon()); } @Override public void actionPerformed(ActionEvent e) { EquipmentSetFacade equipSet = character.getEquipmentSetRef().get(); List<EquipNode> paths = getSelectedEquipmentSetNodes(); if (!paths.isEmpty()) { for (EquipNode node : paths) { equipSet.moveEquipment(node, 1); } selectNodeInEquipmentSetTable(paths.get(0)); } } public void install() { equipmentSetTable.addActionListener(this); } public void uninstall() { equipmentSetTable.removeActionListener(this); } } }