summaryrefslogtreecommitdiff
path: root/src/de/lmu/ifi/dbs/elki/gui/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/gui/util')
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/ClassTree.java211
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/DynamicParameters.java8
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/LogPanel.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/ParameterTable.java461
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/ParametersModel.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/SavedSettingsFile.java2
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/TreePopup.java412
-rw-r--r--src/de/lmu/ifi/dbs/elki/gui/util/package-info.java2
9 files changed, 907 insertions, 195 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/ClassTree.java b/src/de/lmu/ifi/dbs/elki/gui/util/ClassTree.java
new file mode 100644
index 00000000..3071aa7e
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/ClassTree.java
@@ -0,0 +1,211 @@
+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) 2014
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+import java.util.HashMap;
+import java.util.List;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.MutableTreeNode;
+import javax.swing.tree.TreeNode;
+
+/**
+ * Build a tree of available classes for use in Swing UIs.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.has TreeNode
+ */
+public class ClassTree {
+ /**
+ * Build the class tree for a given set of choices.
+ *
+ * @param choices Class choices
+ * @param rootpkg Root package name (to strip / hide)
+ * @return Root node.
+ */
+ public static TreeNode build(List<Class<?>> choices, String rootpkg) {
+ MutableTreeNode root = new PackageNode(rootpkg, rootpkg);
+ HashMap<String, MutableTreeNode> lookup = new HashMap<>();
+ if(rootpkg != null) {
+ lookup.put(rootpkg, root);
+ }
+ lookup.put("de.lmu.ifi.dbs.elki", root);
+ lookup.put("", root);
+
+ // Use the shorthand version of class names.
+ String prefix = rootpkg != null ? rootpkg + "." : null;
+
+ for(Class<?> impl : choices) {
+ String name = impl.getName();
+ name = (prefix != null && name.startsWith(prefix)) ? name.substring(prefix.length()) : name;
+ MutableTreeNode c = new ClassNode(impl.getName().substring(impl.getPackage().getName().length() + 1), name);
+
+ MutableTreeNode p = null;
+ int l = name.lastIndexOf('.');
+ while(p == null) {
+ if(l < 0) {
+ p = root;
+ break;
+ }
+ String pname = name.substring(0, l);
+ p = lookup.get(pname);
+ if(p != null) {
+ break;
+ }
+ l = pname.lastIndexOf('.');
+ MutableTreeNode tmp = new PackageNode(l >= 0 ? pname.substring(l + 1) : pname, pname);
+ tmp.insert(c, 0);
+ c = tmp;
+ lookup.put(pname, tmp);
+ name = pname;
+ }
+ p.insert(c, p.getChildCount());
+ }
+ // Simplify tree, except for root node
+ for(int i = 0; i < root.getChildCount(); i++) {
+ MutableTreeNode c = (MutableTreeNode) root.getChildAt(i);
+ MutableTreeNode c2 = simplifyTree(c, null);
+ if(c != c2) {
+ root.remove(i);
+ root.insert(c2, i);
+ }
+ }
+ return root;
+ }
+
+ /**
+ * Simplify the tree.
+ *
+ * @param cur Current node
+ * @param prefix Prefix to add
+ * @return Replacement node
+ */
+ private static MutableTreeNode simplifyTree(MutableTreeNode cur, String prefix) {
+ if(cur instanceof PackageNode) {
+ PackageNode node = (PackageNode) cur;
+ if(node.getChildCount() == 1) {
+ String newprefix = (prefix != null) ? prefix + "." + (String) node.getUserObject() : (String) node.getUserObject();
+ cur = simplifyTree((MutableTreeNode) node.getChildAt(0), newprefix);
+ }
+ else {
+ if(prefix != null) {
+ node.setUserObject(prefix + "." + (String) node.getUserObject());
+ }
+ for(int i = 0; i < node.getChildCount(); i++) {
+ MutableTreeNode c = (MutableTreeNode) node.getChildAt(i);
+ MutableTreeNode c2 = simplifyTree(c, null);
+ if(c != c2) {
+ node.remove(i);
+ node.insert(c2, i);
+ }
+ }
+ }
+ }
+ else if(cur instanceof ClassNode) {
+ ClassNode node = (ClassNode) cur;
+ if(prefix != null) {
+ node.setUserObject(prefix + "." + (String) node.getUserObject());
+ }
+ }
+ return cur;
+ }
+
+ /**
+ * Tree node representing a single class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class PackageNode extends DefaultMutableTreeNode {
+ /**
+ * Serial version
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Class name.
+ */
+ private String pkgname;
+
+ /**
+ * Current class name.
+ *
+ * @param display Displayed name
+ * @param pkgname Actual class name
+ */
+ public PackageNode(String display, String pkgname) {
+ super(display);
+ this.pkgname = pkgname;
+ }
+
+ /**
+ * Return the package name.
+ *
+ * @return Package name
+ */
+ public String getPackageName() {
+ return pkgname;
+ }
+ }
+
+ /**
+ * Tree node representing a single class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class ClassNode extends DefaultMutableTreeNode {
+ /**
+ * Serial version
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Class name.
+ */
+ private String clsname;
+
+ /**
+ * Current class name.
+ *
+ * @param display Displayed name
+ * @param clsname Actual class name
+ */
+ public ClassNode(String display, String clsname) {
+ super(display);
+ this.clsname = clsname;
+ }
+
+ /**
+ * Return the class name.
+ *
+ * @return Class name
+ */
+ public String getClassName() {
+ return clsname;
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/DynamicParameters.java b/src/de/lmu/ifi/dbs/elki/gui/util/DynamicParameters.java
index d974a6d6..2836b33c 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/util/DynamicParameters.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/DynamicParameters.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) 2013
+ Copyright (C) 2014
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -28,12 +28,12 @@ import java.util.BitSet;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException;
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.TrackedParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.SerializedParameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.TrackParameters;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.StringParameter;
-import de.lmu.ifi.dbs.elki.utilities.pairs.Pair;
/**
* Wrapper around a set of parameters for ELKI, that may not yet be complete or
@@ -141,8 +141,8 @@ public class DynamicParameters {
*/
public synchronized void updateFromTrackParameters(TrackParameters track) {
parameters.clear();
- for (Pair<Object, Parameter<?>> p : track.getAllParameters()) {
- Parameter<?> option = p.getSecond();
+ for (TrackedParameter p : track.getAllParameters()) {
+ Parameter<?> option = p.getParameter();
String value = null;
if (option.isDefined()) {
if (option.tookDefaultValue()) {
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java b/src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java
index 4664b852..e19fd752 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/util/LogPane.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/LogPane.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) 2013
+ Copyright (C) 2014
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/LogPanel.java b/src/de/lmu/ifi/dbs/elki/gui/util/LogPanel.java
index 11080cc9..99cf50b2 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/util/LogPanel.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/LogPanel.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) 2013
+ Copyright (C) 2014
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
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 105242c0..aa94f3ef 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) 2013
+ Copyright (C) 2014
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -29,18 +29,19 @@ 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.awt.event.KeyListener;
import java.io.File;
import java.util.BitSet;
import javax.swing.AbstractCellEditor;
import javax.swing.Action;
import javax.swing.ActionMap;
+import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
+import javax.swing.Icon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComboBox;
@@ -50,11 +51,17 @@ 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;
import javax.swing.table.TableColumn;
-
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import de.lmu.ifi.dbs.elki.gui.icons.StockIcon;
+import de.lmu.ifi.dbs.elki.gui.util.ClassTree.ClassNode;
import de.lmu.ifi.dbs.elki.logging.LoggingUtil;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ClassListParameter;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ClassParameter;
@@ -134,6 +141,54 @@ public class ParameterTable extends JTable {
col1.setPreferredWidth(150);
TableColumn col2 = this.getColumnModel().getColumn(1);
col2.setPreferredWidth(650);
+ this.addKeyListener(new Handler());
+
+ // Increase row height, to make editors usable.
+ setRowHeight(getRowHeight() + 4);
+ }
+
+ /**
+ * Internal key listener.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ protected class Handler implements KeyListener {
+ @Override
+ public void keyTyped(KeyEvent e) {
+ // ignore
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if((e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) {
+ if(e.getKeyCode() == KeyEvent.VK_SPACE //
+ || e.getKeyCode() == KeyEvent.VK_ENTER //
+ || e.getKeyCode() == KeyEvent.VK_DOWN //
+ || e.getKeyCode() == KeyEvent.VK_KP_DOWN) {
+ final ParameterTable parent = ParameterTable.this;
+ if(!parent.isEditing()) {
+ int leadRow = parent.getSelectionModel().getLeadSelectionIndex();
+ int leadColumn = parent.getColumnModel().getSelectionModel().getLeadSelectionIndex();
+ parent.editCellAt(leadRow, leadColumn);
+ Component editorComponent = getEditorComponent();
+ // This is a hack, to make the content assist open immediately.
+ if(editorComponent instanceof DispatchingPanel) {
+ KeyListener[] l = ((DispatchingPanel) editorComponent).component.getKeyListeners();
+ for(KeyListener li : l) {
+ li.keyPressed(e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ // ignore
+ }
}
/**
@@ -156,16 +211,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());
@@ -180,22 +235,27 @@ 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 (!hasFocus) {
- if (row < parameters.size()) {
+ 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))) {
+ if((flags.get(DynamicParameters.BIT_INVALID))) {
c.setBackground(COLOR_SYNTAX_ERROR);
- } else if ((flags.get(DynamicParameters.BIT_SYNTAX_ERROR))) {
+ }
+ else if((flags.get(DynamicParameters.BIT_SYNTAX_ERROR))) {
c.setBackground(COLOR_SYNTAX_ERROR);
- } else if ((flags.get(DynamicParameters.BIT_INCOMPLETE))) {
+ }
+ else if((flags.get(DynamicParameters.BIT_INCOMPLETE))) {
c.setBackground(COLOR_INCOMPLETE);
- } else if ((flags.get(DynamicParameters.BIT_DEFAULT_VALUE))) {
+ }
+ else if((flags.get(DynamicParameters.BIT_DEFAULT_VALUE))) {
c.setBackground(COLOR_DEFAULT_VALUE);
- } else if ((flags.get(DynamicParameters.BIT_OPTIONAL))) {
+ }
+ else if((flags.get(DynamicParameters.BIT_OPTIONAL))) {
c.setBackground(COLOR_OPTIONAL);
- } else {
+ }
+ else {
c.setBackground(null);
}
}
@@ -209,7 +269,7 @@ public class ParameterTable extends JTable {
*
* @author Erich Schubert
*/
- private class DropdownEditor extends DefaultCellEditor {
+ private class DropdownEditor extends DefaultCellEditor implements KeyListener {
/**
* Serial Version
*/
@@ -236,6 +296,32 @@ public class ParameterTable extends JTable {
panel = new DispatchingPanel((JComponent) comboBox.getEditor().getEditorComponent());
panel.setLayout(new BorderLayout());
panel.add(comboBox, BorderLayout.CENTER);
+ comboBox.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3));
+ }
+
+ @Override
+ public void keyTyped(KeyEvent e) {
+ // Ignore
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if((e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) {
+ if(e.getKeyCode() == KeyEvent.VK_SPACE //
+ || e.getKeyCode() == KeyEvent.VK_ENTER //
+ || e.getKeyCode() == KeyEvent.VK_DOWN //
+ || e.getKeyCode() == KeyEvent.VK_KP_DOWN) {
+ if(!comboBox.isPopupVisible()) {
+ comboBox.showPopup();
+ e.consume();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ // Ignore
}
@Override
@@ -244,54 +330,40 @@ public class ParameterTable extends JTable {
comboBox.removeAllItems();
// Put the current value in first.
Object val = table.getValueAt(row, column);
- if (val != null && val instanceof String) {
+ if(val != null && val instanceof String) {
String sval = (String) val;
- if (sval.equals(DynamicParameters.STRING_OPTIONAL)) {
+ if(sval.equals(DynamicParameters.STRING_OPTIONAL)) {
sval = "";
}
- if (sval.startsWith(DynamicParameters.STRING_USE_DEFAULT)) {
+ if(sval.startsWith(DynamicParameters.STRING_USE_DEFAULT)) {
sval = "";
}
- if (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<?>) {
- ClassParameter<?> cp = (ClassParameter<?>) option;
- // For parameters with a default value, offer using the default
- // For optional parameters, offer not specifying them.
- if (cp.hasDefaultValue()) {
- comboBox.addItem(DynamicParameters.STRING_USE_DEFAULT + cp.getDefaultValueAsString());
- } else if (cp.isOptional()) {
- comboBox.addItem(DynamicParameters.STRING_OPTIONAL);
- }
- // Offer the shorthand version of class names.
- 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)) {
+ // for Flag parameters.
+ 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);
}
}
@@ -307,7 +379,7 @@ public class ParameterTable extends JTable {
*
* @author Erich Schubert
*/
- private class FileNameEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {
+ private class FileNameEditor extends AbstractCellEditor implements TableCellEditor, ActionListener, KeyListener {
/**
* Serial version number
*/
@@ -334,6 +406,11 @@ public class ParameterTable extends JTable {
int mode = FileDialog.LOAD;
/**
+ * Default path.
+ */
+ String defaultpath = (new File(".")).getAbsolutePath();
+
+ /**
* Constructor.
*/
public FileNameEditor() {
@@ -342,6 +419,8 @@ public class ParameterTable extends JTable {
panel.setLayout(new BorderLayout());
panel.add(textfield, BorderLayout.CENTER);
panel.add(button, BorderLayout.EAST);
+ textfield.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3));
+ textfield.addKeyListener(this);
}
/**
@@ -349,38 +428,47 @@ public class ParameterTable extends JTable {
*/
@Override
public void actionPerformed(ActionEvent e) {
- final FileDialog fc = new FileDialog(frame);
- fc.setDirectory((new File(".")).getAbsolutePath());
+ FileDialog fc = new FileDialog(frame);
+ fc.setDirectory(defaultpath);
fc.setMode(mode);
final String curr = textfield.getText();
- if (curr != null && curr.length() > 0) {
+ if(curr != null && curr.length() > 0) {
fc.setFile(curr);
}
fc.setVisible(true);
String filename = fc.getFile();
- if (filename != null) {
+ if(filename != null) {
textfield.setText(new File(fc.getDirectory(), filename).getPath());
}
+ fc.setVisible(false);
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();
}
+ @Override
+ public void keyTyped(KeyEvent e) {
+ // Ignore
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if((e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) {
+ if(e.getKeyCode() == KeyEvent.VK_SPACE //
+ || e.getKeyCode() == KeyEvent.VK_ENTER //
+ || e.getKeyCode() == KeyEvent.VK_DOWN //
+ || e.getKeyCode() == KeyEvent.VK_KP_DOWN) {
+ e.consume();
+ actionPerformed(new ActionEvent(e.getSource(), ActionEvent.ACTION_PERFORMED, "assist"));
+ }
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ // Ignore
+ }
+
/**
* Delegate getCellEditorValue to the text field.
*/
@@ -394,21 +482,16 @@ 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;
mode = FileParameter.FileType.INPUT_FILE.equals(fp.getFileType()) ? FileDialog.LOAD : FileDialog.SAVE;
- if (fp.isDefined()) {
+ if(fp.isDefined()) {
f = fp.getValue();
}
- if (f != null) {
- String fn = f.getPath();
- textfield.setText(fn);
- } else {
- textfield.setText("");
- }
+ textfield.setText(f != null ? f.getPath() : "");
}
}
textfield.requestFocus();
@@ -417,11 +500,11 @@ public class ParameterTable extends JTable {
}
/**
- * Editor for selecting input and output file and folders names
+ * Editor for choosing classes.
*
* @author Erich Schubert
*/
- private class ClassListEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {
+ private class ClassListEditor extends AbstractCellEditor implements TableCellEditor, ActionListener, KeyListener {
/**
* Serial version number
*/
@@ -443,30 +526,43 @@ public class ParameterTable extends JTable {
final JButton button = new JButton("+");
/**
- * The combobox we are abusing to produce the popup
+ * The popup menu.
*/
- final JComboBox<String> combo = new JComboBox<>();
+ final TreePopup popup;
/**
- * The popup menu.
+ * Tree model
*/
- final SuperPopup popup;
+ private TreeModel model;
+
+ /**
+ * Parameter we are currently editing.
+ */
+ private Parameter<?> option;
/**
* Constructor.
*/
public ClassListEditor() {
+ textfield.addKeyListener(this);
button.addActionListener(this);
- // So the first item doesn't get automatically selected
- combo.setEditable(true);
- combo.addActionListener(this);
- popup = new SuperPopup(combo);
+ model = new DefaultTreeModel(new DefaultMutableTreeNode());
+ popup = new TreePopup(model);
+ popup.getTree().setRootVisible(false);
+ popup.addActionListener(this);
+
+ Icon classIcon = StockIcon.getStockIcon(StockIcon.GO_NEXT);
+ Icon packageIcon = StockIcon.getStockIcon(StockIcon.PACKAGE);
+ TreePopup.Renderer renderer = (TreePopup.Renderer) popup.getTree().getCellRenderer();
+ renderer.setLeafIcon(classIcon);
+ renderer.setFolderIcon(packageIcon);
panel = new DispatchingPanel(textfield);
panel.setLayout(new BorderLayout());
panel.add(textfield, BorderLayout.CENTER);
panel.add(button, BorderLayout.EAST);
+ textfield.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3));
}
/**
@@ -474,80 +570,42 @@ 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) {
- String newClass = (String) combo.getSelectedItem();
- if (newClass != null && newClass.length() > 0) {
- String val = textfield.getText();
- if (val.equals(DynamicParameters.STRING_OPTIONAL)) {
- val = "";
- }
- if (val.startsWith(DynamicParameters.STRING_USE_DEFAULT)) {
- val = "";
- }
- if (val.length() > 0) {
- val = val + ClassListParameter.LIST_SEP + newClass;
- } else {
- val = newClass;
+ return;
+ }
+ if(e.getSource() == popup) {
+ if(e.getActionCommand() == TreePopup.ACTION_CANCELED) {
+ popup.setVisible(false);
+ textfield.requestFocus();
+ return;
+ }
+ TreePath path = popup.getTree().getSelectionPath();
+ final Object comp = (path != null) ? path.getLastPathComponent() : null;
+ if(comp instanceof ClassNode) {
+ ClassNode sel = (path != null) ? (ClassNode) comp : null;
+ String newClass = (sel != null) ? sel.getClassName() : null;
+ if(newClass != null && newClass.length() > 0) {
+ if(option instanceof ClassListParameter) {
+ String val = textfield.getText();
+ if(val.equals(DynamicParameters.STRING_OPTIONAL) //
+ || val.startsWith(DynamicParameters.STRING_USE_DEFAULT)) {
+ val = "";
+ }
+ val = (val.length() > 0) ? val + ClassListParameter.LIST_SEP + newClass : newClass;
+ textfield.setText(val);
+ }
+ else {
+ textfield.setText(newClass);
+ }
+ popup.setVisible(false);
+ fireEditingStopped();
+ textfield.requestFocus();
}
- textfield.setText(val);
- popup.hide();
}
- fireEditingStopped();
- } else {
- LoggingUtil.warning("Unrecognized action event in ClassListEditor: " + e);
- }
- }
-
- /**
- * Modified popup
- *
- * @author Erich Schubert
- *
- * @apiviz.exclude
- */
- class SuperPopup extends BasicComboPopup {
- /**
- * Serial version
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * Constructor.
- *
- * @param combo Combo box used for data storage.
- */
- public SuperPopup(JComboBox<String> combo) {
- super(combo);
- }
-
- /**
- * Show the menu on a particular panel.
- *
- * This code is mostly copied from
- * {@link BasicComboPopup#getPopupLocation}
- *
- * @param parent Parent element to show at.
- */
- public void show(JPanel parent) {
- Dimension popupSize = parent.getSize();
- Insets insets = getInsets();
-
- // reduce the width of the scrollpane by the insets so that the popup
- // is the same width as the combo box.
- popupSize.setSize(popupSize.width - (insets.right + insets.left), getPopupHeightForRowCount(comboBox.getMaximumRowCount()));
- Rectangle popupBounds = computePopupBounds(0, comboBox.getBounds().height, popupSize.width, popupSize.height);
- Dimension scrollSize = popupBounds.getSize();
-
- scroller.setMaximumSize(scrollSize);
- scroller.setPreferredSize(scrollSize);
- scroller.setMinimumSize(scrollSize);
-
- list.revalidate();
-
- super.show(parent, 0, parent.getBounds().height);
+ return;
}
+ LoggingUtil.warning("Unrecognized action event in ClassListEditor: " + e);
}
/**
@@ -558,36 +616,65 @@ public class ParameterTable extends JTable {
return textfield.getText();
}
+ @Override
+ public void keyTyped(KeyEvent e) {
+ // Ignore
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if((e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) {
+ if(e.getKeyCode() == KeyEvent.VK_SPACE //
+ || e.getKeyCode() == KeyEvent.VK_ENTER //
+ || e.getKeyCode() == KeyEvent.VK_DOWN //
+ || e.getKeyCode() == KeyEvent.VK_KP_DOWN) {
+ if(!popup.isVisible()) {
+ popup.show(ClassListEditor.this.panel);
+ e.consume();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ // Ignore
+ }
+
/**
* Apply the Editor for a selected option.
*/
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
- combo.removeAllItems();
- if (row < parameters.size()) {
- Parameter<?> option = parameters.getNode(row).param;
+ if(row < parameters.size()) {
+ this.option = parameters.getNode(row).param;
+ TreeNode root;
// 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()) {
- String name = impl.getName();
- if (name.startsWith(prefix)) {
- name = name.substring(prefix.length());
- }
- combo.addItem(name);
- }
+ root = ClassTree.build(cp.getKnownImplementations(), cp.getRestrictionClass().getPackage().getName());
+ button.setText("+");
+ }
+ else if(option instanceof ClassParameter<?>) {
+ ClassParameter<?> cp = (ClassParameter<?>) option;
+ root = ClassTree.build(cp.getKnownImplementations(), cp.getRestrictionClass().getPackage().getName());
+ button.setText("v");
+ }
+ else {
+ root = new DefaultMutableTreeNode();
}
- 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("");
}
+ popup.getTree().setModel(new DefaultTreeModel(root));
}
return panel;
}
@@ -642,14 +729,16 @@ public class ParameterTable extends JTable {
final JComboBox<String> combobox = new JComboBox<>();
combobox.setEditable(true);
this.dropdownEditor = new DropdownEditor(combobox);
- this.plaintextEditor = new DefaultCellEditor(new JTextField());
+ JTextField tf = new JTextField();
+ tf.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3));
+ this.plaintextEditor = new DefaultCellEditor(tf);
this.classListEditor = new ClassListEditor();
this.fileNameEditor = new FileNameEditor();
}
@Override
public Object getCellEditorValue() {
- if (activeEditor == null) {
+ if(activeEditor == null) {
return null;
}
return activeEditor.getCellEditorValue();
@@ -657,31 +746,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<?>) {
- activeEditor = dropdownEditor;
- return dropdownEditor.getTableCellEditorComponent(table, value, isSelected, row, column);
+ if(option instanceof ClassParameter<?>) {
+ activeEditor = classListEditor;
+ return classListEditor.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);
}
@@ -733,10 +822,10 @@ public class ParameterTable extends JTable {
InputMap map = component.getInputMap(condition);
ActionMap am = component.getActionMap();
- if (map != null && am != null && isEnabled()) {
+ if(map != null && am != null && isEnabled()) {
Object binding = map.get(ks);
Action action = (binding == null) ? null : am.get(binding);
- if (action != null) {
+ if(action != null) {
return SwingUtilities.notifyAction(action, ks, e, component, e.getModifiers());
}
}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/ParametersModel.java b/src/de/lmu/ifi/dbs/elki/gui/util/ParametersModel.java
index 42f0a1af..82a0ee74 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/util/ParametersModel.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/ParametersModel.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) 2013
+ Copyright (C) 2014
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/SavedSettingsFile.java b/src/de/lmu/ifi/dbs/elki/gui/util/SavedSettingsFile.java
index a99b7414..8e614b85 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/util/SavedSettingsFile.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/SavedSettingsFile.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) 2013
+ Copyright (C) 2014
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/TreePopup.java b/src/de/lmu/ifi/dbs/elki/gui/util/TreePopup.java
new file mode 100644
index 00000000..60ad0604
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/TreePopup.java
@@ -0,0 +1,412 @@
+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) 2014
+ Ludwig-Maximilians-Universität München
+ Lehr- und Forschungseinheit für Datenbanksysteme
+ ELKI Development Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GraphicsConfiguration;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.Icon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.border.LineBorder;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreeModel;
+
+/**
+ * Popup menu that contains a JTree.
+ *
+ * @author Erich Schubert
+ */
+public class TreePopup extends JPopupMenu {
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Action string for confirmed operations (enter or click).
+ */
+ public static final String ACTION_SELECTED = "selected";
+
+ /**
+ * Action string for canceled operations (escape button pressed).
+ */
+ public static final String ACTION_CANCELED = "canceled";
+
+ /**
+ * Tree.
+ */
+ protected JTree tree;
+
+ /**
+ * Scroll pane, containing the tree.
+ */
+ protected JScrollPane scroller;
+
+ /**
+ * Tree model.
+ */
+ private TreeModel model;
+
+ /**
+ * Event handler
+ */
+ private Handler handler = new Handler();
+
+ /**
+ * Border of the popup.
+ */
+ private static Border TREE_BORDER = new LineBorder(Color.BLACK, 1);
+
+ /**
+ * Constructor with an empty tree model.
+ *
+ * This needs to also add a root node, and therefore sets
+ * {@code getTree().setRootVisible(false)}.
+ */
+ public TreePopup() {
+ this(new DefaultTreeModel(new DefaultMutableTreeNode()));
+ tree.setRootVisible(false);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param model Tree model
+ */
+ public TreePopup(TreeModel model) {
+ super();
+ this.setName("TreePopup.popup");
+ this.model = model;
+
+ // UI construction of the popup.
+ tree = createTree();
+ scroller = createScroller();
+ configurePopup();
+ }
+
+ /**
+ * Creates the JList used in the popup to display the items in the combo box
+ * model. This method is called when the UI class is created.
+ *
+ * @return a <code>JList</code> used to display the combo box items
+ */
+ protected JTree createTree() {
+ JTree tree = new JTree(model);
+ tree.setName("TreePopup.tree");
+ tree.setFont(getFont());
+ tree.setForeground(getForeground());
+ tree.setBackground(getBackground());
+ tree.setBorder(null);
+ tree.setFocusable(true);
+ tree.addMouseListener(handler);
+ tree.addKeyListener(handler);
+ tree.setCellRenderer(new Renderer());
+ return tree;
+ }
+
+ /**
+ * Configure the popup display.
+ */
+ protected void configurePopup() {
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ setBorderPainted(true);
+ setBorder(TREE_BORDER);
+ setOpaque(false);
+ add(scroller);
+ setDoubleBuffered(true);
+ setFocusable(false);
+ }
+
+ /**
+ * Creates the scroll pane which houses the scrollable tree.
+ */
+ protected JScrollPane createScroller() {
+ JScrollPane sp = new JScrollPane(tree, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+ sp.setHorizontalScrollBar(null);
+ sp.setName("TreePopup.scrollPane");
+ sp.setFocusable(false);
+ sp.getVerticalScrollBar().setFocusable(false);
+ sp.setBorder(null);
+ return sp;
+ }
+
+ /**
+ * Access the tree contained.
+ *
+ * @return Tree
+ */
+ public JTree getTree() {
+ return tree;
+ }
+
+ /**
+ * Display the popup, attached to the given component.
+ *
+ * @param parent Parent component
+ */
+ public void show(Component parent) {
+ Dimension parentSize = parent.getSize();
+ Insets insets = getInsets();
+
+ // reduce the width of the scrollpane by the insets so that the popup
+ // is the same width as the combo box.
+ parentSize.setSize(parentSize.width - (insets.right + insets.left), 10 * parentSize.height);
+ Dimension scrollSize = computePopupBounds(parent, 0, getBounds().height, parentSize.width, parentSize.height).getSize();
+
+ scroller.setMaximumSize(scrollSize);
+ scroller.setPreferredSize(scrollSize);
+ scroller.setMinimumSize(scrollSize);
+
+ super.show(parent, 0, parent.getHeight());
+ tree.requestFocusInWindow();
+ }
+
+ protected Rectangle computePopupBounds(Component parent, int px, int py, int pw, int ph) {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ Rectangle screenBounds;
+
+ // Calculate the desktop dimensions relative to the combo box.
+ GraphicsConfiguration gc = parent.getGraphicsConfiguration();
+ Point p = new Point();
+ SwingUtilities.convertPointFromScreen(p, parent);
+ if(gc != null) {
+ Insets screenInsets = toolkit.getScreenInsets(gc);
+ screenBounds = gc.getBounds();
+ screenBounds.width -= (screenInsets.left + screenInsets.right);
+ screenBounds.height -= (screenInsets.top + screenInsets.bottom);
+ screenBounds.x += (p.x + screenInsets.left);
+ screenBounds.y += (p.y + screenInsets.top);
+ }
+ else {
+ screenBounds = new Rectangle(p, toolkit.getScreenSize());
+ }
+
+ Rectangle rect = new Rectangle(px, py, pw, ph);
+ if(py + ph > screenBounds.y + screenBounds.height && ph < screenBounds.height) {
+ rect.y = -rect.height;
+ }
+ return rect;
+ }
+
+ /**
+ * Register an action listener.
+ *
+ * @param listener Action listener
+ */
+ public void addActionListener(ActionListener listener) {
+ listenerList.add(ActionListener.class, listener);
+ }
+
+ /**
+ * Unregister an action listener.
+ *
+ * @param listener Action listener
+ */
+ public void removeActionListener(ActionListener listener) {
+ listenerList.remove(ActionListener.class, listener);
+ }
+
+ /**
+ * Notify action listeners.
+ *
+ * @param event the <code>ActionEvent</code> object
+ */
+ protected void fireActionPerformed(ActionEvent event) {
+ Object[] listeners = listenerList.getListenerList();
+ for(int i = listeners.length - 2; i >= 0; i -= 2) {
+ if(listeners[i] == ActionListener.class) {
+ ((ActionListener) listeners[i + 1]).actionPerformed(event);
+ }
+ }
+ }
+
+ /**
+ * Tree cell render.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public class Renderer extends JPanel implements TreeCellRenderer {
+ /**
+ * Serial version
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Label to render
+ */
+ JLabel label;
+
+ /**
+ * Colors
+ */
+ private Color selbg, defbg, selfg, deffg;
+
+ /**
+ * Icons
+ */
+ private Icon leafIcon, folderIcon;
+
+ /**
+ * Constructor.
+ */
+ protected Renderer() {
+ selbg = UIManager.getColor("Tree.selectionBackground");
+ defbg = UIManager.getColor("Tree.textBackground");
+ selfg = UIManager.getColor("Tree.selectionForeground");
+ deffg = UIManager.getColor("Tree.textForeground");
+
+ setLayout(new BorderLayout());
+ add(label = new JLabel("This should never be rendered."));
+ }
+
+ @Override
+ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+ label.setText((String) ((DefaultMutableTreeNode) value).getUserObject());
+ setForeground(selected ? selfg : deffg);
+ setBackground(selected ? selbg : defbg);
+ label.setIcon(leaf ? leafIcon : folderIcon);
+ setPreferredSize(new Dimension(1000, label.getPreferredSize().height));
+ return this;
+ }
+
+ /**
+ * Set the leaf icon
+ *
+ * @param leafIcon Leaf icon
+ */
+ public void setLeafIcon(Icon leafIcon) {
+ this.leafIcon = leafIcon;
+ }
+
+ /**
+ * Set the folder icon.
+ *
+ * @param folderIcon Folder icon
+ */
+ public void setFolderIcon(Icon folderIcon) {
+ this.folderIcon = folderIcon;
+ }
+ }
+
+ /**
+ * Event handler class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ protected class Handler implements MouseListener, KeyListener, FocusListener {
+ @Override
+ public void keyTyped(KeyEvent e) {
+ if(e.getKeyChar() == '\n') {
+ e.consume();
+ }
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if(e.getKeyCode() == KeyEvent.VK_ENTER) {
+ fireActionPerformed(new ActionEvent(TreePopup.this, ActionEvent.ACTION_PERFORMED, ACTION_SELECTED, e.getWhen(), e.getModifiers()));
+ e.consume();
+ return;
+ }
+ if(e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+ fireActionPerformed(new ActionEvent(TreePopup.this, ActionEvent.ACTION_PERFORMED, ACTION_CANCELED, e.getWhen(), e.getModifiers()));
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ if(e.getKeyCode() == KeyEvent.VK_ENTER) {
+ e.consume();
+ }
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if(e.getButton() == MouseEvent.BUTTON1) {
+ fireActionPerformed(new ActionEvent(TreePopup.this, ActionEvent.ACTION_PERFORMED, ACTION_SELECTED, e.getWhen(), e.getModifiers()));
+ }
+ // ignore
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ // ignore
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ // ignore
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ // ignore
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ // ignore
+ }
+
+ @Override
+ public void focusGained(FocusEvent e) {
+ // ignore
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ fireActionPerformed(new ActionEvent(TreePopup.this, ActionEvent.ACTION_PERFORMED, ACTION_CANCELED));
+ }
+ }
+}
diff --git a/src/de/lmu/ifi/dbs/elki/gui/util/package-info.java b/src/de/lmu/ifi/dbs/elki/gui/util/package-info.java
index 706f587a..55f3d259 100644
--- a/src/de/lmu/ifi/dbs/elki/gui/util/package-info.java
+++ b/src/de/lmu/ifi/dbs/elki/gui/util/package-info.java
@@ -5,7 +5,7 @@
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
-Copyright (C) 2013
+Copyright (C) 2014
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team