/*
* Initiative - A role playing utility to track turns
* Copyright (C) 2004 Ross M. Lodge
*
* 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
*
* OpposedCheckDialog.java
*
* Created on May 4, 2004, 1:32:24 PM
*/
package plugin.initiative.gui;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.HeadlessException;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.TransferHandler;
import javax.swing.table.TableModel;
import gmgen.GMGenSystem;
import gmgen.plugin.InitHolder;
import gmgen.plugin.PcgCombatant;
import pcgen.core.Globals;
import pcgen.core.PObject;
import pcgen.core.Skill;
import plugin.initiative.OpposedSkillBasicModel;
import plugin.initiative.OpposedSkillModel;
import plugin.initiative.OpposedSkillTypeModel;
/**
* <p>
* This dialog class presents three lists of the {@code PcgCombatant}s:
* a list of available combatants, a list of combatants rolling a skill check,
* and a list of combatants opposing the skill check. A droplist of skills
* is presented for both lists. Because pcgen doesn't contain any information
* about which skills oppose which, no choices are automatically selected --
* the DM must select both skill names.
* </p>
* <p>
* When a skill is selected, or when the "roll" button is pressed, skill checks are
* rolled for both groups. Combatants without the skill roll if the skill can be used
* untrained, but their roll remains blank if the skill cannot be used untrained.
* No particular other processing is performed -- that is, the results are not interpreted
* or compared in any way, the DM must judge the results by eye.
* </p>
* <p>
* The rolling and opposing tables each have a "fudge" column where the user can enter
* a fudge value to affect the outcome of the roll.
* </p>
* <p>
* Combatants can be dragged from one table to another. The user must select the
* rows in the source table, release the mouse button, then click-and-drag the rows
* to a new table.
* </p>
* <ul>
* <li>TODO: Add comparison/re-sorting support to models?</li>
* <li>TODO: Add logging support to the initiative tracker log</li>
* </ul>
*
* @author LodgeR
*/
class OpposedCheckDialog extends JDialog
{
/**
* <p>
* A transfer handler to manage drag-and-drop between the tables.
* It interprets all drags as moves and won't allow drops on the initiating
* table. It is designed to be shared by all the tables.
* </p>
*/
private static final class CombatantTransferHandler extends TransferHandler
{
/**
* <p>
* A transferrable class that saves a list of combatants being transferred.
* </p>
*/
private final class CombatantTransferable implements Transferable
{
/**
* A list of combatants that are being transferred.
*/
private List<PcgCombatant> items = null;
/**
* <p>
* Constructor. The JTable us used to get the selected rows and store
* them in the {@code items} member.
* </p>
*
* @param table The source JTable
*/
private CombatantTransferable(JTable table)
{
int[] rows = table.getSelectedRows();
if ((rows != null) && (rows.length > 0))
{
OpposedSkillBasicModel model =
(OpposedSkillBasicModel) table.getModel();
items = new ArrayList<>();
Arrays.stream(rows).forEach(model::getCombatant);
}
}
/* (non-Javadoc)
* @see java.awt.datatransfer.Transferable#getTransferData(java.awt.datatransfer.DataFlavor)
*/
@Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException
{
if (!isDataFlavorSupported(flavor))
{
throw new UnsupportedFlavorException(flavor);
}
return items;
}
/* (non-Javadoc)
* @see java.awt.datatransfer.Transferable#getTransferDataFlavors()
*/
@Override
public DataFlavor[] getTransferDataFlavors()
{
return new DataFlavor[]{combatantFlavor};
}
/* (non-Javadoc)
* @see java.awt.datatransfer.Transferable#isDataFlavorSupported(java.awt.datatransfer.DataFlavor)
*/
@Override
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return combatantFlavor.equals(flavor);
}
}
/** A data flavor for use in the transfer */
private DataFlavor combatantFlavor = null;
/** The mime type used by the data flavor. Not really accurate, since
* the transferrable class really returns a List.
*/
private final String mimeType =
DataFlavor.javaJVMLocalObjectMimeType
+ ";class=gmgen.plugin.PcgCombatant";
/** The source data model for the transfer. */
private OpposedSkillBasicModel sourceModel = null;
/** The source table for the transfer. */
private JTable sourceTable = null;
/**
* <p>
* Default constructor -- initializes the data flavor.
* </p>
*/
private CombatantTransferHandler()
{
try
{
combatantFlavor = new DataFlavor(mimeType);
}
catch (final ClassNotFoundException e)
{
//Intentionally left blank
}
}
/* (non-Javadoc)
* @see javax.swing.TransferHandler#canImport(javax.swing.JComponent, java.awt.datatransfer.DataFlavor[])
*/
@Override
public boolean canImport(JComponent c, DataFlavor[] flavors)
{
if ((sourceTable == null) || (c == null)
|| sourceTable.getName()
.equals(c.getName()))
{
return false;
}
for (final DataFlavor flavor : flavors)
{
if (combatantFlavor.equals(flavor))
{
return true;
}
}
return false;
}
/* (non-Javadoc)
* @see javax.swing.TransferHandler#createTransferable(javax.swing.JComponent)
*/
@Override
protected Transferable createTransferable(JComponent c)
{
if (c instanceof JTable)
{
sourceModel = (OpposedSkillBasicModel) ((JTable) c).getModel();
sourceTable = (JTable) c;
return new CombatantTransferable((JTable) c);
}
return null;
}
/* (non-Javadoc)
* @see javax.swing.TransferHandler#exportDone(javax.swing.JComponent, java.awt.datatransfer.Transferable, int)
*/
@Override
protected void exportDone(JComponent c, Transferable data, int action)
{
if (action == TransferHandler.MOVE)
{
try
{
Iterable<PcgCombatant> items = (Iterable<PcgCombatant>) data.getTransferData(combatantFlavor);
items.forEach(item -> sourceModel.removeCombatant(item.getName()));
}
catch (final UnsupportedFlavorException | IOException e)
{
e.printStackTrace();
}
}
sourceModel = null;
}
/* (non-Javadoc)
* @see javax.swing.TransferHandler#getSourceActions(javax.swing.JComponent)
*/
@Override
public int getSourceActions(JComponent c)
{
return TransferHandler.MOVE;
}
/* (non-Javadoc)
* @see javax.swing.TransferHandler#importData(javax.swing.JComponent, java.awt.datatransfer.Transferable)
*/
@Override
public boolean importData(JComponent c, Transferable t)
{
if (canImport(c, t.getTransferDataFlavors()))
{
JTable table = (JTable) c;
OpposedSkillBasicModel model =
(OpposedSkillBasicModel) table.getModel();
try
{
Iterable<PcgCombatant> items = (Iterable<PcgCombatant>) t.getTransferData(combatantFlavor);
items.forEach(model::addCombatant);
return true;
}
catch (final UnsupportedFlavorException | IOException ufe)
{
//Nothing
}
}
return false;
}
}
/** The shared {@code TransferHandler} for all tables */
private final TransferHandler transferHandler =
new CombatantTransferHandler();
/** Label for the available table */
private JLabel availableLabel = null; //
/** Scroll pane for the available table */
private JScrollPane availableScrollPane = null; //
/** JTable that holds the available combatants */
private JTable availableTable = null; //
/** Data model for the available table */
private OpposedSkillTypeModel ivjAvailableModel = null;
/** Data model for the opposed table */
private OpposedSkillModel ivjOpposedSkillModel = null;
/** Data model for the rolling table */
private OpposedSkillModel ivjRollingSkillModel = null;
/** The main panel; functions as a content pane for the dialog. */
private JPanel jContentPane = null;
/** Ok button */
private JButton okButton = null; //
/** Combo box lising skills for the opposing group */
private JComboBox<Object> opposingComboBox = null; //
/** Label for the opposing group */
private JLabel opposingGroupLabel = null; //
/** Scroll pane for the opposing group table */
private JScrollPane opposingGroupScrollPane = null; //
/** Table for the opposing group of combatants */
private JTable opposingGroupTable = null; //
/** Button for rolling skill checks */
private JButton rollButton = null; //
/** Combo box listing skills for the rolling group */
private JComboBox<Object> rollingComboBox = null; //
/** Label for the rolling group */
private JLabel rollingGroupLabel = null; //
/** Scroll pane for rollingGroupTable */
private JScrollPane rollingGroupScrollPane = null; //
/** Table that holds the main (rolling) group of combatants */
private JTable rollingGroupTable = null; //
/** Sorted list of skill names */
private final TreeSet<String> skillNames = new TreeSet<>();
/**
* <p>Constructor</p>
* @param owner
* @param rollingGroup A list comprising the main (rolling) group of combatants
* @param availableGroup A list comprising the other combatants
* @throws HeadlessException if running without a gui
*/
OpposedCheckDialog(Frame owner, List<InitHolder> rollingGroup,
List<InitHolder> availableGroup) throws HeadlessException
{
super(owner);
initializeLists(rollingGroup, availableGroup);
initialize();
}
/**
*
* <p>
* Iniitializes the list of skills, and the main data models for the tables,
* based on the rolling and available groups.
* </p>
*
* @param rollingGroup A list comprising the main (rolling) group of combatants
* @param availableGroup A list comprising the other combatants
*/
private void initializeLists(List<InitHolder> rollingGroup, List<InitHolder> availableGroup)
{
skillNames.addAll(Globals.getContext()
.getReferenceContext()
.getConstructedCDOMObjects(Skill.class)
.stream()
.map(PObject::toString)
.collect(Collectors.toList()));
ivjAvailableModel = new OpposedSkillTypeModel(availableGroup);
ivjRollingSkillModel = new OpposedSkillModel(rollingGroup);
ivjOpposedSkillModel = new OpposedSkillModel();
}
/**
*
* This method initializes availableLabel
*
* @return javax.swing.JLabel
*
*/
private Component getAvailableLabel()
{
if (availableLabel == null)
{
availableLabel = new JLabel();
availableLabel.setText("Available Combatants");
}
return availableLabel;
}
/**
*
* This method initializes availableScrollPane
*
* @return javax.swing.JScrollPane
*
*/
private Component getAvailableScrollPane()
{
if (availableScrollPane == null)
{
availableScrollPane = new JScrollPane();
availableScrollPane.setViewportView(getAvailableTable());
availableScrollPane.setPreferredSize(new Dimension(300,
100));
}
return availableScrollPane;
}
/**
*
* This method initializes availableTable
*
* @return javax.swing.JTable
*
*/
private Component getAvailableTable()
{
if (availableTable == null)
{
availableTable = new JTable();
availableTable.setModel(getIvjAvailableModel());
availableTable.setDragEnabled(true);
availableTable.setTransferHandler(transferHandler);
availableTable.setName("availableTable");
}
return availableTable;
}
/**
*
* This method initializes ivjAvailableModel
*
* @return OpposedSkillAvailableModel
*
*/
private TableModel getIvjAvailableModel()
{
if (ivjAvailableModel == null)
{
ivjAvailableModel = new OpposedSkillTypeModel();
}
return ivjAvailableModel;
}
/**
*
* This method initializes ivjOpposedSkillModel
*
* @return OpposedSkillModel
*
*/
private TableModel getIvjOpposedSkillModel()
{
if (ivjOpposedSkillModel == null)
{
ivjOpposedSkillModel = new OpposedSkillModel();
}
return ivjOpposedSkillModel;
}
/**
*
* This method initializes ivjRollingSkillModel
*
* @return OpposedSkillModel
*
*/
private TableModel getIvjRollingSkillModel()
{
if (ivjRollingSkillModel == null)
{
ivjRollingSkillModel = new OpposedSkillModel();
}
return ivjRollingSkillModel;
}
/**
* This method initializes jContentPane
*
* @return javax.swing.JPanel
*/
private Container getJContentPane()
{
if (jContentPane == null)
{
jContentPane = new JPanel();
jContentPane.setLayout(new GridBagLayout());
GridBagConstraints consAvailableTable = new GridBagConstraints();
consAvailableTable.gridx = 0;
consAvailableTable.gridy = 1;
consAvailableTable.weightx = 1.0;
consAvailableTable.weighty = 1.0;
consAvailableTable.fill = GridBagConstraints.BOTH;
consAvailableTable.gridwidth = 2;
GridBagConstraints consRollingGroupPane = new GridBagConstraints();
consRollingGroupPane.weightx = 1.0;
consRollingGroupPane.weighty = 1.0;
consRollingGroupPane.fill = GridBagConstraints.BOTH;
consRollingGroupPane.gridx = 0;
consRollingGroupPane.gridy = 4;
GridBagConstraints consOpposingComboBox = new GridBagConstraints();
consOpposingComboBox.weightx = 1.0;
consOpposingComboBox.fill = GridBagConstraints.HORIZONTAL;
consOpposingComboBox.gridx = 1;
consOpposingComboBox.gridy = 3;
GridBagConstraints consOpposingGroupPane = new GridBagConstraints();
consOpposingGroupPane.weightx = 1.0;
consOpposingGroupPane.weighty = 1.0;
consOpposingGroupPane.fill = GridBagConstraints.BOTH;
consOpposingGroupPane.gridx = 1;
consOpposingGroupPane.gridy = 4;
GridBagConstraints consRollingComboBox = new GridBagConstraints();
consRollingComboBox.weightx = 1.0;
consRollingComboBox.fill = GridBagConstraints.HORIZONTAL;
consRollingComboBox.gridx = 0;
consRollingComboBox.gridy = 3;
GridBagConstraints consRollButton = new GridBagConstraints();
consRollButton.gridx = 0;
consRollButton.gridy = 5;
GridBagConstraints consOkButton = new GridBagConstraints();
consOkButton.gridx = 1;
consOkButton.gridy = 5;
jContentPane.setBorder(BorderFactory.createEmptyBorder(
5, 5, 5, 5));
GridBagConstraints consRollingGroupLabel = new GridBagConstraints();
consRollingGroupLabel.gridx = 0;
consRollingGroupLabel.gridy = 2;
GridBagConstraints consOpposingGroupLabel = new GridBagConstraints();
consOpposingGroupLabel.gridx = 1;
consOpposingGroupLabel.gridy = 2;
GridBagConstraints consAvailableLabel = new GridBagConstraints();
consAvailableLabel.gridx = 0;
consAvailableLabel.gridy = 0;
consAvailableLabel.gridwidth = 2;
jContentPane.add(getAvailableScrollPane(), consAvailableTable);
jContentPane.add(getRollingGroupScrollPane(), consRollingGroupPane);
jContentPane.add(getOpposingComboBox(), consOpposingComboBox);
jContentPane.add(getOpposingGroupScrollPane(),
consOpposingGroupPane);
jContentPane.add(getRollingComboBox(), consRollingComboBox);
jContentPane.add(getRollButton(), consRollButton);
jContentPane.add(getOkButton(), consOkButton);
jContentPane.add(getRollingGroupLabel(), consRollingGroupLabel);
jContentPane.add(getOpposingGroupLabel(), consOpposingGroupLabel);
jContentPane.add(getAvailableLabel(), consAvailableLabel);
}
return jContentPane;
}
/**
*
* This method initializes okButton
*
* @return javax.swing.JButton
*
*/
private Component getOkButton()
{
if (okButton == null)
{
okButton = new JButton();
okButton.setText("Ok");
okButton.addActionListener(this::okButtonActionPerformed);
}
return okButton;
}
/**
*
* This method initializes opposingComboBox
*
* @return javax.swing.JComboBox
*
*/
private Component getOpposingComboBox()
{
if (opposingComboBox == null)
{
opposingComboBox = new JComboBox<>(skillNames.toArray());
opposingComboBox.setSelectedIndex(-1);
opposingComboBox
.addActionListener(this::opposingComboBoxActionPerformed);
}
return opposingComboBox;
}
/**
*
* This method initializes opposingGroupLabel
*
* @return javax.swing.JLabel
*
*/
private Component getOpposingGroupLabel()
{
if (opposingGroupLabel == null)
{
opposingGroupLabel = new JLabel();
opposingGroupLabel.setText("Opposing Group");
}
return opposingGroupLabel;
}
/**
*
* This method initializes opposingGroupScrollPane
*
* @return javax.swing.JScrollPane
*
*/
private Component getOpposingGroupScrollPane()
{
if (opposingGroupScrollPane == null)
{
opposingGroupScrollPane = new JScrollPane();
opposingGroupScrollPane.setViewportView(getOpposingGroupTable());
opposingGroupScrollPane.setPreferredSize(new Dimension(
300, 100));
}
return opposingGroupScrollPane;
}
/**
*
* This method initializes opposingGroupTable
*
* @return javax.swing.JTable
*
*/
private Component getOpposingGroupTable()
{
if (opposingGroupTable == null)
{
opposingGroupTable = new JTable();
opposingGroupTable.setModel(getIvjOpposedSkillModel());
opposingGroupTable.setDragEnabled(true);
opposingGroupTable.setTransferHandler(transferHandler);
opposingGroupTable.setName("opposingGroupTable");
}
return opposingGroupTable;
}
/**
*
* This method initializes rollButton
*
* @return javax.swing.JButton
*
*/
private Component getRollButton()
{
if (rollButton == null)
{
rollButton = new JButton();
rollButton.setText("Roll");
rollButton.addActionListener(this::rollButtonActionPerformed);
}
return rollButton;
}
/**
*
* This method initializes rollingComboBox
*
* @return javax.swing.JComboBox
*
*/
private Component getRollingComboBox()
{
if (rollingComboBox == null)
{
rollingComboBox = new JComboBox(skillNames.toArray());
rollingComboBox.setSelectedIndex(-1);
rollingComboBox
.addActionListener(this::rollingComboBoxActionPerformed);
}
return rollingComboBox;
}
/**
*
* This method initializes rollingGroupLabel
*
* @return javax.swing.JLabel
*
*/
private Component getRollingGroupLabel()
{
if (rollingGroupLabel == null)
{
rollingGroupLabel = new JLabel();
rollingGroupLabel.setText("Rolling Group");
}
return rollingGroupLabel;
}
/**
*
* This method initializes rollingGroupScrollPane
*
* @return javax.swing.JScrollPane
*
*/
private Component getRollingGroupScrollPane()
{
if (rollingGroupScrollPane == null)
{
rollingGroupScrollPane = new JScrollPane();
rollingGroupScrollPane.setViewportView(getRollingGroupTable());
rollingGroupScrollPane.setPreferredSize(new Dimension(300, 100));
}
return rollingGroupScrollPane;
}
/**
*
* This method initializes rollingGroupTable
*
* @return javax.swing.JTable
*
*/
private Component getRollingGroupTable()
{
if (rollingGroupTable == null)
{
rollingGroupTable = new JTable();
rollingGroupTable.setModel(getIvjRollingSkillModel());
rollingGroupTable.setDragEnabled(true);
rollingGroupTable.setTransferHandler(transferHandler);
rollingGroupTable.setName("rollingGroupTable");
}
return rollingGroupTable;
}
/**
* This method initializes this dialog, packs it, and sets its screen location.
*/
private void initialize()
{
setContentPane(getJContentPane());
pack();
setLocationRelativeTo(GMGenSystem.inst);
}
/**
* <p>
* Hides the dialog when the OK button is pressed.
* </p>
*
* @param e unused
*/
private void okButtonActionPerformed(ActionEvent e)
{
setVisible(false);
}
/**
* <p>
* Sets the skill for ivjOpposedSkillModel when a skill is selected.
* </p>
*
* @param e unused
*/
private void opposingComboBoxActionPerformed(ActionEvent e)
{
ivjOpposedSkillModel.setSkill(opposingComboBox.getSelectedItem()
.toString());
}
/**
* <p>
* Rolls the skill checks in both skill tables.
* </p>
*
* @param e unused
*/
private void rollButtonActionPerformed(ActionEvent e)
{
ivjOpposedSkillModel.rollAll();
ivjRollingSkillModel.rollAll();
}
/**
* <p>
* Sets the skill for ivjRollingSkillModel.
* </p>
*
* @param e unused
*/
private void rollingComboBoxActionPerformed(ActionEvent e)
{
ivjRollingSkillModel.setSkill(rollingComboBox.getSelectedItem()
.toString());
}
}