/*
* PurchaseInfoTab.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 14, 2011, 7:35:28 PM
*/
package pcgen.gui2.tabs;
import static pcgen.gui2.tabs.equip.EquipmentSelection.equipmentArrayFlavor;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.DropMode;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.TransferHandler;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import pcgen.cdom.base.Constants;
import pcgen.facade.core.CharacterFacade;
import pcgen.facade.core.EquipmentFacade;
import pcgen.facade.core.EquipmentListFacade;
import pcgen.facade.core.EquipmentListFacade.EquipmentListEvent;
import pcgen.facade.core.EquipmentListFacade.EquipmentListListener;
import pcgen.facade.core.GearBuySellFacade;
import pcgen.facade.util.DefaultListFacade;
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.FilteredTreeViewTable;
import pcgen.gui2.filter.SearchFilterPanel;
import pcgen.gui2.tabs.equip.EquipmentSelection;
import pcgen.gui2.tabs.models.BigDecimalFieldHandler;
import pcgen.gui2.tabs.models.CharacterComboBoxModel;
import pcgen.gui2.tabs.models.CharacterTreeCellRenderer;
import pcgen.gui2.tabs.models.CharacterTreeCellRenderer.Handler;
import pcgen.gui2.tools.FlippingSplitPane;
import pcgen.gui2.tools.Icons;
import pcgen.gui2.tools.InfoPane;
import pcgen.gui2.util.SignIcon;
import pcgen.gui2.util.SignIcon.Sign;
import pcgen.gui2.util.event.PopupMouseAdapter;
import pcgen.gui2.util.treeview.CachedDataView;
import pcgen.gui2.util.treeview.DataView;
import pcgen.gui2.util.treeview.DataViewColumn;
import pcgen.gui2.util.treeview.DefaultDataViewColumn;
import pcgen.gui2.util.treeview.TreeView;
import pcgen.gui2.util.treeview.TreeViewModel;
import pcgen.gui2.util.treeview.TreeViewPath;
import pcgen.system.CharacterManager;
import pcgen.system.LanguageBundle;
import pcgen.util.enumeration.Tab;
/**
* A character tab providing the user with the ability to buy and sell
* equipment.
*
* @author Connor Petty <cpmeister@users.sourceforge.net>
*/
@SuppressWarnings("serial")
public class PurchaseInfoTab extends FlippingSplitPane implements CharacterInfoTab
{
private static final Set<String> primaryTypes = new HashSet<>();
private final FilteredTreeViewTable<CharacterFacade, EquipmentFacade> availableTable;
private final FilteredTreeViewTable<CharacterFacade, EquipmentFacade> purchasedTable;
private final EquipmentRenderer equipmentRenderer;
private final JCheckBox autoResizeBox;
private final JButton addCustomButton;
private final JButton addEquipmentButton;
private final JButton removeEquipmentButton;
private final InfoPane infoPane;
private final JFormattedTextField wealthLabel;
private final JFormattedTextField goldField;
private final JFormattedTextField goldModField;
private final JComboBox buySellRateBox;
private final JButton fundsAddButton;
private final JButton fundsSubtractButton;
private final JCheckBox allowDebt;
private final List<JLabel> currencyLabels;
/**
* Create a new instance of PurchaseInfoTab
*/
public PurchaseInfoTab()
{
super("Purchase"); //$NON-NLS-1$
this.availableTable = new FilteredTreeViewTable<>();
this.purchasedTable = new FilteredTreeViewTable<>();
this.equipmentRenderer = new EquipmentRenderer();
this.autoResizeBox = new JCheckBox();
this.addCustomButton = new JButton();
this.addEquipmentButton = new JButton();
this.removeEquipmentButton = new JButton();
this.infoPane = new InfoPane();
this.wealthLabel = new JFormattedTextField(NumberFormat.getNumberInstance());
this.goldField = new JFormattedTextField(NumberFormat.getNumberInstance());
this.goldModField = new JFormattedTextField(NumberFormat.getNumberInstance());
this.buySellRateBox = new JComboBox();
this.fundsAddButton = new JButton();
this.fundsSubtractButton = new JButton();
this.allowDebt = new JCheckBox();
this.currencyLabels = new ArrayList<>();
initComponents();
}
private void initComponents()
{
setOrientation(VERTICAL_SPLIT);
FlippingSplitPane splitPane = new FlippingSplitPane("PurchaseTop"); //$NON-NLS-1$
splitPane.setOrientation(HORIZONTAL_SPLIT);
{// Top Left panel
FilterBar<CharacterFacade, EquipmentFacade> filterBar = new FilterBar<>();
{// Filters
filterBar.addDisplayableFilter(new SearchFilterPanel());
FilterButton<CharacterFacade, EquipmentFacade> premadeFilter
= new FilterButton<>("EqQualified"); //$NON-NLS-1$
premadeFilter.setText(LanguageBundle.getString("in_igQualFilter")); //$NON-NLS-1$
premadeFilter.setFilter(CharacterFacade::isQualifiedFor);
FilterButton<CharacterFacade, EquipmentFacade> customFilter
= new FilterButton<>("EqAffordable"); //$NON-NLS-1$
customFilter.setText(LanguageBundle.getString("in_igAffordFilter")); //$NON-NLS-1$
customFilter.setFilter(new Filter<CharacterFacade, EquipmentFacade>()
{
@Override
public boolean accept(CharacterFacade context, EquipmentFacade element)
{
return context.getInfoFactory().getCost(element) <= context.getFundsRef().get().floatValue();
}
});
filterBar.addDisplayableFilter(premadeFilter);
filterBar.addDisplayableFilter(customFilter);
}
JPanel panel = new JPanel(new BorderLayout());
panel.add(filterBar, BorderLayout.NORTH);
availableTable.setTreeCellRenderer(equipmentRenderer);
availableTable.setDisplayableFilter(filterBar);
panel.add(new JScrollPane(availableTable), BorderLayout.CENTER);
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalStrut(5));
box.add(autoResizeBox);
box.add(Box.createHorizontalGlue());
addCustomButton.setHorizontalTextPosition(SwingConstants.LEADING);
box.add(addCustomButton);
box.add(Box.createHorizontalStrut(5));
addEquipmentButton.setHorizontalTextPosition(SwingConstants.LEADING);
box.add(addEquipmentButton);
box.add(Box.createHorizontalStrut(5));
box.setBorder(new EmptyBorder(0, 0, 5, 0));
panel.add(box, BorderLayout.SOUTH);
splitPane.setLeftComponent(panel);
}
{// Top Right panel
FilterBar<CharacterFacade, EquipmentFacade> filterBar = new FilterBar<>();
filterBar.addDisplayableFilter(new SearchFilterPanel());
JPanel panel = new JPanel(new BorderLayout());
panel.add(filterBar, BorderLayout.NORTH);
purchasedTable.setDisplayableFilter(filterBar);
purchasedTable.setTreeCellRenderer(equipmentRenderer);
panel.add(new JScrollPane(purchasedTable), BorderLayout.CENTER);
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalStrut(5));
box.add(removeEquipmentButton);
box.add(Box.createHorizontalGlue());
box.setBorder(new EmptyBorder(0, 0, 5, 0));
panel.add(box, BorderLayout.SOUTH);
splitPane.setRightComponent(panel);
}
setTopComponent(splitPane);
splitPane = new FlippingSplitPane("PurchaseBottom"); //$NON-NLS-1$
splitPane.setOrientation(HORIZONTAL_SPLIT);
{// Bottom Left Panel
JPanel panel = new JPanel();
initMoneyPanel(panel);
splitPane.setLeftComponent(panel);
}
{// Bottom Right Panel
infoPane.setTitle(LanguageBundle.getString("in_igEqInfo")); //$NON-NLS-1$
splitPane.setRightComponent(infoPane);
}
splitPane.setResizeWeight(0.25);
setResizeWeight(1);
setBottomComponent(splitPane);
}
/**
* Create the money panel laying out all components.
*
* @param panel The panel to be populated.
*/
private void initMoneyPanel(JPanel panel)
{
panel.setLayout(new GridBagLayout());
GridBagConstraints leftGbc = new GridBagConstraints();
Insets panelInsets = panel.getInsets();
leftGbc.insets = new Insets(2, panelInsets.left, 2, 10);
leftGbc.gridwidth = 2;
leftGbc.fill = GridBagConstraints.HORIZONTAL;
leftGbc.anchor = GridBagConstraints.LINE_START;
GridBagConstraints middleGbc = new GridBagConstraints();
middleGbc.insets = new Insets(2, 5, 2, 5);
middleGbc.gridwidth = 1;
middleGbc.fill = GridBagConstraints.HORIZONTAL;
middleGbc.anchor = GridBagConstraints.LINE_END;
//middleGbc.weightx = 1.0f;
GridBagConstraints rightGbc = new GridBagConstraints();
rightGbc.insets = new Insets(2, 10, 2, panelInsets.right);
rightGbc.gridwidth = GridBagConstraints.REMAINDER;
rightGbc.fill = GridBagConstraints.HORIZONTAL;
GridBagConstraints fullLineGbc = new GridBagConstraints();
fullLineGbc.insets = new Insets(2, panelInsets.left, 2, panelInsets.right);
fullLineGbc.gridwidth = GridBagConstraints.REMAINDER;
fullLineGbc.fill = GridBagConstraints.HORIZONTAL;
JLabel label = new JLabel(LanguageBundle.getString("in_igValueLabel")); //$NON-NLS-1$
panel.add(label, leftGbc);
wealthLabel.setEditable(false);
wealthLabel.setColumns(10);
wealthLabel.setHorizontalAlignment(SwingConstants.RIGHT);
panel.add(wealthLabel, middleGbc);
panel.add(createCurrencyLabel(), rightGbc);
label = new JLabel(LanguageBundle.getString("in_igFundsLabel")); //$NON-NLS-1$
panel.add(label, leftGbc);
goldField.setHorizontalAlignment(SwingConstants.RIGHT);
goldField.setColumns(10);
goldField.setMinimumSize(new Dimension(50, goldField.getPreferredSize().height));
panel.add(goldField, middleGbc);
panel.add(createCurrencyLabel(), rightGbc);
label = new JLabel(LanguageBundle.getString("in_igAddSubFundsLabel")); //$NON-NLS-1$
panel.add(label, fullLineGbc);
JPanel expmodPanel = new JPanel();
{
GridBagConstraints gbc2 = new GridBagConstraints();
gbc2.fill = GridBagConstraints.HORIZONTAL;
gbc2.weightx = 1.0;
gbc2.insets = new Insets(0, 1, 0, 1);
expmodPanel.add(fundsAddButton, gbc2);
expmodPanel.add(fundsSubtractButton, gbc2);
}
panel.add(expmodPanel, leftGbc);
goldModField.setHorizontalAlignment(SwingConstants.RIGHT);
panel.add(goldModField, middleGbc);
panel.add(createCurrencyLabel(), rightGbc);
label = new JLabel(LanguageBundle.getString("in_igBuySellRateLabel")); //$NON-NLS-1$
fullLineGbc.insets = new Insets(10, 2, 2, 2);
panel.add(label, fullLineGbc);
buySellRateBox.setPrototypeDisplayValue("QuiteLongPrototypeDisplayValue"); //$NON-NLS-1$
fullLineGbc.insets = new Insets(2, 2, 2, 2);
panel.add(buySellRateBox, fullLineGbc);
panel.add(allowDebt, fullLineGbc);
fullLineGbc.weighty = 1.0f;
panel.add(new JLabel(), fullLineGbc);
}
/**
* Create a new label with the currency abbreviation.
*
* @return A new label
*/
private JLabel createCurrencyLabel()
{
JLabel label;
label = new JLabel(""); //$NON-NLS-1$
currencyLabels.add(label);
return label;
}
@Override
public ModelMap createModels(final CharacterFacade character)
{
ModelMap models = new ModelMap();
models.put(AvailableTreeViewModel.class, new AvailableTreeViewModel(character));
models.put(PurchasedTreeViewModel.class, new PurchasedTreeViewModel(character));
models.put(UseAutoResizeAction.class, new UseAutoResizeAction(character));
models.put(AddCustomAction.class, new AddCustomAction(character));
models.put(AddAction.class, new AddAction(character));
models.put(RemoveAction.class, new RemoveAction(character));
models.put(DeleteCustomAction.class, new DeleteCustomAction(character));
models.put(FundsAddAction.class, new FundsAddAction(character));
models.put(FundsSubtractAction.class, new FundsSubtractAction(character));
models.put(AllowDebtAction.class, new AllowDebtAction(character));
models.put(EquipInfoHandler.class, new EquipInfoHandler(character));
models.put(Handler.class, equipmentRenderer.createHandler(character));
models.put(EquipmentFilterHandler.class, new EquipmentFilterHandler(character));
models.put(EquipmentTransferHandler.class, new EquipmentTransferHandler(character));
models.put(CurrencyLabelHandler.class, new CurrencyLabelHandler(character));
models.put(BuyPopupMenuHandler.class, new BuyPopupMenuHandler(character));
models.put(SellPopupMenuHandler.class, new SellPopupMenuHandler(character));
models.put(CurrencyFieldHandler.class, new CurrencyFieldHandler(character));
CharacterComboBoxModel<GearBuySellFacade> buySellModel = new CharacterComboBoxModel<GearBuySellFacade>(character.getDataSet().getGearBuySellSchemes(), character.getGearBuySellRef())
{
@Override
public void setSelectedItem(Object anItem)
{
character.setGearBuySellRef((GearBuySellFacade) anItem);
}
};
models.put(CharacterComboBoxModel.class, buySellModel);
return models;
}
@Override
public void restoreModels(ModelMap models)
{
models.get(EquipmentFilterHandler.class).install();
models.get(Handler.class).install();
models.get(AvailableTreeViewModel.class).install();
purchasedTable.setTreeViewModel(models.get(PurchasedTreeViewModel.class));
autoResizeBox.setAction(models.get(UseAutoResizeAction.class));
addCustomButton.setAction(models.get(AddCustomAction.class));
addEquipmentButton.setAction(models.get(AddAction.class));
removeEquipmentButton.setAction(models.get(RemoveAction.class));
fundsAddButton.setAction(models.get(FundsAddAction.class));
fundsSubtractButton.setAction(models.get(FundsSubtractAction.class));
buySellRateBox.setModel(models.get(CharacterComboBoxModel.class));
allowDebt.setAction(models.get(AllowDebtAction.class));
models.get(EquipInfoHandler.class).install();
models.get(EquipmentTransferHandler.class).install();
models.get(UseAutoResizeAction.class).install();
models.get(AddAction.class).install();
models.get(RemoveAction.class).install();
models.get(CurrencyFieldHandler.class).install();
models.get(CurrencyLabelHandler.class).install();
models.get(BuyPopupMenuHandler.class).install();
models.get(SellPopupMenuHandler.class).install();
models.get(AllowDebtAction.class).install();;
}
@Override
public void storeModels(ModelMap models)
{
// models.get(AvailableTreeViewModel.class).uninstall();
models.get(EquipInfoHandler.class).uninstall();
models.get(AddAction.class).uninstall();
models.get(RemoveAction.class).uninstall();
models.get(CurrencyFieldHandler.class).uninstall();
models.get(BuyPopupMenuHandler.class).uninstall();
models.get(SellPopupMenuHandler.class).uninstall();
models.get(Handler.class).uninstall();
}
@Override
public TabTitle getTabTitle()
{
return new TabTitle(Tab.PURCHASE);
}
private List<EquipmentFacade> getMenuTargets(JTable table, MouseEvent e)
{
int row = table.rowAtPoint(e.getPoint());
if (!table.isRowSelected(row))
{
if ((0 <= row) && (table.getRowCount() > row))
{
table.setRowSelectionInterval(row, row);
}
}
List<EquipmentFacade> targets = new ArrayList<>();
for (int selRow : table.getSelectedRows())
{
Object value = table.getModel().getValueAt(selRow, 0);
if (value instanceof EquipmentFacade)
{
targets.add((EquipmentFacade) value);
}
}
return targets;
}
private class CurrencyFieldHandler
{
private final BigDecimalFieldHandler fundsHandler;
private final BigDecimalFieldHandler wealthHandler;
public CurrencyFieldHandler(final CharacterFacade character)
{
/**
* Handler for the Current Funds field. This listens for and
* processes both changes to the value from the character and
* modifications to the field made by the user.
*/
fundsHandler = new BigDecimalFieldHandler(goldField, character.getFundsRef())
{
@Override
protected void valueChanged(BigDecimal value)
{
character.setFunds(value);
availableTable.refilter();
}
};
/**
* Handler for theTotal Wealth field. This listens for and processes
* changes to the value from the character.
*/
wealthHandler = new BigDecimalFieldHandler(wealthLabel, character.getWealthRef())
{
@Override
protected void valueChanged(BigDecimal value)
{
// Ignored for this read-only field
}
};
}
public void install()
{
fundsHandler.install();
wealthHandler.install();
}
public void uninstall()
{
fundsHandler.uninstall();
wealthHandler.uninstall();
}
}
private class AddAction extends AbstractAction
{
private CharacterFacade character;
public AddAction(CharacterFacade character)
{
super(LanguageBundle.getString("in_ieAddEq")); //$NON-NLS-1$
putValue(SMALL_ICON, Icons.Forward16.getImageIcon());
this.character = character;
}
@Override
public void actionPerformed(ActionEvent e)
{
List<?> data = availableTable.getSelectedData();
if (data != null)
{
for (Object object : data)
{
if (object instanceof EquipmentFacade)
{
EquipmentFacade equip = (EquipmentFacade) object;
if (character.isAutoResize())
{
equip = character.getEquipmentSizedForCharacter(equip);
}
character.addPurchasedEquipment(equip, 1, false, false);
}
}
availableTable.refilter();
}
}
public void install()
{
availableTable.addActionListener(this);
}
public void uninstall()
{
availableTable.removeActionListener(this);
}
}
private class AddCustomAction extends AbstractAction
{
private CharacterFacade character;
public AddCustomAction(CharacterFacade character)
{
super(LanguageBundle.getString("in_igAddCustom")); //$NON-NLS-1$
putValue(SMALL_ICON, Icons.Forward16.getImageIcon());
this.character = character;
}
@Override
public void actionPerformed(ActionEvent e)
{
List<?> data = availableTable.getSelectedData();
if (data != null)
{
for (Object object : data)
{
if (object instanceof EquipmentFacade)
{
EquipmentFacade equip = (EquipmentFacade) object;
if (character.isAutoResize())
{
equip = character.getEquipmentSizedForCharacter(equip);
}
character.addPurchasedEquipment(equip, 1, true, false);
}
}
}
}
}
/**
* The Class {@code DeleteCustomAction} defines an action to delete a
* custom equipment item.
*
* @author James Dempsey <jdempsey@users.sourceforge.net>
*/
private class DeleteCustomAction extends AbstractAction
{
private CharacterFacade character;
public DeleteCustomAction(CharacterFacade character)
{
super(LanguageBundle.getString("in_igDeleteCustom")); //$NON-NLS-1$
this.character = character;
}
@Override
public void actionPerformed(ActionEvent e)
{
List<?> data = availableTable.getSelectedData();
if (data != null)
{
for (Object object : data)
{
if (object instanceof EquipmentFacade)
{
EquipmentFacade equip = (EquipmentFacade) object;
character.deleteCustomEquipment(equip);
}
}
}
}
}
private class UseAutoResizeAction extends AbstractAction
{
private CharacterFacade character;
public UseAutoResizeAction(CharacterFacade character)
{
super(LanguageBundle.getString("in_igAutoResize")); //$NON-NLS-1$
this.character = character;
}
@Override
public void actionPerformed(ActionEvent e)
{
character.setAutoResize(autoResizeBox.isSelected());
}
public void install()
{
autoResizeBox.setSelected(character.isAutoResize());
}
}
private class RemoveAction extends AbstractAction
{
private CharacterFacade character;
public RemoveAction(CharacterFacade character)
{
super(LanguageBundle.getString("in_ieRemEq")); //$NON-NLS-1$
putValue(SMALL_ICON, Icons.Back16.getImageIcon());
this.character = character;
}
@Override
public void actionPerformed(ActionEvent e)
{
List<?> data = purchasedTable.getSelectedData();
if (data != null)
{
for (Object object : data)
{
character.removePurchasedEquipment((EquipmentFacade) object, 1, false);
}
availableTable.refilter();
}
}
public void install()
{
purchasedTable.addActionListener(this);
}
public void uninstall()
{
purchasedTable.removeActionListener(this);
}
}
/**
* Handler for actions from the add funds button. Also defines the
* appearance of the button.
*/
private class FundsAddAction extends AbstractAction
{
private CharacterFacade character;
public FundsAddAction(CharacterFacade character)
{
this.character = character;
putValue(SMALL_ICON, new SignIcon(Sign.Plus));
}
@Override
public void actionPerformed(ActionEvent e)
{
Object value = goldModField.getValue();
if (value == null)
{
return;
}
BigDecimal modVal = new BigDecimal(((Number) value).doubleValue());
character.adjustFunds(modVal);
}
}
/**
* Handler for actions from the subtract funds button. Also defines the
* appearance of the button.
*/
private class FundsSubtractAction extends AbstractAction
{
private CharacterFacade character;
public FundsSubtractAction(CharacterFacade character)
{
this.character = character;
putValue(SMALL_ICON, new SignIcon(Sign.Minus));
}
@Override
public void actionPerformed(ActionEvent e)
{
Object value = goldModField.getValue();
if (value == null)
{
return;
}
BigDecimal modVal = new BigDecimal(((Number) value).doubleValue() * -1);
character.adjustFunds(modVal);
}
}
/**
* The Class {@code AllowDebtAction} links the allow debt checkbox to a
* character.
*/
private class AllowDebtAction extends AbstractAction
{
private CharacterFacade character;
public AllowDebtAction(CharacterFacade character)
{
super(LanguageBundle.getString("in_igAllowDebt")); //$NON-NLS-1$
this.character = character;
}
@Override
public void actionPerformed(ActionEvent e)
{
character.setAllowDebt(allowDebt.isSelected());
}
public void install()
{
allowDebt.setSelected(character.isAllowDebt());
}
}
private class EquipInfoHandler implements ListSelectionListener
{
private CharacterFacade character;
private String text;
private List<EquipmentFacade> oldList;
public EquipInfoHandler(CharacterFacade character)
{
this.character = character;
this.text = ""; //$NON-NLS-1$
oldList = null;
}
public void install()
{
availableTable.getSelectionModel().addListSelectionListener(this);
purchasedTable.getSelectionModel().addListSelectionListener(this);
infoPane.setText(text);
}
public void uninstall()
{
availableTable.getSelectionModel().removeListSelectionListener(this);
purchasedTable.getSelectionModel().removeListSelectionListener(this);
}
@Override
public void valueChanged(ListSelectionEvent e)
{
JTable target = availableTable;
if (purchasedTable.getSelectionModel().equals(e.getSource()))
{
target = purchasedTable;
}
if (!e.getValueIsAdjusting())
{
int selectedRows[] = target.getSelectedRows();
List<EquipmentFacade> newList = new ArrayList<>(selectedRows.length);
for (int row : selectedRows)
{
if (row != -1)
{
Object value = target.getModel().getValueAt(row, 0);
if (value instanceof EquipmentFacade)
{
newList.add((EquipmentFacade) value);
}
}
}
if (newList.isEmpty() || newList.equals(oldList))
{
return;
}
oldList = newList;
StringBuilder sb = new StringBuilder(2000);
for (EquipmentFacade equip : newList)
{
sb.append(character.getInfoFactory().getHTMLInfo(equip));
}
text = "<html>" + sb.toString() + "</html>"; //$NON-NLS-1$ //$NON-NLS-2$
infoPane.setText(text);
}
}
}
/**
* The Class {@code CurrencyLabelHandler} manages the currently
* displayed currency.
*/
private class CurrencyLabelHandler
{
private CharacterFacade character;
public CurrencyLabelHandler(CharacterFacade character)
{
this.character = character;
}
public void install()
{
String currencyDisplay
= character.getDataSet().getGameMode().getCurrencyDisplay();
for (JLabel label : currencyLabels)
{
label.setText(currencyDisplay);
}
}
}
private class AvailableTreeViewModel
extends CachedDataView<EquipmentFacade>
implements TreeViewModel<EquipmentFacade>, DataView<EquipmentFacade>
{
private final ListFacade<? extends TreeView<EquipmentFacade>> treeviews
= new DefaultListFacade<TreeView<EquipmentFacade>>(Arrays.asList(EquipmentTreeView.values()));
private final List<DefaultDataViewColumn> columns
= Arrays.asList(new DefaultDataViewColumn("in_igEqModelColCost", Float.class, true), //$NON-NLS-1$
new DefaultDataViewColumn("in_igEqModelColWeight", Float.class, true), //$NON-NLS-1$
new DefaultDataViewColumn("in_descrip", String.class, false), //$NON-NLS-1$
new DefaultDataViewColumn("in_igEqModelColSource", String.class, false)); //$NON-NLS-1$
private final CharacterFacade character;
private final ListFacade<EquipmentFacade> equipmentList;
public AvailableTreeViewModel(CharacterFacade character)
{
this.character = character;
this.equipmentList = character.getDataSet().getEquipment();
if (primaryTypes.isEmpty())
{
for (int i = 0; i < equipmentList.getSize(); i++)
{
EquipmentFacade eq = equipmentList.getElementAt(i);
List<String> types = eq.getTypesForDisplay();
if (!types.isEmpty())
{
primaryTypes.add(types.get(0));
}
}
}
}
@Override
public ListFacade<? extends TreeView<EquipmentFacade>> getTreeViews()
{
return treeviews;
}
@Override
public int getDefaultTreeViewIndex()
{
return 2;
}
@Override
public DataView<EquipmentFacade> getDataView()
{
return this;
}
@Override
public ListFacade<EquipmentFacade> getDataModel()
{
return equipmentList;
}
@Override
public List<? extends DataViewColumn> getDataColumns()
{
return columns;
}
@Override
public String getPrefsKey()
{
return "PurchaseAvail"; //$NON-NLS-1$
}
@Override
public Object getDataInternal(EquipmentFacade obj, int column)
{
switch(column){
case 0:
return character.getInfoFactory().getCost(obj);
case 1:
return character.getInfoFactory().getWeight(obj);
case 2:
return character.getInfoFactory().getDescription(obj);
case 3:
return obj.getSource();
default:
return null;
}
}
@Override
public void setData(Object value, EquipmentFacade element, int column)
{
}
// @Override
// protected void refreshTableData()
// {
// availableTable.refreshModelData();
// }
// @Override
public void install()
{
// super.install();
availableTable.setTreeViewModel(this);
}
}
private class PurchasedTreeViewModel
implements TreeViewModel<EquipmentFacade>, DataView<EquipmentFacade>, EquipmentListListener
{
private final ListFacade<? extends TreeView<EquipmentFacade>> treeviews
= new DefaultListFacade<TreeView<EquipmentFacade>>(Arrays.asList(EquipmentTreeView.values()));
private final List<DefaultDataViewColumn> columns
= Arrays.asList(new DefaultDataViewColumn("in_igEqModelColCost", Float.class, true), //$NON-NLS-1$
new DefaultDataViewColumn("in_igEqModelColWeight", Float.class, false), //$NON-NLS-1$
new DefaultDataViewColumn("in_igEqModelColQty", Integer.class, true), //$NON-NLS-1$
new DefaultDataViewColumn("in_descrip", String.class, false)); //$NON-NLS-1$
private final CharacterFacade character;
private final EquipmentListFacade equipmentList;
public PurchasedTreeViewModel(CharacterFacade character)
{
this.character = character;
this.equipmentList = character.getPurchasedEquipment();
this.equipmentList.addEquipmentListListener(this);
}
@Override
public ListFacade<? extends TreeView<EquipmentFacade>> getTreeViews()
{
return treeviews;
}
@Override
public int getDefaultTreeViewIndex()
{
return 0;
}
@Override
public DataView<EquipmentFacade> getDataView()
{
return this;
}
@Override
public ListFacade<EquipmentFacade> getDataModel()
{
return equipmentList;
}
@Override
public Object getData(EquipmentFacade obj, int column)
{
switch (column)
{
case 0:
return character.getInfoFactory().getCost(obj);
case 1:
return character.getInfoFactory().getWeight(obj);
case 2:
return equipmentList.getQuantity(obj);
case 3:
return character.getInfoFactory().getDescription(obj);
default:
return null;
}
}
@Override
public void setData(Object value, EquipmentFacade element, int column)
{
}
@Override
public List<? extends DataViewColumn> getDataColumns()
{
return columns;
}
@Override
public void quantityChanged(EquipmentListEvent equipment)
{
purchasedTable.refreshModelData();
}
@Override
public String getPrefsKey()
{
return "Purchased"; //$NON-NLS-1$
}
}
private enum EquipmentTreeView implements TreeView<EquipmentFacade>
{
NAME(LanguageBundle.getString("in_nameLabel")), //$NON-NLS-1$
TYPE_NAME(LanguageBundle.getString("in_typeName")), //$NON-NLS-1$
TYPE_SUBTYPE_NAME(LanguageBundle.getString("in_typeSubtypeName")), //$NON-NLS-1$
SOURCE_NAME(LanguageBundle.getString("in_sourceName")); //$NON-NLS-1$
//SOURCE_NAME("Source/Name");
private final String name;
private EquipmentTreeView(String name)
{
this.name = name;
}
@Override
public String getViewName()
{
return name;
}
@Override
public List<TreeViewPath<EquipmentFacade>> getPaths(EquipmentFacade pobj)
{
switch (this)
{
case TYPE_SUBTYPE_NAME:
List<String> types = pobj.getTypesForDisplay();
if (types != null && types.size() > 1)
{
List<TreeViewPath<EquipmentFacade>> paths
= new ArrayList<>();
for (String type : types)
{
if (primaryTypes.contains(type))
{
for (String subType : types)
{
if (!type.equals(subType))
{
paths.add(new TreeViewPath<>(
pobj, type, subType));
}
}
}
}
return paths;
}
// Less then two types, fall through to treat it as a type tree.
case TYPE_NAME:
types = pobj.getTypesForDisplay();
if (types != null && !types.isEmpty())
{
List<TreeViewPath<EquipmentFacade>> paths = new ArrayList<>(
types.size());
for (String type : types)
{
if (primaryTypes.contains(type))
{
paths.add(new TreeViewPath<>(pobj, type));
}
}
return paths;
}
// No types, fall through and treat it as just a name.
case NAME:
return Collections.singletonList(new TreeViewPath<>(pobj));
case SOURCE_NAME:
return Collections.singletonList(new TreeViewPath<>(pobj,
pobj.getSourceForNodeDisplay()));
default:
throw new InternalError();
}
}
}
/**
* The Class {@code EquipmentRenderer} displays the tree cells of the
* available and purchased equipment tables.
*/
private class EquipmentRenderer 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 equipObj = ((DefaultMutableTreeNode) value).getUserObject();
if (equipObj instanceof EquipmentFacade
&& !character.isQualifiedFor((EquipmentFacade) equipObj))
{
setForeground(UIPropertyContext.getNotQualifiedColor());
}
return this;
}
}
private class EquipmentFilterHandler
{
private final CharacterFacade character;
public EquipmentFilterHandler(CharacterFacade character)
{
this.character = character;
}
public void install()
{
availableTable.setContext(character);
}
}
private class PurchaseTransferHandler extends TransferHandler
{
private CharacterFacade character;
public PurchaseTransferHandler(CharacterFacade character)
{
this.character = character;
}
@Override
public int getSourceActions(JComponent c)
{
return MOVE;
}
@Override
protected Transferable createTransferable(JComponent c)
{
List<?> data = purchasedTable.getSelectedData();
if (data == null)
{
return null;
}
Iterator<?> it = data.iterator();
while (it.hasNext())
{
if (!(it.next() instanceof EquipmentFacade))
{
it.remove();
}
}
if (data.isEmpty())
{
return null;
}
EquipmentFacade[] equipArray = data.toArray(new EquipmentFacade[0]);
return new EquipmentSelection(equipArray);
}
@Override
public boolean canImport(TransferSupport support)
{
if (!support.isDataFlavorSupported(equipmentArrayFlavor))
{
return false;
}
support.setShowDropLocation(false);
return true;
}
}
private class EquipmentTransferHandler extends TransferHandler
{
private CharacterFacade character;
public EquipmentTransferHandler(CharacterFacade character)
{
this.character = character;
}
public void install()
{
availableTable.setDragEnabled(true);
availableTable.setDropMode(DropMode.ON);
availableTable.setTransferHandler(this);
purchasedTable.setDragEnabled(true);
purchasedTable.setDropMode(DropMode.ON);
purchasedTable.setTransferHandler(this);
}
@Override
public int getSourceActions(JComponent c)
{
return MOVE;
}
@Override
protected Transferable createTransferable(JComponent c)
{
List<?> data = null;
if (c == availableTable)
{
data = availableTable.getSelectedData();
}
else if (c == purchasedTable)
{
data = purchasedTable.getSelectedData();
}
if (data == null)
{
return null;
}
Iterator<?> it = data.iterator();
while (it.hasNext())
{
if (!(it.next() instanceof EquipmentFacade))
{
it.remove();
}
}
if (data.isEmpty())
{
return null;
}
EquipmentFacade[] equipArray = data.toArray(new EquipmentFacade[0]);
return new EquipmentSelection(equipArray);
}
@Override
public boolean canImport(TransferSupport support)
{
if (!support.isDataFlavorSupported(equipmentArrayFlavor))
{
return false;
}
support.setShowDropLocation(false);
return true;
}
private EquipmentFacade[] getEquipmentArray(TransferSupport support)
{
EquipmentFacade[] equipmentArray = null;
try
{
equipmentArray = (EquipmentFacade[]) support.getTransferable().getTransferData(equipmentArrayFlavor);
}
catch (UnsupportedFlavorException | IOException ex)
{
}
return equipmentArray;
}
@Override
public boolean importData(TransferSupport support)
{
if (!canImport(support))
{
return false;
}
if (!support.isDrop())
{
return false;
}
EquipmentFacade[] equipmentArray = getEquipmentArray(support);
if (support.getComponent() == availableTable)
{
for (EquipmentFacade equipmentFacade : equipmentArray)
{
character.removePurchasedEquipment(equipmentFacade, 1, false);
}
}
else if (support.getComponent() == purchasedTable)
{
for (EquipmentFacade equipmentFacade : equipmentArray)
{
EquipmentFacade equip = character.getEquipmentSizedForCharacter(equipmentFacade);
character.addPurchasedEquipment(equip, 1, false, false);
}
}
availableTable.refilter();
return true;
}
}
private class BuyPopupMenuHandler extends PopupMouseAdapter
{
private final CharacterFacade character;
BuyPopupMenuHandler(CharacterFacade character)
{
this.character = character;
}
@Override
public void showPopup(MouseEvent e)
{
List<EquipmentFacade> targets = getMenuTargets(availableTable, e);
if (targets.isEmpty())
{
return;
}
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new BuyNumMenuItem(character, targets, 1));
JMenu buyMenu = new JMenu(LanguageBundle.getString("in_igBuyQuantity")); //$NON-NLS-1$
buyMenu.add(new BuyNumMenuItem(character, targets, 2));
buyMenu.add(new BuyNumMenuItem(character, targets, 5));
buyMenu.add(new BuyNumMenuItem(character, targets, 10));
buyMenu.add(new BuyNumMenuItem(character, targets, 15));
buyMenu.add(new BuyNumMenuItem(character, targets, 20));
buyMenu.add(new BuyNumMenuItem(character, targets, 50));
popupMenu.add(buyMenu);
popupMenu.add(new BuyNumMenuItem(character, targets, 0));
popupMenu.addSeparator();
popupMenu.add(new AddCustomAction(character));
popupMenu.add(new DeleteCustomAction(character));
popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
public void install()
{
availableTable.addMouseListener(this);
}
public void uninstall()
{
availableTable.removeMouseListener(this);
}
}
/**
* Menu item for buying a quanity of an item at the current rate.
*/
private class BuyNumMenuItem extends JMenuItem implements ActionListener
{
private final int quantity;
private final CharacterFacade character;
private final List<EquipmentFacade> targets;
BuyNumMenuItem(CharacterFacade character, List<EquipmentFacade> targets, int quantity)
{
super(LanguageBundle.getFormattedString(quantity > 0
? "in_igBuyMenuCommand" : "in_igBuyN", quantity)); //$NON-NLS-1$ //$NON-NLS-2$
this.character = character;
this.targets = targets;
this.quantity = quantity;
if (quantity > 0)
{
setToolTipText(LanguageBundle.getFormattedString(
"in_igBuyMenuDesc", quantity)); //$NON-NLS-1$
}
else
{
setToolTipText(LanguageBundle.getString("in_igBuyNMenuDesc")); //$NON-NLS-1$
}
// setMnemonic(LanguageBundle.getMnemonic("in_mn_center"));
setIcon(Icons.Add16.getImageIcon());
addActionListener(this);
}
/**
* Action to buy a certain number of items.
*/
@Override
public void actionPerformed(ActionEvent e)
{
int num = quantity;
if (num == 0)
{
String selectedValue
= JOptionPane.showInputDialog(null, LanguageBundle.getString("in_igBuyEnterQuantity"), //$NON-NLS-1$
Constants.APPLICATION_NAME, JOptionPane.QUESTION_MESSAGE);
if (selectedValue != null)
{
try
{
num = (int) Float.parseFloat(selectedValue);
}
catch (NumberFormatException ex)
{
//Ignored
}
}
}
if (num > 0)
{
for (EquipmentFacade equip : targets)
{
if (character.isAutoResize())
{
equip = character.getEquipmentSizedForCharacter(equip);
}
character.addPurchasedEquipment(equip, num, false, false);
availableTable.refilter();
}
}
}
}
private class SellPopupMenuHandler extends PopupMouseAdapter
{
private final CharacterFacade character;
SellPopupMenuHandler(CharacterFacade character)
{
this.character = character;
}
@Override
public void showPopup(MouseEvent e)
{
List<EquipmentFacade> targets = getMenuTargets(purchasedTable, e);
if (targets.isEmpty())
{
return;
}
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new SellNumMenuItem(character, targets, 1));
JMenu sellMenu = new JMenu(LanguageBundle.getString("in_igSellQuantity")); //$NON-NLS-1$
sellMenu.add(new SellNumMenuItem(character, targets, 2));
sellMenu.add(new SellNumMenuItem(character, targets, 5));
sellMenu.add(new SellNumMenuItem(character, targets, 10));
sellMenu.add(new SellNumMenuItem(character, targets, 15));
sellMenu.add(new SellNumMenuItem(character, targets, 20));
sellMenu.add(new SellNumMenuItem(character, targets, 50));
popupMenu.add(sellMenu);
popupMenu.add(new SellNumMenuItem(character, targets, 0));
popupMenu.add(new SellNumMenuItem(character, targets, SellNumMenuItem.SELL_ALL_QUANTITY));
popupMenu.addSeparator();
JMenu moveMenu = new JMenu(LanguageBundle.getString("in_igMoveItemMenuTitle")); //$NON-NLS-1$
moveMenu.setEnabled(false);
for (CharacterFacade dest : CharacterManager.getCharacters())
{
if (dest != character)
{
moveMenu.add(new MoveItemMenuItem(character, dest, targets));
moveMenu.setEnabled(true);
}
}
popupMenu.add(moveMenu);
JMenu copyMenu = new JMenu(LanguageBundle.getString("in_igCopyItemMenuTitle")); //$NON-NLS-1$
copyMenu.setEnabled(false);
for (CharacterFacade dest : CharacterManager.getCharacters())
{
if (dest != character)
{
copyMenu.add(new CopyItemMenuItem(character, dest, targets));
copyMenu.setEnabled(true);
}
}
popupMenu.add(copyMenu);
popupMenu.addSeparator();
popupMenu.add(new ModifyChargesMenuItem(character, targets));
popupMenu.add(new AddNoteMenuItem(character, targets));
popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
public void install()
{
purchasedTable.addMouseListener(this);
}
public void uninstall()
{
purchasedTable.removeMouseListener(this);
}
}
/**
* Menu item for selling a quantity of an item at the current rate.
*/
private class SellNumMenuItem extends JMenuItem implements ActionListener
{
public static final int SELL_ALL_QUANTITY = -5;
private final int quantity;
private final CharacterFacade character;
private final List<EquipmentFacade> targets;
SellNumMenuItem(CharacterFacade character, List<EquipmentFacade> targets, int quantity)
{
super(LanguageBundle.getFormattedString(quantity > 0
? "in_igSellMenuCommand" : (quantity == SELL_ALL_QUANTITY //$NON-NLS-1$
? "in_igSellAll" : "in_igSellN"), quantity)); //$NON-NLS-1$ //$NON-NLS-2$
this.character = character;
this.targets = targets;
this.quantity = quantity;
if (quantity > 0)
{
setToolTipText(LanguageBundle.getFormattedString(
"in_igSellMenuDesc", quantity)); //$NON-NLS-1$
}
else if (quantity == SELL_ALL_QUANTITY)
{
setToolTipText(LanguageBundle.getString("in_igSellAllMenuDesc")); //$NON-NLS-1$
}
else
{
setToolTipText(LanguageBundle.getString("in_igSellNMenuDesc")); //$NON-NLS-1$
}
// setMnemonic(LanguageBundle.getMnemonic("in_mn_center"));
setIcon(Icons.Remove16.getImageIcon());
addActionListener(this);
}
/**
* Action to buy a certain number of items.
*/
@Override
public void actionPerformed(ActionEvent e)
{
boolean sellAll = quantity == SELL_ALL_QUANTITY;
int num = quantity;
if (num == 0)
{
String selectedValue
= JOptionPane.showInputDialog(null, LanguageBundle.getString("in_igBuyEnterQuantity"), //$NON-NLS-1$
Constants.APPLICATION_NAME, JOptionPane.QUESTION_MESSAGE);
if (selectedValue != null)
{
try
{
num = (int) Float.parseFloat(selectedValue);
}
catch (NumberFormatException ex)
{
//Ignored
}
}
}
if (num > 0 || sellAll)
{
for (EquipmentFacade equip : targets)
{
if (sellAll)
{
num = character.getPurchasedEquipment().getQuantity(equip);
}
character.removePurchasedEquipment(equip, num, false);
}
availableTable.refilter();
}
}
}
private class ModifyChargesMenuItem extends JMenuItem implements ActionListener
{
private final CharacterFacade character;
private final List<EquipmentFacade> targets;
ModifyChargesMenuItem(CharacterFacade character, List<EquipmentFacade> targets)
{
super(LanguageBundle.getString("in_igModifyCharges")); //$NON-NLS-1$
this.character = character;
this.targets = targets;
addActionListener(this);
}
/**
* Action to modify the number of charges on the items.
*/
@Override
public void actionPerformed(ActionEvent e)
{
character.modifyCharges(targets);
}
}
private class AddNoteMenuItem extends JMenuItem implements ActionListener
{
private final CharacterFacade character;
private final List<EquipmentFacade> targets;
AddNoteMenuItem(CharacterFacade character, List<EquipmentFacade> targets)
{
super(LanguageBundle.getString("in_igAddNote")); //$NON-NLS-1$
this.character = character;
this.targets = targets;
addActionListener(this);
}
/**
* Action to modify the number of charges on the items.
*/
@Override
public void actionPerformed(ActionEvent e)
{
character.addNote(targets);
}
}
private class MoveItemMenuItem extends JMenuItem implements ActionListener
{
private final CharacterFacade character;
private final CharacterFacade destination;
private final List<EquipmentFacade> targets;
MoveItemMenuItem(CharacterFacade character, CharacterFacade destination, List<EquipmentFacade> targets)
{
super(destination.getNameRef().get());
this.character = character;
this.destination = destination;
this.targets = targets;
addActionListener(this);
}
/**
* Action to move the items between characters.
*/
@Override
public void actionPerformed(ActionEvent e)
{
for (EquipmentFacade item : targets)
{
character.removePurchasedEquipment(item, 1, true);
destination.addPurchasedEquipment(item, 1, false, true);
}
}
}
private class CopyItemMenuItem extends JMenuItem implements ActionListener
{
private final CharacterFacade character;
private final CharacterFacade destination;
private final List<EquipmentFacade> targets;
CopyItemMenuItem(CharacterFacade character, CharacterFacade destination, List<EquipmentFacade> targets)
{
super(destination.getNameRef().get());
this.character = character;
this.destination = destination;
this.targets = targets;
addActionListener(this);
}
/**
* Action to copy the items to another character.
*/
@Override
public void actionPerformed(ActionEvent e)
{
for (EquipmentFacade item : targets)
{
destination.addPurchasedEquipment(item, 1, false, true);
}
}
}
}