diff options
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/gui/util/ParameterTable.java')
-rw-r--r-- | src/de/lmu/ifi/dbs/elki/gui/util/ParameterTable.java | 299 |
1 files changed, 199 insertions, 100 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/ParameterTable.java b/src/de/lmu/ifi/dbs/elki/gui/util/ParameterTable.java index a28d3f43..105242c0 100644 --- a/src/de/lmu/ifi/dbs/elki/gui/util/ParameterTable.java +++ b/src/de/lmu/ifi/dbs/elki/gui/util/ParameterTable.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.gui.util; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -27,21 +27,29 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; +import java.awt.FileDialog; +import java.awt.Frame; import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; import java.io.File; import java.util.BitSet; import javax.swing.AbstractCellEditor; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.DefaultCellEditor; +import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComboBox; -import javax.swing.JFileChooser; +import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JTable; import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; import javax.swing.plaf.basic.BasicComboPopup; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellEditor; @@ -94,6 +102,11 @@ public class ParameterTable extends JTable { static final Color COLOR_DEFAULT_VALUE = new Color(0xDFDFDF); /** + * Containing frame. + */ + protected Frame frame; + + /** * The parameters we edit. */ protected DynamicParameters parameters; @@ -101,11 +114,13 @@ public class ParameterTable extends JTable { /** * Constructor * + * @param frame Containing frame * @param pm Parameter Model * @param parameters Parameter storage */ - public ParameterTable(ParametersModel pm, DynamicParameters parameters) { + public ParameterTable(Frame frame, ParametersModel pm, DynamicParameters parameters) { super(pm); + this.frame = frame; this.parameters = parameters; this.setPreferredScrollableViewportSize(new Dimension(800, 400)); this.setFillsViewportHeight(true); @@ -141,16 +156,16 @@ public class ParameterTable extends JTable { @Override public void setValue(Object value) { - if(value instanceof String) { + if (value instanceof String) { setText((String) value); setToolTipText(null); return; } - if(value instanceof DynamicParameters.Node) { + if (value instanceof DynamicParameters.Node) { Parameter<?> o = ((DynamicParameters.Node) value).param; // Simulate a tree using indentation - there is no JTreeTable AFAICT StringBuilder buf = new StringBuilder(); - for(int i = 1; i < ((DynamicParameters.Node) value).depth; i++) { + for (int i = 1; i < ((DynamicParameters.Node) value).depth; i++) { buf.append(' '); } buf.append(o.getOptionID().getName()); @@ -165,27 +180,24 @@ public class ParameterTable extends JTable { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - if(row < parameters.size()) { - BitSet flags = parameters.getNode(row).flags; - // TODO: don't hardcode black - maybe mix the other colors, too? - c.setForeground(Color.BLACK); - if((flags.get(DynamicParameters.BIT_INVALID))) { - c.setBackground(COLOR_SYNTAX_ERROR); - } - else if((flags.get(DynamicParameters.BIT_SYNTAX_ERROR))) { - c.setBackground(COLOR_SYNTAX_ERROR); - } - else if((flags.get(DynamicParameters.BIT_INCOMPLETE))) { - c.setBackground(COLOR_INCOMPLETE); - } - else if((flags.get(DynamicParameters.BIT_DEFAULT_VALUE))) { - c.setBackground(COLOR_DEFAULT_VALUE); - } - else if((flags.get(DynamicParameters.BIT_OPTIONAL))) { - c.setBackground(COLOR_OPTIONAL); - } - else { - c.setBackground(null); + if (!hasFocus) { + if (row < parameters.size()) { + BitSet flags = parameters.getNode(row).flags; + // TODO: don't hardcode black - maybe mix the other colors, too? + c.setForeground(Color.BLACK); + if ((flags.get(DynamicParameters.BIT_INVALID))) { + c.setBackground(COLOR_SYNTAX_ERROR); + } else if ((flags.get(DynamicParameters.BIT_SYNTAX_ERROR))) { + c.setBackground(COLOR_SYNTAX_ERROR); + } else if ((flags.get(DynamicParameters.BIT_INCOMPLETE))) { + c.setBackground(COLOR_INCOMPLETE); + } else if ((flags.get(DynamicParameters.BIT_DEFAULT_VALUE))) { + c.setBackground(COLOR_DEFAULT_VALUE); + } else if ((flags.get(DynamicParameters.BIT_OPTIONAL))) { + c.setBackground(COLOR_OPTIONAL); + } else { + c.setBackground(null); + } } } return c; @@ -204,75 +216,89 @@ public class ParameterTable extends JTable { private static final long serialVersionUID = 1L; /** + * We need a panel to ensure focusing. + */ + final JPanel panel; + + /** * Combo box to use */ - private final JComboBox comboBox; + private final JComboBox<String> comboBox; /** * Constructor. * * @param comboBox Combo box we're going to use */ - public DropdownEditor(JComboBox comboBox) { + public DropdownEditor(JComboBox<String> comboBox) { super(comboBox); this.comboBox = comboBox; + panel = new DispatchingPanel((JComponent) comboBox.getEditor().getEditorComponent()); + panel.setLayout(new BorderLayout()); + panel.add(comboBox, BorderLayout.CENTER); } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { - Component c = super.getTableCellEditorComponent(table, value, isSelected, row, column); // remove old contents comboBox.removeAllItems(); // Put the current value in first. Object val = table.getValueAt(row, column); - if(val != null) { - comboBox.addItem(val); - comboBox.setSelectedIndex(0); + if (val != null && val instanceof String) { + String sval = (String) val; + if (sval.equals(DynamicParameters.STRING_OPTIONAL)) { + sval = ""; + } + if (sval.startsWith(DynamicParameters.STRING_USE_DEFAULT)) { + sval = ""; + } + if (sval != "") { + comboBox.addItem(sval); + comboBox.setSelectedIndex(0); + } } - if(row < parameters.size()) { + if (row < parameters.size()) { Parameter<?> option = parameters.getNode(row).param; // We can do dropdown choices for class parameters - if(option instanceof ClassParameter<?>) { + if (option instanceof ClassParameter<?>) { ClassParameter<?> cp = (ClassParameter<?>) option; // For parameters with a default value, offer using the default // For optional parameters, offer not specifying them. - if(cp.hasDefaultValue()) { + if (cp.hasDefaultValue()) { comboBox.addItem(DynamicParameters.STRING_USE_DEFAULT + cp.getDefaultValueAsString()); - } - else if(cp.isOptional()) { + } else if (cp.isOptional()) { comboBox.addItem(DynamicParameters.STRING_OPTIONAL); } // Offer the shorthand version of class names. - for(Class<?> impl : cp.getKnownImplementations()) { + for (Class<?> impl : cp.getKnownImplementations()) { comboBox.addItem(ClassParameter.canonicalClassName(impl, cp.getRestrictionClass())); } } // and for Flag parameters. - else if(option instanceof Flag) { - if(!Flag.SET.equals(val)) { + else if (option instanceof Flag) { + if (!Flag.SET.equals(val)) { comboBox.addItem(Flag.SET); } - if(!Flag.NOT_SET.equals(val)) { + if (!Flag.NOT_SET.equals(val)) { comboBox.addItem(Flag.NOT_SET); } } // and for Enum parameters. - else if(option instanceof EnumParameter<?>) { + else if (option instanceof EnumParameter<?>) { EnumParameter<?> ep = (EnumParameter<?>) option; - for(String s : ep.getPossibleValues()) { - if(ep.hasDefaultValue() && ep.getDefaultValueAsString().equals(s)) { - if(!(DynamicParameters.STRING_USE_DEFAULT + ep.getDefaultValueAsString()).equals(val)) { + for (String s : ep.getPossibleValues()) { + if (ep.hasDefaultValue() && ep.getDefaultValueAsString().equals(s)) { + if (!(DynamicParameters.STRING_USE_DEFAULT + ep.getDefaultValueAsString()).equals(val)) { comboBox.addItem(DynamicParameters.STRING_USE_DEFAULT + s); } - } - else if(!s.equals(val)) { + } else if (!s.equals(val)) { comboBox.addItem(s); } } } // No completion for others } - return c; + return panel; } } @@ -290,7 +316,7 @@ public class ParameterTable extends JTable { /** * We need a panel to put our components on. */ - final JPanel panel = new JPanel(); + final JPanel panel; /** * Text field to store the name @@ -303,10 +329,16 @@ public class ParameterTable extends JTable { final JButton button = new JButton("..."); /** + * File selector mode. + */ + int mode = FileDialog.LOAD; + + /** * Constructor. */ public FileNameEditor() { button.addActionListener(this); + panel = new DispatchingPanel(textfield); panel.setLayout(new BorderLayout()); panel.add(textfield, BorderLayout.CENTER); panel.add(button, BorderLayout.EAST); @@ -317,19 +349,35 @@ public class ParameterTable extends JTable { */ @Override public void actionPerformed(ActionEvent e) { - final JFileChooser fc = new JFileChooser(new File(".")); + final FileDialog fc = new FileDialog(frame); + fc.setDirectory((new File(".")).getAbsolutePath()); + fc.setMode(mode); final String curr = textfield.getText(); if (curr != null && curr.length() > 0) { - fc.setSelectedFile(new File(curr)); + fc.setFile(curr); } - int returnVal = fc.showOpenDialog(button); - - if(returnVal == JFileChooser.APPROVE_OPTION) { - textfield.setText(fc.getSelectedFile().getPath()); - } - else { - // Do nothing on cancel. + fc.setVisible(true); + String filename = fc.getFile(); + if (filename != null) { + textfield.setText(new File(fc.getDirectory(), filename).getPath()); } + fc.dispose(); + textfield.requestFocus(); + + // Swing file chooser. Currently much worse on Linux/GTK. + // final JFileChooser fc = new JFileChooser(new File(".")); + // final String curr = textfield.getText(); + // if (curr != null && curr.length() > 0) { + // fc.setSelectedFile(new File(curr)); + // } + // int returnVal = fc.showOpenDialog(button); + // + // if(returnVal == JFileChooser.APPROVE_OPTION) { + // textfield.setText(fc.getSelectedFile().getPath()); + // } + // else { + // // Do nothing on cancel. + // } fireEditingStopped(); } @@ -346,23 +394,24 @@ public class ParameterTable extends JTable { */ @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { - if(row < parameters.size()) { + if (row < parameters.size()) { Parameter<?> option = parameters.getNode(row).param; - if(option instanceof FileParameter) { + if (option instanceof FileParameter) { FileParameter fp = (FileParameter) option; File f = null; - if(fp.isDefined()) { + mode = FileParameter.FileType.INPUT_FILE.equals(fp.getFileType()) ? FileDialog.LOAD : FileDialog.SAVE; + if (fp.isDefined()) { f = fp.getValue(); } - if(f != null) { + if (f != null) { String fn = f.getPath(); textfield.setText(fn); - } - else { + } else { textfield.setText(""); } } } + textfield.requestFocus(); return panel; } } @@ -381,7 +430,7 @@ public class ParameterTable extends JTable { /** * We need a panel to put our components on. */ - final JPanel panel = new JPanel(); + final JPanel panel; /** * Text field to store the name @@ -396,7 +445,7 @@ public class ParameterTable extends JTable { /** * The combobox we are abusing to produce the popup */ - final JComboBox combo = new JComboBox(); + final JComboBox<String> combo = new JComboBox<>(); /** * The popup menu. @@ -413,6 +462,8 @@ public class ParameterTable extends JTable { combo.addActionListener(this); popup = new SuperPopup(combo); + panel = new DispatchingPanel(textfield); + panel.setLayout(new BorderLayout()); panel.add(textfield, BorderLayout.CENTER); panel.add(button, BorderLayout.EAST); @@ -423,31 +474,28 @@ public class ParameterTable extends JTable { */ @Override public void actionPerformed(ActionEvent e) { - if(e.getSource() == button) { + if (e.getSource() == button) { popup.show(panel); - } - else if(e.getSource() == combo) { + } else if (e.getSource() == combo) { String newClass = (String) combo.getSelectedItem(); - if(newClass != null && newClass.length() > 0) { + if (newClass != null && newClass.length() > 0) { String val = textfield.getText(); - if(val.equals(DynamicParameters.STRING_OPTIONAL)) { + if (val.equals(DynamicParameters.STRING_OPTIONAL)) { val = ""; } - if(val.startsWith(DynamicParameters.STRING_USE_DEFAULT)) { + if (val.startsWith(DynamicParameters.STRING_USE_DEFAULT)) { val = ""; } - if(val.length() > 0) { + if (val.length() > 0) { val = val + ClassListParameter.LIST_SEP + newClass; - } - else { + } else { val = newClass; } textfield.setText(val); popup.hide(); } fireEditingStopped(); - } - else { + } else { LoggingUtil.warning("Unrecognized action event in ClassListEditor: " + e); } } @@ -470,7 +518,7 @@ public class ParameterTable extends JTable { * * @param combo Combo box used for data storage. */ - public SuperPopup(JComboBox combo) { + public SuperPopup(JComboBox<String> combo) { super(combo); } @@ -516,30 +564,28 @@ public class ParameterTable extends JTable { @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { combo.removeAllItems(); - if(row < parameters.size()) { + if (row < parameters.size()) { Parameter<?> option = parameters.getNode(row).param; // We can do dropdown choices for class parameters - if(option instanceof ClassListParameter<?>) { + if (option instanceof ClassListParameter<?>) { ClassListParameter<?> cp = (ClassListParameter<?>) option; // Offer the shorthand version of class names. String prefix = cp.getRestrictionClass().getPackage().getName() + "."; - for(Class<?> impl : cp.getKnownImplementations()) { + for (Class<?> impl : cp.getKnownImplementations()) { String name = impl.getName(); - if(name.startsWith(prefix)) { + if (name.startsWith(prefix)) { name = name.substring(prefix.length()); } combo.addItem(name); } } - if(option.isDefined()) { - if(option.tookDefaultValue()) { + if (option.isDefined()) { + if (option.tookDefaultValue()) { textfield.setText(DynamicParameters.STRING_USE_DEFAULT + option.getDefaultValueAsString()); - } - else { + } else { textfield.setText(option.getValueAsString()); } - } - else { + } else { textfield.setText(""); } } @@ -593,7 +639,7 @@ public class ParameterTable extends JTable { * Constructor. */ public AdjustingEditor() { - final JComboBox combobox = new JComboBox(); + final JComboBox<String> combobox = new JComboBox<>(); combobox.setEditable(true); this.dropdownEditor = new DropdownEditor(combobox); this.plaintextEditor = new DefaultCellEditor(new JTextField()); @@ -603,7 +649,7 @@ public class ParameterTable extends JTable { @Override public Object getCellEditorValue() { - if(activeEditor == null) { + if (activeEditor == null) { return null; } return activeEditor.getCellEditorValue(); @@ -611,31 +657,31 @@ public class ParameterTable extends JTable { @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { - if(value instanceof String) { + if (value instanceof String) { String s = (String) value; - if(s.startsWith(DynamicParameters.STRING_USE_DEFAULT)) { + if (s.startsWith(DynamicParameters.STRING_USE_DEFAULT)) { value = s.substring(DynamicParameters.STRING_USE_DEFAULT.length()); } } - if(row < parameters.size()) { + if (row < parameters.size()) { Parameter<?> option = parameters.getNode(row).param; - if(option instanceof Flag) { + if (option instanceof Flag) { activeEditor = dropdownEditor; return dropdownEditor.getTableCellEditorComponent(table, value, isSelected, row, column); } - if(option instanceof ClassListParameter<?>) { + if (option instanceof ClassListParameter<?>) { activeEditor = classListEditor; return classListEditor.getTableCellEditorComponent(table, value, isSelected, row, column); } - if(option instanceof ClassParameter<?>) { + if (option instanceof ClassParameter<?>) { activeEditor = dropdownEditor; return dropdownEditor.getTableCellEditorComponent(table, value, isSelected, row, column); } - if(option instanceof FileParameter) { + if (option instanceof FileParameter) { activeEditor = fileNameEditor; return fileNameEditor.getTableCellEditorComponent(table, value, isSelected, row, column); } - if(option instanceof EnumParameter<?>) { + if (option instanceof EnumParameter<?>) { activeEditor = dropdownEditor; return dropdownEditor.getTableCellEditorComponent(table, value, isSelected, row, column); } @@ -644,4 +690,57 @@ public class ParameterTable extends JTable { return plaintextEditor.getTableCellEditorComponent(table, value, isSelected, row, column); } } -}
\ No newline at end of file + + /** + * This is a panel that will dispatch keystrokes to a particular component. + * + * This makes the tabular GUI much more user friendly. + * + * @author Erich Schubert + * + * @apiviz.exclude + */ + private class DispatchingPanel extends JPanel { + /** + * Serial version. + */ + private static final long serialVersionUID = 1L; + + /** + * Component to dispatch to. + */ + protected JComponent component; + + /** + * Constructor. + * + * @param component Component to dispatch to. + */ + public DispatchingPanel(JComponent component) { + super(); + this.component = component; + setRequestFocusEnabled(true); + } + + @Override + public void addNotify() { + super.addNotify(); + component.requestFocus(); + } + + @Override + protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { + InputMap map = component.getInputMap(condition); + ActionMap am = component.getActionMap(); + + if (map != null && am != null && isEnabled()) { + Object binding = map.get(ks); + Action action = (binding == null) ? null : am.get(binding); + if (action != null) { + return SwingUtilities.notifyAction(action, ks, e, component, e.getModifiers()); + } + } + return false; + } + }; +} |