/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* OptionTree.java
* Copyright (C) 2009 University of Waikato, Hamilton, New Zealand
*/
package wekaexamples.gui;
import weka.core.Utils;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
/**
* Displays commandline options as tree.
*
* @author FracPete (fracpete at waikato dot ac dot nz)
* @version $Revision$
*/
public class OptionTree
extends JPanel {
/** for serialization. */
private static final long serialVersionUID = 8177217505366217714L;
/** the label of the root node. */
public final static String LABEL_ROOT = "<root>";
/** the label of the nested node. */
public final static String LABEL_NESTED = "<nested>";
/** the text field for pasting the complete option string. */
protected JTextField m_TextOptionsFull;
/** the button to update the tree. */
protected JButton m_ButtonUpdate;
/** the text field for options represented by the currently selected sub-tree. */
protected JTextField m_TextOptionsSelected;
/** the button to copy the partial options to clipboard. */
protected JButton m_ButtonCopy;
/** the tree for displaying the options hierarchy. */
protected JTree m_TreeOptions;
/**
* Initializes the panel.
*/
public OptionTree() {
super();
initGUI();
}
/**
* For initializing the GUI.
*/
protected void initGUI() {
JPanel panel;
JLabel label;
setLayout(new BorderLayout());
// full options
panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
add(panel, BorderLayout.NORTH);
m_TextOptionsFull = new JTextField(50);
m_TextOptionsFull.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
update();
}
public void insertUpdate(DocumentEvent e) {
update();
}
public void removeUpdate(DocumentEvent e) {
update();
}
});
label = new JLabel("Full options");
label.setDisplayedMnemonic('F');
label.setLabelFor(m_TextOptionsFull);
panel.add(label);
panel.add(m_TextOptionsFull);
m_ButtonUpdate = new JButton("Update");
m_ButtonUpdate.setMnemonic('U');
m_ButtonUpdate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateTree();
}
});
panel.add(m_ButtonUpdate);
// table
m_TreeOptions = new JTree();
m_TreeOptions.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
generatePartialOptions();
}
});
add(new JScrollPane(m_TreeOptions));
// partial options
panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
add(panel, BorderLayout.SOUTH);
m_TextOptionsSelected = new JTextField(50);
m_TextOptionsSelected.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
update();
}
public void insertUpdate(DocumentEvent e) {
update();
}
public void removeUpdate(DocumentEvent e) {
update();
}
});
label = new JLabel("Selected options");
label.setDisplayedMnemonic('S');
label.setLabelFor(m_TextOptionsSelected);
panel.add(label);
panel.add(m_TextOptionsSelected);
m_ButtonCopy = new JButton("Copy");
m_ButtonCopy.setMnemonic('C');
m_ButtonCopy.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
StringSelection selection = new StringSelection(m_TextOptionsSelected.getText());
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(selection, selection);
}
});
panel.add(m_ButtonCopy);
update();
updateTree();
}
/**
* Updates the GUI.
*/
protected void update() {
m_ButtonCopy.setEnabled(m_TextOptionsSelected.getText().length() > 0);
}
/**
* Parses the given option and adds the generated parts to the parent.
* If there are nested options, then the parsing follows depth-first.
*
* @param parent the parent node to add the children to
* @param s the string to parse
*/
protected void parse(DefaultMutableTreeNode parent, String s) {
String[] options;
String currOption;
int i;
DefaultMutableTreeNode node;
try {
options = Utils.splitOptions(s);
for (i = 0; i < options.length; i++) {
currOption = options[i].trim();
if (currOption.indexOf(" ") > -1)
node = new DefaultMutableTreeNode(LABEL_NESTED);
else
node = new DefaultMutableTreeNode(currOption);
parent.add(node);
if (currOption.indexOf(" ") > -1)
parse(node, currOption);
}
}
catch (Exception e) {
node = new DefaultMutableTreeNode(e.toString());
parent.add(node);
e.printStackTrace();
}
}
/**
* Updates the tree with the current full options.
*/
protected void updateTree() {
DefaultMutableTreeNode root;
root = new DefaultMutableTreeNode(LABEL_ROOT);
if (m_TextOptionsFull.getText().length() > 0)
parse(root, m_TextOptionsFull.getText());
m_TreeOptions.setModel(new DefaultTreeModel(root));
}
/**
* Returns the options for this node and its subtree.
*
* @param node the node to get the options for
* @return the generated options
*/
protected String getOptions(DefaultMutableTreeNode node) {
Vector<String> options;
int i;
options = new Vector<String>();
// the node itself
if (!node.toString().equals(LABEL_ROOT) && !node.toString().equals(LABEL_NESTED))
options.add(node.toString());
// the node's children
for (i = 0; i < node.getChildCount(); i++)
options.add(getOptions((DefaultMutableTreeNode) node.getChildAt(i)));
return Utils.joinOptions(options.toArray(new String[options.size()]));
}
/**
* Generates the options based on the currently selected node and the
* corresponding subtree.
*/
protected void generatePartialOptions() {
DefaultMutableTreeNode node;
node = (DefaultMutableTreeNode) m_TreeOptions.getSelectionPath().getLastPathComponent();
m_TextOptionsSelected.setText(getOptions(node));
}
/**
* Sets the text and re-generates the tree.
*
* @param s the options to display
*/
public void setOptions(String s) {
m_TextOptionsFull.setText(s);
m_ButtonUpdate.doClick();
}
/**
* Displays a frame with the option tree panel.
*
* @param args displayed in the full options text
*/
public static void main(String[] args) {
OptionTree tree = new OptionTree();
final JFrame jf = new JFrame("Option Tree");
jf.getContentPane().setLayout(new BorderLayout());
jf.getContentPane().add(tree, BorderLayout.CENTER);
jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jf.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
jf.dispose();
}
});
jf.pack();
jf.setSize(800, 600);
jf.setLocationRelativeTo(null);
jf.setVisible(true);
if (args.length > 0)
tree.setOptions(Utils.joinOptions(args));
}
}