/* * 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); } }