/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* Copyright (C) 2003 Vivid Solutions
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.vividsolutions.jump.workbench.ui;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.*;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import com.vividsolutions.jts.util.Assert;
import com.vividsolutions.jump.feature.AttributeType;
import com.vividsolutions.jump.feature.FeatureSchema;
import com.vividsolutions.jump.I18N;
import com.vividsolutions.jump.util.CollectionMap;
import com.vividsolutions.jump.workbench.ui.GUIUtil;
import com.vividsolutions.jump.workbench.ui.LayerNameRenderer;
import com.vividsolutions.jump.workbench.ui.OKCancelPanel;
import com.vividsolutions.jump.workbench.model.Layer;
import com.vividsolutions.jump.workbench.model.LayerManager;
import com.vividsolutions.jump.workbench.plugin.EnableCheck;
/**
* Flexible generic dialog for prompting the user to type in several values.
* This Dialog is a refactoring of the previous vividsolutions MultiInputDialog
*/
public class MultiInputDialog extends AbstractMultiInputDialog {
// Main components of a MultiInputDialog
//|------------------------------------------------------------------------|
//| this.contentPane (BorderLayout) |
//| |--------------------------------------------------------------------| |
//| | CENTER dialogPanel (BorderLayout) | |
//| | |----------------------------------------------------------------| | |
//| | | WEST: | CENTER : mainComponent | | |
//| | | infoPanel | | | |
//| | | |-------------| | any container | | |
//| | | |label | | - default is simple JPanel | | |
//| | | |-------------| | - option double left/right panel | | |
//| | | |image | | - JSplitPanel | | |
//| | | | | | - JTabbedPane | | |
//| | | |-------------| | - Widget | | |
//| | | |description | | | | |
//| | | | | | | | |
//| | | |------------ | | | | |
//| | |----------------------------------------------------------------| | |
//| | | SOUTH : console | | |
//| | |----------------------------------------------------------------| | |
//| |--------------------------------------------------------------------| |
//| | SOUTH : OKCancelPanel | |
//| | | |
//| |--------------------------------------------------------------------| |
//|------------------------------------------------------------------------|
// dialogPanel contains everything but the OKCancelPanel
final private JPanel dialogPanel = new JPanel(new BorderLayout());
// imagePanel contains an image and a description
final private MultiInputDialogInfoPanel infoPanel = new MultiInputDialogInfoPanel();
// mainComponent contains all the components for user inputs
Container mainComponent;
JPanel currentPanel;
// consolePanel can show warnings or comments to help the user
final private MultiInputDialogConsole console = new MultiInputDialogConsole();
// This panel just contains the OK and the Cancel Buttons
final protected OKCancelPanel okCancelPanel = new OKCancelPanel();
private int inset = 0;
protected void setMainComponent() {
mainComponent = new JPanel(new GridBagLayout());
currentPanel = (JPanel)mainComponent;
Border mainComponentBorder = BorderFactory.createCompoundBorder(
BorderFactory.createEtchedBorder(),
BorderFactory.createEmptyBorder(5+inset, 5+inset, 5+inset, 5+inset)
);
currentPanel.setBorder(mainComponentBorder);
}
/**
* @return the JPanel where new Rows are added
*/
public JPanel getCurrentPanel() {
return currentPanel;
}
/**
* @param panel the JPanel where new Rows are added
*/
public void setCurrentPanel(JPanel panel) {
currentPanel = panel;
}
/**
* @return the MultiInputDialogConsole panel.
*/
public MultiInputDialogConsole getConsole() {
return console;
}
private int rowCount = 0;
/**
* @param frame the frame on which to make this dialog modal and centred
*/
public MultiInputDialog(final Frame frame, String title, boolean modal) {
super(frame, title, modal);
try {
jbInit();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public MultiInputDialog() {
this(null, "", false);
}
/**
* @param visible if true, the MultiInputDialog will be visible.
*/
public void setVisible(boolean visible) {
pack();
super.setVisible(visible);
}
/**
* @return the MultiInputDialogInfoPanel.
*/
public MultiInputDialogInfoPanel getDescriptionPanel() {
return infoPanel;
}
public void setSideBarImage(Icon icon) {
infoPanel.setIcon(icon);
pack();
}
public void setSideBarDescription(String description) {
infoPanel.setDescription(description);
pack();
}
public void showConsole() {
console.setVisible(true);
pack();
}
public void setCancelVisible(boolean cancelVisible) {
okCancelPanel.setCancelVisible(cancelVisible);
}
public boolean wasOKPressed() {
return okCancelPanel.wasOKPressed();
}
//Experience suggests that one should avoid using weights when using the
//GridBagLayout. I find that nonzero weights can cause layout bugs that are
//hard to track down. [Jon Aquino]
void jbInit() throws Exception {
okCancelPanel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
okCancelPanel_actionPerformed(e);
}
});
//LDB: set the default button for Enter to the OK for all
this.getRootPane().setDefaultButton(okCancelPanel.getButton("OK"));
setMainComponent();
this.setResizable(true);
this.getContentPane().add(dialogPanel, BorderLayout.CENTER);
dialogPanel.add(infoPanel, BorderLayout.WEST);
dialogPanel.add(mainComponent, BorderLayout.CENTER);
dialogPanel.add(console, BorderLayout.SOUTH);
console.setVisible(false);
this.getContentPane().add(okCancelPanel, BorderLayout.SOUTH);
}
/**
* Add a row to the current JPanel.
* The GridBagLayout is used as follows :
* <ul>
* <li>For JCheckBox, JRadioButton and JLabel and JPanel the component spreads
* over 3 columns. Empty label is in the fourth column.</li>
* <li>For JComboBox and JTextField, label is in first column and component in
* second column. Third and fourth column or empty.</li>
* </ul>
* |---|------------------------|----------------------|-----|---|
* | 0 | 1 | 2 | 3 | 4 |
* |---|-----------------------------------------------------|---|
* | | JChekBox with label associated | |
* |---|-----------------------------------------------------|---|
* | | JRadioButton with label associated | |
* |---|-----------------------------------------------------|---|
* | | JLabel | |
* |---|-----------------------------------------------------|---|
* | | JPanel | |
* |---|------------------------|----------------------------|---|
* | | Label | JComboBox | |
* |---|------------------------|----------------------------|---|
* | | Label | JTextField | |
* |---|------------------------|----------------------|-----|---|
/**
* Adds a row (containing either a control or a label) to the Dialog.
*
* @param fieldName field name of the control (used as a key)
* @param label label of the control
* @param component the control itself (may also be a label or a separator)
* @param enableChecks checks to validate inputs
* @param toolTipText
*/
public void addRow(String fieldName,
JComponent label,
JComponent component,
EnableCheck[] enableChecks,
String toolTipText,
int labelPos,
int fillMode) {
if (toolTipText != null) {
label.setToolTipText(toolTipText);
component.setToolTipText(toolTipText);
}
fieldNameToLabelMap.put(fieldName, label);
fieldNameToComponentMap.put(fieldName, component);
if (enableChecks != null) {
addEnableChecks(fieldName, Arrays.asList(enableChecks));
}
if (labelPos == NO_LABEL) {
currentPanel.add(component,
new GridBagConstraints(1, rowCount, 3, 1, 1.0, 0.0,
GridBagConstraints.WEST, fillMode,
new Insets(5+inset, 2+inset, 2+inset, 2+inset), 0, 0));
}
else if (labelPos == LEFT_LABEL) {
currentPanel.add(label,
new GridBagConstraints(1, rowCount, 1, 1, 0.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.NONE,
new Insets(2+inset, 2+inset, 2+inset, 2+inset), 0, 0));
currentPanel.add(component,
new GridBagConstraints(2, rowCount, 2, 1, 0.0, 0.0,
GridBagConstraints.WEST, fillMode,
new Insets(2+inset, 2+inset, 2+inset, 2+inset), 0, 0));
}
else if (labelPos == RIGHT_LABEL) {
currentPanel.add(component,
new GridBagConstraints(1, rowCount, 2, 1, 1.0, 0.0,
GridBagConstraints.WEST, fillMode,
new Insets(2+inset, 2+inset, 2+inset, 2+inset), 0, 0));
currentPanel.add(label,
new GridBagConstraints(3, rowCount, 1, 1, 0.0, 0.0,
GridBagConstraints.WEST, GridBagConstraints.NONE,
new Insets(2+inset, 2+inset, 2+inset, 2+inset), 0, 0));
}
rowCount++;
}
// Add a row with a component without label (ex. subtitle, checkbox))
public void addRow(String fieldName, JComponent component, EnableCheck[] enableChecks, String toolTipText) {
addRow(fieldName, null, component, enableChecks, toolTipText, NO_LABEL, HORIZONTAL);
}
// Add a row with a label on the left, and a component on the right (layer, textfield,...)
public void addRow(String fieldName, JLabel label, JTextField component, EnableCheck[] enableChecks, String toolTipText) {
addRow(fieldName, label, component, enableChecks, toolTipText, LEFT_LABEL, NONE);
}
// Add a row with a label on the left, and a component on the right (layer, textfield,...)
public void addRow(String fieldName, JLabel label, JComponent component, EnableCheck[] enableChecks, String toolTipText) {
addRow(fieldName, label, component, enableChecks, toolTipText, LEFT_LABEL, NONE);
}
public void addRow(JComponent component) {
addRow("DUMMY", new JLabel(""), component, null, "", NO_LABEL, HORIZONTAL);
}
public void addRow() {
currentPanel.add(new JPanel(),
new GridBagConstraints(1, rowCount, 2, 1, 1.0, 1.0,
GridBagConstraints.WEST, BOTH, new Insets(2, 2, 2, 2), 0, 0));
}
public void setInset(int inset) {
this.inset = inset;
try {
jbInit();
} catch (Exception ex) {
ex.printStackTrace();
}
}
void okCancelPanel_actionPerformed(ActionEvent e) {
if (!okCancelPanel.wasOKPressed() || isInputValid()) {
setVisible(false);
return;
}
reportValidationError(firstValidationErrorMessage());
}
void this_componentShown(ComponentEvent e) {
okCancelPanel.setOKPressed(false);
}
private boolean isInputValid() {
return firstValidationErrorMessage() == null;
}
private void reportValidationError(String errorMessage) {
JOptionPane.showMessageDialog(
this,
errorMessage,
"JUMP",
JOptionPane.ERROR_MESSAGE);
}
private String firstValidationErrorMessage() {
for (Iterator i = fieldNameToEnableCheckListMap.keySet().iterator();
i.hasNext();
) {
String fieldName = (String) i.next();
for (Iterator j =
fieldNameToEnableCheckListMap.getItems(fieldName).iterator();
j.hasNext();
) {
EnableCheck enableCheck = (EnableCheck) j.next();
String message = enableCheck.check(null);
if (message != null) {
return message;
}
}
}
return null;
}
/**
* Indent the label of a field with a MatteBorder having the width of
* a JCheckBox and the color of the component background.
* This helps to align JCheckBox label (text on the right of the CheckBox)
* with other component labels (text on the left of the component).
*
* @param fieldName the field to indent
*/
public void indentLabel(String fieldName) {
getLabel(fieldName).setBorder(
BorderFactory.createMatteBorder(
0,
(int) new JCheckBox().getPreferredSize().getWidth(),
0,
0,
getLabel(fieldName).getBackground()));
}
/**
* @deprecated
*/
public void startNewColumn() {
JOptionPane.showMessageDialog(
this,
"MultiInputDialog.startNewColumn() is deprecated,\n" +
"if you want to layout widgets on two panels,\n " +
"please, use the new DualPaneInputDialog class instead",
"OpenJUMP",
JOptionPane.ERROR_MESSAGE);
}
// Sample to test the class
public static void main(String[] args) {
final LayerManager lm = new LayerManager();
// Schema containing a single Geometry attribute
FeatureSchema fs1 = new FeatureSchema();
fs1.addAttribute("GEOMETRY", AttributeType.GEOMETRY);
com.vividsolutions.jump.feature.FeatureDataset ds1 =
new com.vividsolutions.jump.feature.FeatureDataset(fs1);
lm.addLayer("","LayerWithJustGeometry",ds1);
// Schema containing a Geometry and a String attributes
FeatureSchema fs2 = new FeatureSchema();
fs2.addAttribute("GEOMETRY", AttributeType.GEOMETRY);
fs2.addAttribute("Name", AttributeType.STRING);
com.vividsolutions.jump.feature.FeatureDataset ds2 =
new com.vividsolutions.jump.feature.FeatureDataset(fs2);
lm.addLayer("","LayerWithStringAttribute",ds2);
// Schema containing a Geometry, a String and a Integer attributes
FeatureSchema fs3 = new FeatureSchema();
fs3.addAttribute("GEOMETRY", AttributeType.GEOMETRY);
fs3.addAttribute("Name", AttributeType.STRING);
fs3.addAttribute("Age", AttributeType.INTEGER);
com.vividsolutions.jump.feature.FeatureDataset ds3 =
new com.vividsolutions.jump.feature.FeatureDataset(fs3);
lm.addLayer("","LayerWithNumericAttribute",ds3);
// MultiInputDialog usage demonstration
final MultiInputDialog d = new MultiInputDialog(null, "Title!", true);
d.setInset(2);
d.addSubTitle("Subtitle 1");
d.addLabel("This is just a label");
d.addTextField("Name", "", 24, null, "");
d.addPositiveIntegerField("Age", 0, 6, "");
d.addNonNegativeDoubleField("Salary", 0, 12, "");
d.addComboBox("Occupation", "Cadre", java.util.Arrays.asList("Manager","Developper","Technician","Secretary"), "");
d.indentLabel("Occupation");
d.addSubTitle("Layer and attribute selection");
AttributeTypeFilter STRING_FILTER = new AttributeTypeFilter(AttributeTypeFilter.STRING);
AttributeTypeFilter NUMERIC_FILTER = AttributeTypeFilter.NUMERIC_FILTER;
AttributeTypeFilter NOGEOM_FILTER = AttributeTypeFilter.NO_GEOMETRY_FILTER;
AttributeTypeFilter ALL_FILTER = AttributeTypeFilter.ALL_FILTER;
final JComboBox typeChooser = d.addComboBox("Choose Attribute Type", "ALL",
Arrays.asList(STRING_FILTER,NUMERIC_FILTER,ALL_FILTER,NOGEOM_FILTER), "");
final JComboBox layerChooser = d.addLayerComboBox("LayerField", null, "ToolTip", lm);
final JComboBox attributeChooser = d.addAttributeComboBox("Attribute field", "LayerField", NUMERIC_FILTER, "");
typeChooser.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
AttributeTypeFilter atf = (AttributeTypeFilter)typeChooser.getSelectedItem();
layerChooser.setModel(new DefaultComboBoxModel(atf.filter(lm).toArray(new Layer[0])));
}
});
d.addSeparator();
final JCheckBox jcb = d.addCheckBox("Display icon", false, "");
JButton button = d.addButton("Switch image panel");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (d.infoPanel.getDescription().equals("")) {
d.infoPanel.setDescription("Description of the dialog box.\nThis description must be helpful for the user. I must give meaningful information about which parameters are mandatory, optional, what they represent and which value they can take.");
d.getConsole().flashMessage("Add description");
}
else {
d.infoPanel.setDescription("");
d.getConsole().flashMessage("Remove description");
}
d.pack();
}
});
jcb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (jcb.isSelected()) {
d.infoPanel.setIcon(new ImageIcon(com.vividsolutions.jump.workbench.ui.images.IconLoader.class.getResource("Butt.gif")));
d.getConsole().flashMessage("Add image");
}
else {
d.infoPanel.setIcon(null);
d.getConsole().flashMessage("Remove image");
}
d.pack();
}
});
JButton button2 = d.addButton("Second button", "OK", "");
GUIUtil.centreOnScreen(d);
d.setVisible(true);
System.out.println(d.getLayer("LayerField"));
System.exit(0);
}
}