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) 2011
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);
}
}