/*
* Copyright 2003 (C) 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
*/
package plugin.dicebag.gui;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import pcgen.gui2.tools.Icons;
/**
* <p>The internal frame view class for the DiceBag.</p>
*
* @author Ross M. Lodge
*/
public class DiceBagView extends JInternalFrame implements Observer
{
/** Bag model for this bag view */
DiceBagModel m_bag = null;
/** Listener for all buttons */
private BagListener m_bagListener = new BagListener();
/** Table model for editing table */
private BagTableModel m_tableModel = new BagTableModel();
/** Button to delete selected records */
private JButton m_deleteSelected;
/** Switches to editing mode */
private JButton m_editButton;
/** Button to move selected records down */
private JButton m_moveDown;
/** Button to move selected records up */
private JButton m_moveUp;
/** Rolls the expression in the expression field */
private JButton m_rollButton;
/** Button to stop editing and return to rolling mode */
private JButton m_stopEditing;
/** Label for expression editing field. */
private JLabel m_exprFieldLabel;
/** Result of the current roll. */
private JLabel m_exprResult;
/** Label for name of dice bag */
private JLabel m_nameFieldLabel;
/** JPanel for {@code BoxLayout.CENTER} of content pane */
private JPanel m_center;
/** Jpanel for top ({@code BorderLayout.NORTH}) region */
private JPanel m_top;
/** JPanel for bottom of m_top. */
private JPanel m_topBottom;
/** JPanel for top of m_top. */
private JPanel m_topTop;
/** Scroll pane for editing table */
private JScrollPane m_scrollPane;
/** Editing table */
private JTable m_table;
/** Field for editing dice expressions. */
private JTextField m_exprField;
/** Text field for editing name of dice bag */
private JTextField m_nameField;
/** List for holding dice rolling buttons */
private List<JButton> m_diceButtons;
/**
* <p>Constructs the view; initializes the components.</p>
*
* @param bag The DiceBagModel for this view
*/
public DiceBagView(DiceBagModel bag)
{
m_bag = bag;
initComponents();
}
/**
* <p>Returns this view's bag model.</p>
*
* @return The model for this view.
*/
public DiceBagModel getBag()
{
return m_bag;
}
/**
*
* <p>Handles the press of the editing button; calls
* {@code setupEditMode()}.</p>
*
* @param e Event that fired handler.
*/
public void handleEdit(ActionEvent e)
{
setupEditMode();
}
/* (non-Javadoc)
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*
* Does nothing
*/
@Override
public void update(Observable o, Object arg)
{
// TODO: Method doesn't do anything?
}
/**
*
* <p>Sets the result display.</p>
*
* @param diceRoll The die string utilized
* @param result The double value to display
*/
private void setRollResult(String diceRoll, double result)
{
String resultString = null;
if ((result % 1) == 0)
{
resultString = Integer.toString((int) Math.round(result));
}
else
{
resultString = Double.toString(result);
}
m_exprResult.setText("<html><b>" + diceRoll + ": " + resultString
+ "</b></html>");
}
/**
* <p>Removes all components from the various containers. It does not
* dereference the components, so they can be re-used.</p>
*/
private void cleanup()
{
m_top.removeAll();
m_topTop.removeAll();
m_topBottom.removeAll();
m_center.removeAll();
getContentPane().removeAll();
}
/**
* <p>The handler for deleting records; deletes the selected
* dice from the bag.</p>
*
* @param e The event that fired this handler.
*/
private void deleteRecords(ActionEvent e)
{
while ((m_table.getSelectedRow() >= 0)
&& (m_table.getSelectedRow() < m_bag.diceCount()))
{
m_bag.removeDie(m_table.getSelectedRow());
m_tableModel.fireTableRowsDeleted(m_table.getSelectedRow(), m_table
.getSelectedRow());
}
}
/**
* <p>Initializes the view and all components, and starts the bag in
* editing mode. It sets the size ({@code pack()}) of the
* internal frame, but does <b>not</b> show the frame.</p>
*
*
*/
private void initComponents()
{
/*
* /////////////////////////////////
* // Expr [ ] [roll] //
* // |RESULT | [edit] //
* /////////////////////////////////
* // [ expr ] [ expr ] [ expr ] //
* // [ expr ] [ expr ] [ expr ] //
* // [ expr ] [ expr ] [ expr ] //
* // [ expr ] [ expr ] [ expr ] //
* // [ expr ] [ expr ] [ expr ] //
* // [ expr ] [ expr ] [ expr ] //
* /////////////////////////////////
*/
// Set basic properties
setTitle(m_bag.getName());
setResizable(true);
setClosable(true);
setMaximizable(true);
setIconifiable(true);
// Assure main panes content borderlayout
getContentPane().setLayout(new BorderLayout());
//Create all JPanels
m_top = new JPanel();
m_topTop = new JPanel();
m_topBottom = new JPanel();
m_center = new JPanel();
//Set Layout managers for all panels
m_top.setLayout(new GridLayout(2, 1));
m_topTop.setLayout(new BoxLayout(m_topTop, BoxLayout.X_AXIS));
m_topBottom.setLayout(new BoxLayout(m_topBottom, BoxLayout.X_AXIS));
m_center.setLayout(new DiceBagGridLayout(0, 3,
DiceBagGridLayout.MANAGE_BY_COLUMNS, 75, 100));
m_center.setBorder(new EmptyBorder(5, 5, 5, 5));
//Rolling mode controls
m_rollButton = new JButton("Roll");
m_rollButton.setActionCommand("ROLL");
m_rollButton.addActionListener(m_bagListener);
m_exprResult = new JLabel(" ");
m_exprResult.setMinimumSize(new Dimension(50, m_exprResult
.getMinimumSize().height));
m_exprResult.setPreferredSize(new Dimension(50, m_exprResult
.getPreferredSize().height));
m_exprResult.setMaximumSize(new Dimension(Integer.MAX_VALUE,
m_exprResult.getMaximumSize().height));
m_editButton = new JButton("Edit");
m_editButton.setActionCommand("EDIT");
m_editButton.addActionListener(m_bagListener);
m_diceButtons = new ArrayList<>();
m_exprField = new JTextField();
m_exprFieldLabel = new JLabel("Roll Expr: ");
//Components for editing mode
m_table = new JTable(m_tableModel);
m_scrollPane = new JScrollPane(m_table);
/*
* stop editing button
* delete selected button
* move up button
* move down button
*/
m_stopEditing = new JButton("Stop Editing");
m_stopEditing.setActionCommand("STOP_EDITING");
m_stopEditing.addActionListener(m_bagListener);
m_deleteSelected = new JButton("Delete");
m_deleteSelected.setActionCommand("DELETE");
m_deleteSelected.addActionListener(m_bagListener);
m_moveUp = new JButton("Move up");
m_moveUp.setActionCommand("MOVE_UP");
m_moveUp.addActionListener(m_bagListener);
m_moveDown = new JButton("Move down");
m_moveDown.setActionCommand("MOVE_DOWN");
m_moveDown.addActionListener(m_bagListener);
m_nameField = new JTextField();
m_nameFieldLabel = new JLabel("Name: ");
m_nameField.getDocument().addDocumentListener(new DocumentListener()
{
@Override
public void insertUpdate(DocumentEvent e)
{
m_bag.setName(m_nameField.getText());
setTitle(m_nameField.getText());
}
@Override
public void removeUpdate(DocumentEvent e)
{
m_bag.setName(m_nameField.getText());
setTitle(m_nameField.getText());
}
@Override
public void changedUpdate(DocumentEvent e)
{
// TODO: Method doesn't do anything?
}
});
setFrameIcon(Icons.gmgen_icon.getImageIcon());
//Start in rolling mode
setupRollingMode();
//Size and position the window
pack();
}
/**
* <p>The handler for the move down button. Moves the selected
* records down one step.</p>
*
* @param e The event that fired this handler.
*/
private void moveRecordsDown(ActionEvent e)
{
for (int row = m_table.getRowCount() - 2; row >= 0; row--)
{
if ((row < (m_table.getRowCount() - 2))
&& m_table.isRowSelected(row)
&& !m_table.isRowSelected(row + 1))
{
final String die1 = m_bag.getDie(row);
final String die2 = m_bag.getDie(row + 1);
m_bag.setDie(row + 1, die1);
m_bag.setDie(row, die2);
m_tableModel.fireTableRowsUpdated(row, row + 1);
m_table.getSelectionModel().addSelectionInterval(row + 1,
row + 1);
m_table.getSelectionModel().removeSelectionInterval(row, row);
}
}
}
/**
* <p>The handler for the move up button. Moves the selected
* records up one step.</p>
*
* @param e The event that fired this handler.
*/
private void moveRecordsUp(ActionEvent e)
{
for (int row = 0; row < m_table.getRowCount(); row++)
{
if ((row > 0) && m_table.isRowSelected(row)
&& !m_table.isRowSelected(row - 1))
{
final String die1 = m_bag.getDie(row);
final String die2 = m_bag.getDie(row - 1);
m_bag.setDie(row - 1, die1);
m_bag.setDie(row, die2);
m_tableModel.fireTableRowsUpdated(row - 1, row);
m_table.getSelectionModel().addSelectionInterval(row - 1,
row - 1);
m_table.getSelectionModel().removeSelectionInterval(row, row);
}
}
}
/**
* <p>Sets up the editing mode; cleans components and then
* displays the editing component setup.</p>
*/
private void setupEditMode()
{
cleanup();
getContentPane().add(m_center, BorderLayout.CENTER);
getContentPane().add(m_top, BorderLayout.NORTH);
m_top.add(m_topTop);
m_top.add(m_topBottom);
m_nameField.setPreferredSize(new Dimension(100, m_nameField
.getPreferredSize().height));
m_topBottom.add(m_nameFieldLabel);
m_topBottom.add(m_nameField);
m_nameField.setText(m_bag.getName());
if (m_center.getLayout() instanceof DiceBagGridLayout)
{
((DiceBagGridLayout) m_center.getLayout()).setRows(1);
((DiceBagGridLayout) m_center.getLayout()).setColumns(1);
}
m_center.add(m_scrollPane);
m_topTop.add(m_stopEditing);
m_topTop.add(Box.createHorizontalStrut(5));
m_topTop.add(m_deleteSelected);
m_topTop.add(Box.createHorizontalStrut(5));
m_topTop.add(m_moveUp);
m_topTop.add(Box.createHorizontalStrut(5));
m_topTop.add(m_moveDown);
m_table.setPreferredScrollableViewportSize(new Dimension(m_table
.getPreferredScrollableViewportSize().width, 200));
getContentPane().validate();
pack();
}
/**
* <p>Sets up the rolling mode; first cleans out all components
* and then displays the components for the rolling mode.</p>
*
*
*/
private void setupRollingMode()
{
cleanup();
if (m_center.getLayout() instanceof DiceBagGridLayout)
{
((DiceBagGridLayout) m_center.getLayout()).setRows(0);
((DiceBagGridLayout) m_center.getLayout()).setColumns(3);
}
//Add main JPanels to content pane
getContentPane().add(m_top, BorderLayout.NORTH);
getContentPane().add(m_center, BorderLayout.CENTER);
//Add secondary panels to top panel
m_top.add(m_topTop);
m_top.add(m_topBottom);
//Add components to topTop panel
m_topTop.add(m_exprFieldLabel);
m_exprField.setPreferredSize(new Dimension(50, m_exprField
.getPreferredSize().height));
m_topTop.add(m_exprField);
m_topTop.add(Box.createHorizontalStrut(10));
m_topTop.add(m_rollButton);
//Add components to topBottom panel
m_topBottom.add(m_exprResult);
m_topBottom.add(Box.createHorizontalStrut(10));
m_topBottom.add(m_editButton);
//Add components to the center panel
for (int i = 0; i < m_bag.diceCount(); i++)
{
JButton button = null;
if (m_diceButtons.size() > i)
{
button = m_diceButtons.get(i);
}
else
{
button = new JButton(" ");
button.setPreferredSize(new Dimension(100, button
.getPreferredSize().height));
button.addActionListener(m_bagListener);
button.setActionCommand(Integer.toString(i));
m_diceButtons.add(button);
}
button.setText(m_bag.getDie(i));
m_center.add(button);
}
getContentPane().validate();
pack();
}
/**
* <p>Stops editing and goes back to rolling mode.</p>
*
* @param e The event that fired this handler.
*/
private void stopEditing(ActionEvent e)
{
setupRollingMode();
}
/**
* <p>An action listener for the buttons in the GUI.</p>
*
* @author Ross M. Lodge
*/
private class BagListener implements ActionListener
{
/* (non-Javadoc)
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(ActionEvent e)
{
String command = e.getActionCommand();
if ("ROLL".equals(command))
{
setRollResult(m_exprField.getText(), m_bag.rollDie(m_exprField
.getText()));
}
else if ("EDIT".equals(command))
{
handleEdit(e);
}
else if ("STOP_EDITING".equals(command))
{
stopEditing(e);
}
else if ("DELETE".equals(command))
{
deleteRecords(e);
}
else if ("MOVE_UP".equals(command))
{
moveRecordsUp(e);
}
else if ("MOVE_DOWN".equals(command))
{
moveRecordsDown(e);
}
else
{
try
{
int index = Integer.parseInt(command);
setRollResult(m_bag.getDie(index), m_bag.rollDie(index));
}
catch (NumberFormatException ex)
{
System.err
.println("Invalid command passed to BagListener.");
ex.printStackTrace();
}
}
}
}
/**
*
* <p>Table model for displaying/editing the dice bag information.
* Basically this overrides enough of {@code AbstracTableModel}
* to make the code useful.</p>
*
* @author Ross M. Lodge
*
*/
private class BagTableModel extends AbstractTableModel
{
/* (non-Javadoc)
* @see javax.swing.table.TableModel#isCellEditable(int, int)
*/
@Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return true;
}
/* (non-Javadoc)
* @see javax.swing.table.TableModel#getColumnClass(int)
*/
@Override
public Class<?> getColumnClass(int columnIndex)
{
return String.class;
}
/* (non-Javadoc)
* @see javax.swing.table.TableModel#getColumnCount()
*
* Always a single column
*/
@Override
public int getColumnCount()
{
return 1;
}
/* (non-Javadoc)
* @see javax.swing.table.TableModel#getColumnName(int)
*/
@Override
public String getColumnName(int column)
{
return "Dice Expression";
}
/* (non-Javadoc)
* @see javax.swing.table.TableModel#getRowCount()
*
* We add one more row than the number of dice in the bag;
* users can type in that row to add a new die.
*/
@Override
public int getRowCount()
{
return m_bag.diceCount() + 1;
}
/* (non-Javadoc)
* @see javax.swing.table.TableModel#setValueAt(java.lang.Object, int, int)
*
* Sets the value at the specified index, or adds a new die at the end.
*/
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
{
if ((rowIndex >= 0) && (rowIndex < (getRowCount() - 1)))
{
if ((aValue == null) || (aValue.toString().isEmpty()))
{
m_bag.removeDie(rowIndex);
fireTableRowsDeleted(rowIndex, rowIndex);
}
else
{
m_bag.setDie(rowIndex, aValue.toString());
}
}
else if (rowIndex == (getRowCount() - 1))
{
m_bag.addDie(aValue.toString());
fireTableRowsInserted(rowIndex, rowIndex);
}
}
/* (non-Javadoc)
* @see javax.swing.table.TableModel#getValueAt(int, int)
*/
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
String returnValue = null;
if ((rowIndex >= 0) && (rowIndex < (getRowCount() - 1)))
{
returnValue = m_bag.getDie(rowIndex);
}
return returnValue;
}
}
}