/*
* This file is part of JGAP.
*
* JGAP offers a dual license model containing the LGPL as well as the MPL.
*
* For licensing information please see the file license.txt included with JGAP
* or have a look at the top of class org.jgap.Chromosome which representatively
* includes the JGAP license policy applicable for any file delivered with JGAP.
*/
package org.jgap.gui;
import java.util.*;
import java.awt.Dimension;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import org.jgap.data.config.*;
import info.clearthought.layout.*;
/**
* GUI for the JGAP Configurator.
*
* @author Siddhartha Azad
* @since 2.3
*/
public class ConfigFrame
extends JFrame
implements IConfigInfo {
/** String containing the CVS revision. Read out via reflection!*/
private final static String CVS_REVISION = "$Revision: 1.19 $";
// data members of class ConfigFrame
private Object m_conHandler;
private boolean m_isRoot;
// list of JPanel objects added in this frame
private List m_panels;
// ListBox properties
private List m_listProps;
// TextBox properties
private List m_textProps;
// list of ListGroups
private List m_listGroups;
// list of TextGroups
private List m_textGroups;
private JPanel m_listPanel;
private JPanel m_textPanel;
private JPanel m_configPanel;
private JButton m_configButton;
private ConfigButtonListener m_cbl;
private JTextField m_fileName;
private JButton m_configureButton;
private JTextField m_configItem;
private Configurable m_conObj;
// the parent frame of this frame
private ConfigFrame m_parent;
// default name for the config file
private static final String m_defaultConfigFile = "jgap.con";
/**
* Constructor
* @param a_parent
* @param a_title the title of the frame
* @param a_isRoot
*
* @author Siddhartha Azad
* @since 2.3
*/
ConfigFrame(final ConfigFrame a_parent, final String a_title,
final boolean a_isRoot) {
super(a_title);
m_panels = Collections.synchronizedList(new ArrayList());
m_textProps = Collections.synchronizedList(new ArrayList());
m_listProps = Collections.synchronizedList(new ArrayList());
m_listGroups = Collections.synchronizedList(new ArrayList());
m_textGroups = Collections.synchronizedList(new ArrayList());
m_cbl = new ConfigButtonListener(this);
m_isRoot = a_isRoot;
m_parent = a_parent;
}
/**
* Does the initial setup of the JFrame and shows it.
* @param a_conHandler the configuration handler from which this ConfigFrame
* would get information
*
* @author Siddhartha Azad
* @since 2.3
*/
public void createAndShowGUI(final Object a_conHandler) {
JFrame.setDefaultLookAndFeelDecorated(true);
m_conHandler = a_conHandler;
// display
pack();
setVisible(true);
setBounds(100, 100, 300, 300);
setSize(500, 300);
try {
MetaConfig mt = MetaConfig.getInstance();
}
catch (MetaConfigException mcEx) {
JOptionPane.showMessageDialog(null,
"Exception while parsing JGAP Meta"
+ " Config file "
+ mcEx.getMessage(),
"Meta Config Exception",
JOptionPane.ERROR_MESSAGE);
}
catch (Exception ex) {
JOptionPane.showMessageDialog(null,
"Exception while parsing JGAP Meta Config"
+ " file "
+ ex.getMessage(),
"Meta Config Exception",
JOptionPane.ERROR_MESSAGE);
}
setup();
show();
if (m_isRoot) {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
else {
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
}
/**
* Getter for the Configuration Information on this frame.
* @return The ConfigData object containing the configuration
* information on this frame
*
* @author Siddhartha Azad
* @since 2.3
*/
public ConfigData getConfigData() {
ConfigData cd = new ConfigData();
cd.setNS(m_conHandler.getClass().getName());
// add lists
List values;
try {
Iterator lIter = m_listGroups.iterator();
while (lIter.hasNext()) {
ListGroup lg = (ListGroup) lIter.next();
values = Collections.synchronizedList(new ArrayList());
Enumeration e = lg.getOutListModel().elements();
while (e.hasMoreElements()) {
String val = (String) e.nextElement();
values.add(val);
}
cd.addListData(lg.getProp().getName(), values);
}
// add textFields
TextGroup tg;
Iterator tIter = m_textGroups.iterator();
while (tIter.hasNext()) {
tg = (TextGroup) tIter.next();
cd.addTextData(tg.getProp().getName(), tg.getTextField().getText());
}
}
catch (ClassCastException cex) {
JOptionPane.showMessageDialog(null, cex.getMessage(),
"ConfigFrame.getConfigData():Configuration"
+ " Error",
JOptionPane.INFORMATION_MESSAGE);
}
return cd;
}
/**
* Get the config file to write to.
* @return the config file name to write to
*
* @author Siddhartha Azad
* @since 2.3
*/
public String getFileName() {
// only the root frame has the text box for the filename
if (m_isRoot) {
String fName = m_fileName.getText();
// use a default file name
if (fName.equals("")) {
fName = ConfigFrame.m_defaultConfigFile;
}
return fName;
}
else {
return m_parent.getFileName();
}
}
/**
* Setup the GUI.
* There are 3 maximum panels at this time. The first one contains JLists if
* there are configurable values that can be choosen from a list of items.
* The second panel contains all values configurable via a JTextField. The
* third panel contains the filename and configure button.
*
* @author Siddhartha Azad
* @since 2.3
*/
private void setup() {
int numLists = 0, numTexts = 0;
List props = null;
try {
/** @todo find a better way to get the classname than getNS() */
props = MetaConfig.getInstance().getConfigProperty(m_conHandler.getClass().getName());
}
catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex.getMessage(),
"Configuration Error: Could not get"
+ " properties for class "
+ m_conHandler.getClass().getName(),
JOptionPane.INFORMATION_MESSAGE);
}
if (props == null) {
JOptionPane.showMessageDialog(null,
"setup():No Configurable Properties in"
+ " this Configuration",
"Configuration Message",
JOptionPane.INFORMATION_MESSAGE);
return;
}
Iterator iter = props.iterator();
while (iter.hasNext()) {
try {
ConfigProperty prop = (ConfigProperty) iter.next();
if (prop.getWidget().equals("JList")) {
numLists++;
m_listProps.add(prop);
}
else if (prop.getWidget().equals("JTextField")) {
numTexts++;
m_textProps.add(prop);
}
else {
// Only JLists and JTextFields allowed at this point
JOptionPane.showMessageDialog(null,
"Unknown Widget " + prop.getWidget(),
"Configuration Error",
JOptionPane.INFORMATION_MESSAGE);
}
}
catch (ClassCastException cex) {
JOptionPane.showMessageDialog(null,
cex.getMessage(),
"ConfigError.setup():Configuration Error:"
+ " Invalid cast",
JOptionPane.INFORMATION_MESSAGE);
}
}
// If no known widgets are present, a GUI cannot be rendered
if (numLists == 0 && numTexts == 0) {
JOptionPane.showMessageDialog(null,
"No Configurable Properties in this"
+ " Configuration",
"Configuration Information",
JOptionPane.INFORMATION_MESSAGE);
return;
}
// 2 panels at least, 1 for the widgets and 1 in the end for the
// Frame specific buttons
int numPanels = 2;
if (numLists > 0 && numTexts > 0) {
numPanels = 3;
}
// add the appropriate number of panels
addWidgets(numPanels, numLists, numTexts);
}
/**
* Add the widgets to the frame.
*
* @param a_numPanels Number of panels to add
* @param a_numLists Number of lists to add
* @param a_numTexts Number of text boxes to add
*
* @author Siddhartha Azad
* @since 2.3
*/
private void addWidgets(int a_numPanels, final int a_numLists,
final int a_numTexts) {
try {
a_numPanels = 3;
// TableLayout setup for the panels on the frame
double[][] tableArray = new double[2][a_numPanels];
double perPanel = (double) (1.0 / (double) a_numPanels);
int i = 0;
for (i = 0; i < a_numPanels - 1; i++) {
tableArray[1][i] = perPanel;
}
// give the remaining space to the last row
tableArray[1][i] = TableLayout.FILL;
// single column can take all the space available
tableArray[0][0] = TableLayout.FILL;
getContentPane().setLayout(new TableLayout(tableArray));
// add the panels to the frame now
int panelsAdded = 0;
// if we have lists to add
if (a_numLists > 0) {
double[][] panelSize;
// for every input list there's an output list and the buttons
// hence 3 columns for every list
int numCols = 3 * a_numLists;
// TableLayout setup for the list panel
panelSize = new double[2][numCols];
double space = (double) (1.0 / (double) a_numLists);
// 40% space to the lists, 20% to the buttons
double listSpace = space * 0.4;
double buttonSpace = space * 0.2;
for (int itr = 0; itr < a_numLists; itr++) {
panelSize[0][3 * itr] = listSpace;
panelSize[0][3 * itr + 1] = buttonSpace;
panelSize[0][3 * itr + 2] = listSpace;
}
// single row can take all the space
panelSize[1][0] = TableLayout.FILL;
m_listPanel = new JPanel();
m_panels.add(m_listPanel);
m_listPanel.setLayout(new TableLayout(panelSize));
getContentPane().add(m_listPanel, new TableLayoutConstraints(
0, panelsAdded, 0, panelsAdded,
TableLayout.FULL, TableLayout.FULL));
// increment number of panels added
panelsAdded++;
// add the lists to the panel
Iterator iter = m_listProps.iterator(), valIter;
ConfigProperty prop;
ListGroup lg;
for (int itr1 = 0; itr1 < a_numLists && iter.hasNext(); itr1++) {
lg = new ListGroup(this);
m_listGroups.add(lg);
prop = (ConfigProperty) iter.next();
lg.setProp(prop);
m_listPanel.add(lg.getListScroller(),
new TableLayoutConstraints(3 * itr1, 0, 3 * itr1, 0,
TableLayout.CENTER, TableLayout.CENTER));
// add the button to move data from outlist back to list
m_listPanel.add(lg.getLButton(),
new TableLayoutConstraints(3 * itr1 + 1, 0,
3 * itr1 + 1, 0,
TableLayout.CENTER, TableLayout.TOP));
// add the button to move data from list to outlist
m_listPanel.add(lg.getRButton(),
new TableLayoutConstraints(3 * itr1 + 1, 0,
3 * itr1 + 1, 0,
TableLayout.CENTER, TableLayout.BOTTOM));
// added the item values to the list
valIter = prop.getValuesIter();
while (valIter.hasNext()) {
lg.getListModel().addElement(valIter.next());
}
m_listPanel.add(lg.getOutListScroller(),
new TableLayoutConstraints(3 * itr1 + 2, 0,
3 * itr1 + 2, 0,
TableLayout.CENTER, TableLayout.CENTER));
}
}
// add the textFields
if (a_numTexts > 0) {
double[][] panelSize;
int numCols = a_numTexts * 2;
panelSize = new double[2][numCols];
// TableLayout setup for the JTextFields panel
double perText = (double) (1.0 / (double) numCols);
int itr = 0;
// add the panel for the texts fields
for (itr = 0; itr < numCols - 1; itr++) {
panelSize[0][itr] = perText;
}
panelSize[0][itr] = TableLayout.FILL;
// single row
panelSize[1][0] = TableLayout.FILL;
m_textPanel = new JPanel();
m_panels.add(m_textPanel);
m_textPanel.setLayout(new TableLayout(panelSize));
getContentPane().add(m_textPanel, new TableLayoutConstraints(
0, panelsAdded, 0, panelsAdded,
TableLayout.FULL, TableLayout.FULL));
panelsAdded++;
// add the text fields to the panel
TextGroup tg;
Iterator iter = m_textProps.iterator(), valIter;
ConfigProperty prop;
for (int itr1 = 0; itr1 < a_numTexts && iter.hasNext(); itr1++) {
tg = new TextGroup();
m_textGroups.add(tg);
prop = (ConfigProperty) iter.next();
tg.setProp(prop);
JLabel label = tg.getLabel();
label.setText(prop.getName());
m_textPanel.add(label,
new TableLayoutConstraints(itr1, 0, itr1, 0,
TableLayout.RIGHT, TableLayout.CENTER));
m_textPanel.add(tg.getTextField(),
new TableLayoutConstraints(itr1 + 1, 0, itr1 + 1, 0,
TableLayout.LEFT, TableLayout.CENTER));
}
}
// add the configure button
double[][] panelSize;
panelSize = new double[2][4];
// percentage per column for the tablelayout
panelSize[0][0] = .25;
panelSize[0][1] = .25;
panelSize[0][2] = .25;
panelSize[0][3] = .25;
// single row
panelSize[1][0] = TableLayout.FILL;
m_configPanel = new JPanel();
m_panels.add(m_configPanel);
m_configPanel.setLayout(new TableLayout(panelSize));
getContentPane().add(m_configPanel, new TableLayoutConstraints(
0, panelsAdded, 0, panelsAdded,
TableLayout.FULL, TableLayout.FULL));
// add the textfield for the config file name
m_configItem = new JTextField(50);
m_configPanel.add(m_configItem,
new TableLayoutConstraints(0, 0, 0, 0,
TableLayout.RIGHT,
TableLayout.CENTER));
m_configureButton = new JButton("Configure");
m_configureButton.addActionListener(m_cbl);
m_configPanel.add(m_configureButton,
new TableLayoutConstraints(1, 0, 1, 0,
TableLayout.LEFT,
TableLayout.CENTER));
if (m_isRoot) {
m_fileName = new JTextField("jgap.con");
m_configPanel.add(m_fileName,
new TableLayoutConstraints(2, 0, 2, 0,
TableLayout.RIGHT, TableLayout.CENTER));
m_configButton = new JButton("Generate");
m_configButton.addActionListener(m_cbl);
m_configPanel.add(m_configButton,
new TableLayoutConstraints(3, 0, 3, 0,
TableLayout.LEFT, TableLayout.CENTER));
}
else {
m_configButton = new JButton("Save Configuration");
m_configButton.addActionListener(m_cbl);
m_configPanel.add(m_configButton,
new TableLayoutConstraints(3, 0, 3, 0,
TableLayout.LEFT, TableLayout.CENTER));
}
}
catch (Exception ex) {
JOptionPane.showMessageDialog(null,
"Exception" + ex.toString(),
"This is the title",
JOptionPane.INFORMATION_MESSAGE);
}
}
/**
* This class groups the property data structure along with the JLists
* associated with it.
*
* @author Siddhartha Azad
* @since 2.3
*/
public class ListGroup {
// list that will display the available items
private JList m_list;
// model for list
private DefaultListModel m_listModel;
private JScrollPane m_listScroller;
// list that will display the selected items
private JList m_outList;
// model for outList
private DefaultListModel m_outListModel;
private JScrollPane m_outListScroller;
private ConfigListSelectionListener m_outListListener;
// buttons to move data to/from lists
private JButton m_lButton;
private JButton m_rButton;
// property object associated with this ListGroup
private ConfigProperty m_prop;
private ListButtonListener m_listBL;
private ConfigFrame m_frame;
/**
* Constructor responsible for creating all items that go on the list
* panel.
*
* @author Siddhartha Azad
* @since 2.3
*/
ListGroup(final ConfigFrame a_frame) {
m_frame = a_frame;
// create the List of values
m_listModel = new DefaultListModel();
m_list = new JList(m_listModel);
m_list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
m_list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
m_list.setVisibleRowCount( -1);
m_listScroller = new JScrollPane(m_list);
m_listScroller.setPreferredSize(new Dimension(250, 80));
// create the output list
m_outListModel = new DefaultListModel();
m_outList = new JList(m_outListModel);
m_outList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
m_outList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
m_outList.setVisibleRowCount( -1);
m_outListScroller = new JScrollPane(m_outList);
m_outListListener = new ConfigListSelectionListener(m_frame, m_outList);
m_outList.getSelectionModel().addListSelectionListener(m_outListListener);
m_outListScroller.setPreferredSize(new Dimension(250, 80));
// The buttons to move data to/from outList
m_listBL = new ListButtonListener(this);
m_lButton = new JButton("<-");
m_lButton.addActionListener(m_listBL);
m_rButton = new JButton("->");
m_rButton.addActionListener(m_listBL);
}
/**
* Getter for the ConfigProperty object associated with this ListGroup.
* @return the ConfigProperty object associated with this ListGroup
*
* @author Siddhartha Azad
* @since 2.3
*/
public ConfigProperty getProp() {
return m_prop;
}
/**
* Setter for the ConfigProperty object associated with this ListGroup.
* This object is used to retrieve the values that the list is initialized
* with.
* @param a_prop the ConfigProperty object associated with this ListGroup
*
* @author Siddhartha Azad
* @since 2.3
*/
public void setProp(final ConfigProperty a_prop) {
m_prop = a_prop;
}
/**
* @return the JList containing the items to select from
*
* @author Siddhartha Azad
* @since 2.3
*/
public JList getList() {
return m_list;
}
/**
* @return DefaultListModel for the list
*
* @author Siddhartha Azad
* @since 2.3
*/
public DefaultListModel getListModel() {
return m_listModel;
}
/**
* @return scroller for the list
*
* @author Siddhartha Azad
* @since 2.3
*/
public JScrollPane getListScroller() {
return m_listScroller;
}
/**
* @return Output JList
*
* @author Siddhartha Azad
* @since 2.3
*/
public JList getOutList() {
return m_outList;
}
/**
* Getter for the output list's associated model.
* @return DefaultListModel for the output list
*
* @author Siddhartha Azad
* @since 2.3
*/
public DefaultListModel getOutListModel() {
return m_outListModel;
}
/**
* @return scroller for the output list
*
* @author Siddhartha Azad
* @since 2.3
*/
public JScrollPane getOutListScroller() {
return m_outListScroller;
}
/**
* @return the button to move items from outlist to list
*
* @author Siddhartha Azad
* @since 2.3
*/
public JButton getLButton() {
return m_lButton;
}
/**
* @return the button to move items from list to outlist
*
* @author Siddhartha Azad
* @since 2.3
*/
public JButton getRButton() {
return m_rButton;
}
/**
* Move selected items from the output list back to the list.
*
* @author Siddhartha Azad
* @since 2.3
*/
public void leftButtonPressed() {
int[] indices = m_outList.getSelectedIndices();
for (int i = 0; i < indices.length; i++) {
String removed = (String) m_outListModel.remove(indices[0]);
m_listModel.addElement(removed);
}
}
/**
* Move selected items from list to the output list.
*
* @author Siddhartha Azad
* @since 2.3
*/
public void rightButtonPressed() {
int[] indices = m_list.getSelectedIndices();
for (int i = 0; i < indices.length; i++) {
String removed = (String) m_listModel.remove(indices[0]);
m_outListModel.addElement(removed);
}
}
}
/**
* This class groups the property data structure along with the JLists
* associated with it.
*
* @author Siddhartha Azad
* @since 2.3
*/
class TextGroup {
private JTextField m_textField;
private JLabel m_label;
private ConfigProperty m_prop;
TextGroup() {
m_textField = new JTextField(20);
m_label = new JLabel();
}
public ConfigProperty getProp() {
return m_prop;
}
public void setProp(final ConfigProperty a_prop) {
m_prop = a_prop;
}
public JTextField getTextField() {
return m_textField;
}
public JLabel getLabel() {
return m_label;
}
}
/**
* Listener for the Configure button.
*
* @author Siddhartha Azad
* @since 2.3
*/
class ConfigButtonListener
implements ActionListener {
private ConfigFrame m_frame;
ConfigButtonListener(final ConfigFrame a_frame) {
m_frame = a_frame;
}
public void actionPerformed(final ActionEvent a_e) {
// configButton is pressed
if (a_e.getActionCommand().equals("Configure")) {
String conStr = m_configItem.getText();
if (conStr.equals("")) {
JOptionPane.showMessageDialog(null,
"Configurable name is empty, cannot"
+ " configure.",
"Configuration Error",
JOptionPane.INFORMATION_MESSAGE);
}
else {
try {
Class conClass;
m_conObj = null;
try {
conClass = Class.forName(conStr);
}
catch (ClassNotFoundException cnfEx) {
JOptionPane.showMessageDialog(null,
cnfEx.getMessage(),
"Configuration Error: Class not"
+ " found",
JOptionPane.INFORMATION_MESSAGE);
return;
}
try {
m_conObj = (Configurable) conClass.
newInstance();
}
catch (Exception ex) {
JOptionPane.showMessageDialog(null,
ex.getMessage(),
"Configuration Error:Could not"
+ " create object",
JOptionPane.INFORMATION_MESSAGE);
return;
}
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
GUIManager.getInstance().showFrame(m_frame, m_conObj);
}
catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex.getMessage(),
"Configuration Error:Could"
+ " not create new Frame",
JOptionPane.ERROR_MESSAGE);
}
}
});
}
catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex.getMessage(),
"Configuration Error:Could not"
+ " create new frame",
JOptionPane.ERROR_MESSAGE);
}
}
catch (Exception ex) {
JOptionPane.showMessageDialog(null,
ex.getMessage(),
"Configuration Error",
JOptionPane.INFORMATION_MESSAGE);
}
}
}
else {
// generate the config file
ConfigWriter.getInstance().write(m_frame);
}
}
}
/**
* Listener for list buttons to move items around.
*
* @author Siddhartha Azad
* @since 2.3
*/
public class ListButtonListener
implements ActionListener {
private ListGroup m_lg;
ListButtonListener(final ListGroup a_lg) {
m_lg = a_lg;
}
public void actionPerformed(final ActionEvent a_e) {
// one of the list buttons is pressed
if (a_e.getActionCommand().equals("<-")) {
// from outList to list
m_lg.leftButtonPressed();
}
else {
// from list to outList
m_lg.rightButtonPressed();
}
}
}
/**
* Listener for changes in the list of items.
*
* @author Siddhartha Azad
* @since 2.3
*/
public class ConfigListSelectionListener
implements ListSelectionListener {
private JList m_list;
private ConfigFrame m_frame;
public ConfigListSelectionListener(final ConfigFrame a_frame,
final JList a_list) {
m_list = a_list;
m_frame = a_frame;
}
public void valueChanged(final ListSelectionEvent a_e) {
Object[] values = m_list.getSelectedValues();
if (values.length > 0) {
String value = (String) values[0];
notifySelection(value);
}
}
}
/**
* Notify the frame that a value has been selected in the output list for
* further configuration.
*
* @author Siddhartha Azad
* @since 2.3
*/
private void notifySelection(final String a_value) {
m_configItem.setText(a_value);
}
}