/* * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.program.option.gui; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.util.*; import java.util.List; import javax.swing.*; import com.sun.max.*; import com.sun.max.gui.*; import com.sun.max.lang.*; import com.sun.max.program.*; import com.sun.max.program.option.*; /** * A dialog used to present the {@linkplain Option options} defined by a {@link OptionSet}. */ public class OptionsDialog extends JDialog { public static final int TEXT_FIELD_COLUMNS = 40; protected abstract static class GUIOption<T> implements ItemListener { protected final Option<T> option; protected final JCheckBox guard; protected final JLabel label; protected JComponent input; protected GUIOption(Option<T> opt) { this.option = opt; guard = new JCheckBox(); label = new JLabel(option.getName()); guard.addItemListener(this); } public JCheckBox getGuard() { return guard; } public JLabel getLabel() { return label; } public JComponent getInputComponent() { return input; } public void itemStateChanged(ItemEvent e) { setInputAndLabelEnabled(guard.isSelected()); } protected void setEnabledOfContainedComponents(Container container, boolean enabled) { for (Component component : container.getComponents()) { component.setEnabled(enabled); if (component instanceof Container) { setEnabledOfContainedComponents((Container) component, enabled); } } } protected void setInputAndLabelEnabled(boolean enabled) { input.setEnabled(enabled); setEnabledOfContainedComponents(input, enabled); label.setEnabled(enabled); } public abstract T getValue() throws Option.Error; public abstract void setValue(T value); public void commitOption() { if (guard.isSelected()) { option.setValue(getValue()); } } public String unparse() { return option.getType().unparseValue(getValue()); } public void parse(String str) { guard.setEnabled(true); setValue(option.getType().parseValue(str)); } } protected static class IntegerGUIOption extends GUIOption<Integer> { private final JTextField textField; protected IntegerGUIOption(Option<Integer> option) { super(option); textField = new JTextField(); input = textField; textField.setColumns(TEXT_FIELD_COLUMNS); setValue(option.getValue()); } @Override public void setValue(Integer i) { textField.setText(String.valueOf(i)); guard.setSelected(i != null); setInputAndLabelEnabled(i != null); } @Override public Integer getValue() { if (guard.isSelected()) { return option.getType().parseValue(textField.getText()); } return null; } } protected static class BooleanGUIOption extends GUIOption<Boolean> { private final JLabel helpText; protected BooleanGUIOption(Option<Boolean> option) { super(option); helpText = new JLabel("<html>" + option.getHelp() + "</html>"); input = helpText; setValue(option.getValue()); } @Override public void commitOption() { if (guard.isSelected()) { option.setValue(true); } else { option.setValue(false); } } @Override public void setValue(Boolean b) { guard.setSelected(b); helpText.setEnabled(b); setInputAndLabelEnabled(b); } @Override public Boolean getValue() { return guard.isSelected(); } } protected static class StringGUIOption extends GUIOption<String> { private final JTextField textField; protected StringGUIOption(Option<String> option) { super(option); textField = new JTextField(TEXT_FIELD_COLUMNS); input = textField; setValue(option.getValue()); } @Override public void setValue(String i) { textField.setText(String.valueOf(i)); guard.setSelected(i != null); setInputAndLabelEnabled(i != null); } @Override public String getValue() { if (guard.isSelected()) { return textField.getText(); } return null; } } protected static class ListGUIOption extends GUIOption<List<Object>> { private final JTextField textField; private final JList list; @SuppressWarnings("unchecked") protected ListGUIOption(Option<List<Object>> option) { super(option); if (option.getType() instanceof OptionTypes.EnumListType) { textField = null; final OptionTypes.EnumListType elt = (OptionTypes.EnumListType) option.getType(); final OptionTypes.EnumType et = (OptionTypes.EnumType) elt.elementOptionType; list = new JList(et.values); list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); list.setVisibleRowCount(Math.min(10, et.values.length)); list.setLayoutOrientation(JList.VERTICAL); final JScrollPane scrollPane = new JScrollPane(list); input = scrollPane; } else { list = null; textField = new JTextField(TEXT_FIELD_COLUMNS); input = textField; } setValue(option.getValue()); } @Override public void setValue(List<Object> objects) { if (objects != null) { if (textField == null) { this.list.clearSelection(); final int[] selectedIndices = new int[objects.size()]; int i = 0; for (Object value : objects) { final Enum enumValue = (Enum) value; selectedIndices[i++] = enumValue.ordinal(); } this.list.setSelectedIndices(selectedIndices); } else { textField.setText(String.valueOf(option.getType().unparseValue(objects))); } } guard.setSelected(objects != null); setInputAndLabelEnabled(objects != null); } @Override public List<Object> getValue() { if (guard.isSelected()) { if (textField == null) { final List<Object> result = new LinkedList<Object>(); // Stick to deprecated method until we EOL JDK 6 and before. for (Object value : this.list.getSelectedValues()) { result.add(value); } return null; } return option.getType().parseValue(textField.getText()); } return null; } } protected static class URLGUIOption extends GUIOption<URL> { private final JTextField textField; protected URLGUIOption(Option<URL> option) { super(option); textField = new JTextField(TEXT_FIELD_COLUMNS); input = textField; setValue(option.getValue()); } @Override public void setValue(URL i) { textField.setText(String.valueOf(i)); guard.setSelected(i != null); setInputAndLabelEnabled(i != null); } @Override public URL getValue() { if (guard.isSelected()) { try { return new URL(textField.getText()); } catch (MalformedURLException e) { return null; } } return null; } } protected static class EnumGUIOption extends GUIOption<Object> { private final JComboBox comboBox; @SuppressWarnings("unchecked") protected EnumGUIOption(Option<Object> option) { super(option); final OptionTypes.EnumType et = (OptionTypes.EnumType) option.getType(); comboBox = new JComboBox(et.values); input = comboBox; setValue(option.getValue()); } @Override public void setValue(Object value) { comboBox.setSelectedItem(value); guard.setSelected(true); } @Override public Object getValue() { return comboBox.getSelectedItem(); } } protected static class FileGUIOption extends GUIOption<File> { private final JTextField textField; private final JButton fileChooserButton; private JFileChooser fileChooser; protected FileGUIOption(Option<File> option) { super(option); textField = new JTextField(TEXT_FIELD_COLUMNS); final JPanel inputPanel = new JPanel(); input = inputPanel; fileChooserButton = new JButton(new AbstractAction("Select") { public void actionPerformed(ActionEvent e) { if (fileChooser == null) { fileChooser = new JFileChooser(new File(".")); fileChooser.setCurrentDirectory(new File(System.getProperty("user.home"))); fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); } final File file = new File(textField.getText()); if (file.exists()) { final File parent = file.getParentFile(); if (parent != null && parent.isDirectory()) { fileChooser.setCurrentDirectory(parent); } fileChooser.ensureFileIsVisible(file); fileChooser.setSelectedFile(file); } if (fileChooser.showDialog(fileChooserButton, "Select") == JFileChooser.APPROVE_OPTION) { textField.setText(fileChooser.getSelectedFile().getAbsolutePath()); } } }); inputPanel.add(textField); inputPanel.add(fileChooserButton); setValue(option.getValue()); } @Override public void setValue(File i) { if (i != null) { if (fileChooser != null) { fileChooser.setSelectedFile(i); } textField.setText(i.getAbsolutePath()); } guard.setSelected(i != null); setInputAndLabelEnabled(i != null); } @Override public File getValue() { return new File(textField.getText()); } } protected static class PackageGUIOption extends GUIOption<String> { private final JComboBox values; @SuppressWarnings("unchecked") protected PackageGUIOption(Option<String> option) { super(option); final PackageOptionType type = (PackageOptionType) option.getType(); final Set<String> pkgNames = new TreeSet<String>(); final String root = type.superPackage; new ClassSearch() { @Override protected boolean visitClass(String className) { if (className.startsWith(root)) { pkgNames.add(Classes.getPackageName(className)); } return true; } }.run(Classpath.fromSystem(), root.replace('.', '/')); this.values = new JComboBox(pkgNames.toArray(new String[pkgNames.size()])); input = this.values; // The combo box must be editable as the prepopulated items are just those packages found from the super package this.values.setEditable(true); setValue(option.getValue()); } @Override public String getValue() { return option.getType().parseValue(values.getSelectedItem().toString()); } @Override public void setValue(String p) { if (p != null) { values.setSelectedItem(p); guard.setSelected(true); } else { values.setSelectedIndex(-1); guard.setSelected(false); } } } private static GUIOption createGUIOption(Option<?> option) { final Option.Type<?> type = option.getType(); if (type == OptionTypes.BOOLEAN_TYPE) { final Option<Boolean> opt = OptionTypes.BOOLEAN_TYPE.cast(option); return new BooleanGUIOption(opt); } if (type == OptionTypes.INT_TYPE) { final Option<Integer> opt = OptionTypes.INT_TYPE.cast(option); return new IntegerGUIOption(opt); } if (type == OptionTypes.STRING_TYPE) { final Option<String> opt = OptionTypes.STRING_TYPE.cast(option); return new StringGUIOption(opt); } if (type == OptionTypes.URL_TYPE) { final Option<URL> opt = OptionTypes.URL_TYPE.cast(option); return new URLGUIOption(opt); } if (type == OptionTypes.FILE_TYPE) { final Option<File> opt = OptionTypes.FILE_TYPE.cast(option); return new FileGUIOption(opt); } if (type instanceof OptionTypes.EnumType) { final Option<Object> opt = Utils.cast(option); return new EnumGUIOption(opt); } if (type instanceof OptionTypes.ListType) { final Option<List<Object>> opt = Utils.cast(option); return new ListGUIOption(opt); } if (type instanceof PackageOptionType) { final Option<String> opt = Utils.cast(option); return new PackageGUIOption(opt); } return null; } private boolean cancelled; public OptionsDialog(JFrame owner, final OptionSet optionSet) { super(owner, "Options Dialog", true); final LinkedList<GUIOption<?>> guiOptions = new LinkedList<GUIOption<?>>(); final Container contentPane = getContentPane(); contentPane.setLayout(new BorderLayout()); // Configure panel of options final JPanel optionsPanel = new JPanel(); optionsPanel.setLayout(new SpringLayout()); optionsPanel.setBorder(BorderFactory.createTitledBorder("")); for (final Option<?> programOption : optionSet.getOptions()) { final GUIOption guiOption = createGUIOption(programOption); if (guiOption != null) { guiOptions.add(guiOption); final JCheckBox enabled = guiOption.getGuard(); final JLabel label = guiOption.getLabel(); label.setToolTipText(programOption.getHelp()); optionsPanel.add(enabled); optionsPanel.add(label); optionsPanel.add(guiOption.getInputComponent()); } } SpringUtilities.makeCompactGrid(optionsPanel, guiOptions.size(), 3, 0, 0, 5, 5); contentPane.add(optionsPanel, BorderLayout.CENTER); // Configure buttons final JButton ok = new JButton(new AbstractAction("OK") { public void actionPerformed(ActionEvent e) { for (GUIOption guiOption : guiOptions) { try { guiOption.commitOption(); } catch (Option.Error e1) { showErrorDialog(guiOption, e1); return; } } OptionsDialog.this.setVisible(false); } }); final JButton cancel = new JButton(new AbstractAction("Use Defaults") { public void actionPerformed(ActionEvent e) { OptionsDialog.this.setVisible(false); OptionsDialog.this.cancelled = true; } }); final JButton exit = new JButton(new AbstractAction("Cancel") { public void actionPerformed(ActionEvent e) { System.exit(1); } }); ok.setToolTipText("Apply options as configured in this dialog and ignore the command line arguments."); cancel.setToolTipText("Ignore options as configured in this dialog and use the command line arguments instead."); exit.setToolTipText("Stop running the program [calls System.exit()]."); final JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); buttonPanel.add(ok); buttonPanel.add(cancel); buttonPanel.add(exit); contentPane.add(buttonPanel, BorderLayout.SOUTH); pack(); // Places dialog in the middle of the screen if 'owner == null' setLocationRelativeTo(owner); } private class ErrorDialog extends JDialog { ErrorDialog(JDialog owner, GUIOption guiOption, Option.Error error) { super(owner, "Option Error", true); final JPanel errorPanel = new JPanel(); errorPanel.setLayout(new SpringLayout()); errorPanel.setBorder(BorderFactory.createTitledBorder("")); final Container contentPane = getContentPane(); contentPane.setLayout(new BorderLayout()); // Configure buttons final JButton ok = new JButton(new AbstractAction("OK") { public void actionPerformed(ActionEvent e) { errorPanel.setVisible(false); } }); final JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); buttonPanel.add(ok); final JLabel textPane = new JLabel(); textPane.setText("Invalid value for " + guiOption.option.getType().getTypeName() + " option \"-" + guiOption.option.getName() + "\""); contentPane.add(textPane); contentPane.add(buttonPanel, BorderLayout.SOUTH); pack(); // Places dialog in the middle of the screen if 'owner == null' setLocationRelativeTo(owner); } } public void showErrorDialog(GUIOption guiOption, Option.Error error) { final String typeName = guiOption.option.getType().getTypeName(); JOptionPane.showMessageDialog(this, "Error in option \"-" + guiOption.option.getName() + "\" of type " + typeName + "\n" + error.getMessage()); // new ErrorDialog(this, guiOption, error).setVisible(true); } /** * Creates a dialog for displaying a GUI for a given option set. * * @return true if the user wants to use the options as configured by the dialog, false if the caller of this method * should defer to parsing an argument array instead */ public static boolean show(JFrame owner, OptionSet optionSet) { final OptionsDialog programOptionDialog = new OptionsDialog(owner, optionSet); programOptionDialog.setVisible(true); programOptionDialog.dispose(); return !programOptionDialog.cancelled; } }