package de.lmu.ifi.dbs.elki.gui.configurator; /* This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures Copyright (C) 2013 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 . */ import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.basic.BasicComboPopup; import de.lmu.ifi.dbs.elki.gui.icons.StockIcon; import de.lmu.ifi.dbs.elki.logging.LoggingUtil; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.ListParameterization; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.TrackParameters; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ClassListParameter; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter; /** * Provide a configuration panel to choose classes with the help of a dropdown. * Additionally, the classes can in turn have additional parameters. * * @author Erich Schubert * * @apiviz.uses ClassListParameter */ public class ClassListParameterConfigurator extends AbstractSingleParameterConfigurator> implements ActionListener, ChangeListener { final ConfiguratorPanel child; /** * We need a panel to put our components on. */ final JPanel panel; /** * Text field to store the name */ final JTextField textfield; /** * The button to open the file selector */ final JButton button; /** * The combobox we are abusing to produce the popup */ final JComboBox combo; /** * The popup menu. */ final SuperPopup popup; public ClassListParameterConfigurator(ClassListParameter cp, JComponent parent) { super(cp, parent); textfield = new JTextField(); textfield.setToolTipText(param.getShortDescription()); if(cp.isDefined() && !cp.tookDefaultValue()) { textfield.setText(cp.getValueAsString()); } textfield.setPreferredSize(new Dimension(400, textfield.getPreferredSize().height)); button = new JButton(StockIcon.getStockIcon(StockIcon.LIST_ADD)); button.setToolTipText(param.getShortDescription()); button.addActionListener(this); // So the first item doesn't get automatically selected combo = new JComboBox<>(); combo.setEditable(true); combo.setPrototypeDisplayValue(cp.getRestrictionClass().getSimpleName()); popup = new SuperPopup(combo); // fill dropdown menu { // 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); } } combo.addActionListener(this); // setup panel { panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.add(textfield, BorderLayout.CENTER); panel.add(button, BorderLayout.EAST); GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.weightx = 1.0; parent.add(panel, constraints); finishGridRow(); } // Child options { GridBagConstraints constraints = new GridBagConstraints(); constraints.gridwidth = GridBagConstraints.REMAINDER; constraints.fill = GridBagConstraints.HORIZONTAL; constraints.weightx = 1.0; constraints.insets = new Insets(0, 10, 0, 0); child = new ConfiguratorPanel(); child.addChangeListener(this); parent.add(child, constraints); } textfield.addActionListener(this); } @Override public void addParameter(Object owner, Parameter param, TrackParameters track) { child.addParameter(owner, param, track); } /** * Callback to show the popup menu */ @Override public void actionPerformed(ActionEvent e) { 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.length() > 0) { val = val + ClassListParameter.LIST_SEP + newClass; } else { val = newClass; } textfield.setText(val); popup.hide(); fireValueChanged(); } } else if(e.getSource() == textfield) { fireValueChanged(); } else { LoggingUtil.warning("actionPerformed triggered by unknown source: " + e.getSource()); } } // FIXME: Refactor - duplicate code. /** @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 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); } } @Override public void stateChanged(ChangeEvent e) { if(e.getSource() == child) { fireValueChanged(); } else { LoggingUtil.warning("stateChanged triggered by unknown source: " + e.getSource()); } } @Override public String getUserInput() { return textfield.getText(); } @Override public void appendParameters(ListParameterization params) { super.appendParameters(params); child.appendParameters(params); } }