001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032: package org.openjump.core.ui;
033:
034: import java.awt.event.ActionEvent;
035: import java.awt.event.ComponentEvent;
036: import java.util.Arrays;
037: import java.util.Collection;
038: import java.util.HashMap;
039: import java.util.Iterator;
040: import java.util.Vector;
041: import javax.swing.BorderFactory;
042: import javax.swing.Icon;
043: import javax.swing.JCheckBox;
044: import javax.swing.JComboBox;
045: import javax.swing.JComponent;
046: import javax.swing.JDialog;
047: import javax.swing.JLabel;
048: import javax.swing.JOptionPane;
049: import javax.swing.JPanel;
050: import javax.swing.JTextArea;
051: import javax.swing.JTextField;
052: import com.vividsolutions.jts.util.Assert;
053: import com.vividsolutions.jump.I18N;
054: import com.vividsolutions.jump.util.CollectionMap;
055: import com.vividsolutions.jump.workbench.model.Layer;
056: import com.vividsolutions.jump.workbench.model.LayerManager;
057: import com.vividsolutions.jump.workbench.plugin.EnableCheck;
058: import java.awt.*;
059: import com.vividsolutions.jump.workbench.ui.*;
060:
061: /**
062: * Flexible generic dialog for prompting the user
063: * to type in several values.
064: */
065: public class MultiInputDialogWithoutCancel extends JDialog {
066: public EnableCheck createDoubleCheck(final String fieldName) {
067: return new EnableCheck() {
068: public String check(JComponent component) {
069: try {
070: Double.parseDouble(getText(fieldName).trim());
071: return null;
072: } catch (NumberFormatException e) {
073: return "\""
074: + getText(fieldName).trim()
075: + "\" "
076: + I18N
077: .get("ui.MultiInputDialog.is-an-invalid-double")
078: + " (" + fieldName + ")";
079: }
080: }
081: };
082: }
083:
084: public EnableCheck createIntegerCheck(final String fieldName) {
085: return new EnableCheck() {
086: public String check(JComponent component) {
087: try {
088: Integer.parseInt(getText(fieldName).trim());
089: return null;
090: } catch (NumberFormatException e) {
091: return "\""
092: + getText(fieldName).trim()
093: + "\" "
094: + I18N
095: .get("ui.MultiInputDialog.is-an-invalid-integer")
096: + " (" + fieldName + ")";
097: }
098: }
099: };
100: }
101:
102: public EnableCheck createPositiveCheck(final String fieldName) {
103: return new EnableCheck() {
104: public String check(JComponent component) {
105: if (Double.parseDouble(getText(fieldName).trim()) > 0) {
106: return null;
107: }
108: return "\"" + getText(fieldName).trim() + "\" "
109: + I18N.get("ui.MultiInputDialog.must-be")
110: + " > 0 (" + fieldName + ")";
111: }
112: };
113: }
114:
115: public EnableCheck createNonNegativeCheck(final String fieldName) {
116: return new EnableCheck() {
117: public String check(JComponent component) {
118: if (Double.parseDouble(getText(fieldName).trim()) >= 0) {
119: return null;
120: }
121: return "\"" + getText(fieldName).trim() + "\" "
122: + I18N.get("ui.MultiInputDialog.must-be")
123: + " >= 0 (" + fieldName + ")";
124: }
125: };
126: }
127:
128: private final static int SIDEBAR_WIDTH = 150;
129: OKCancelPanel okCancelPanel = new OKCancelPanel();
130: GridBagLayout gridBagLayout2 = new GridBagLayout();
131: JPanel outerMainPanel = new JPanel();
132: private HashMap fieldNameToComponentMap = new HashMap();
133:
134: private JComponent getComponent(String fieldName) {
135: return (JComponent) fieldNameToComponentMap.get(fieldName);
136: }
137:
138: public JComboBox getComboBox(String fieldName) {
139: return (JComboBox) getComponent(fieldName);
140: }
141:
142: public JCheckBox getCheckBox(String fieldName) {
143: return (JCheckBox) getComponent(fieldName);
144: }
145:
146: public JComponent getLabel(String fieldName) {
147: return (JComponent) fieldNameToLabelMap.get(fieldName);
148: }
149:
150: private HashMap fieldNameToLabelMap = new HashMap();
151: private int rowCount = 0;
152: private LayerNameRenderer layerListCellRenderer = new LayerNameRenderer();
153: private CollectionMap fieldNameToEnableCheckListMap = new CollectionMap();
154: private BorderLayout borderLayout2 = new BorderLayout();
155: private JPanel imagePanel = new JPanel();
156: private GridBagLayout gridBagLayout3 = new GridBagLayout();
157: private JLabel imageLabel = new JLabel();
158: private JPanel mainPanel = new JPanel();
159: private GridBagLayout mainPanelGridBagLayout = new GridBagLayout();
160: private JPanel innerMainPanel = new JPanel();
161: private JPanel innerMainPanel2 = new JPanel();
162: private GridBagLayout gridBagLayout5 = new GridBagLayout();
163: private GridBagLayout gridBagLayout7 = new GridBagLayout();
164: private GridBagLayout gridBagLayout6 = new GridBagLayout();
165: private JTextArea descriptionTextArea = new JTextArea();
166: private JPanel strutPanel = new JPanel();
167: private JPanel currentMainPanel = innerMainPanel;
168: private JPanel verticalSeparatorPanel = new JPanel();
169:
170: /**
171: * @param frame the frame on which to make this dialog modal and centred
172: */
173: public MultiInputDialogWithoutCancel(final Frame frame,
174: String title, boolean modal) {
175: super (frame, title, modal);
176: try {
177: jbInit();
178: } catch (Exception ex) {
179: ex.printStackTrace();
180: }
181: imagePanel.setVisible(false);
182: descriptionTextArea.setText("");
183: imageLabel.setText("");
184: innerMainPanel2.setVisible(false);
185: verticalSeparatorPanel.setVisible(false);
186: //[sstein : 28.01.2006]
187: this .okCancelPanel.setCancelVisible(false);
188: }
189:
190: public MultiInputDialogWithoutCancel() {
191: this (null, "", false);
192: }
193:
194: public void setVisible(boolean visible) {
195: //Workaround for Java bug 4446522 " JTextArea.getPreferredSize()
196: //incorrect when line wrapping is used": call #pack twice [Jon Aquino]
197: pack();
198: pack();
199: GUIUtil.centreOnWindow(MultiInputDialogWithoutCancel.this );
200: super .setVisible(visible);
201: }
202:
203: public String getText(String fieldName) {
204: if (fieldNameToComponentMap.get(fieldName) instanceof JTextField) {
205: return ((JTextField) fieldNameToComponentMap.get(fieldName))
206: .getText();
207: }
208: if (fieldNameToComponentMap.get(fieldName) instanceof JComboBox) {
209: return ((JComboBox) fieldNameToComponentMap.get(fieldName))
210: .getSelectedItem().toString();
211: }
212: Assert.shouldNeverReachHere(fieldName);
213: return null;
214: }
215:
216: public boolean getBoolean(String fieldName) {
217: JCheckBox checkbox = (JCheckBox) fieldNameToComponentMap
218: .get(fieldName);
219: return checkbox.isSelected();
220: }
221:
222: public double getDouble(String fieldName) {
223: return Double.parseDouble(getText(fieldName).trim());
224: }
225:
226: public int getInteger(String fieldName) {
227: return Integer.parseInt(getText(fieldName).trim());
228: }
229:
230: public Layer getLayer(String fieldName) {
231: JComboBox comboBox = (JComboBox) fieldNameToComponentMap
232: .get(fieldName);
233: return (Layer) comboBox.getSelectedItem();
234: }
235:
236: public JTextField addTextField(String fieldName,
237: String initialValue, int approxWidthInChars,
238: EnableCheck[] enableChecks, String toolTipText) {
239: JTextField textField = new JTextField(initialValue,
240: approxWidthInChars);
241: addRow(fieldName, new JLabel(fieldName), textField,
242: enableChecks, toolTipText);
243: return textField;
244: }
245:
246: public JComboBox addComboBox(String fieldName, Object selectedItem,
247: Collection items, String toolTipText) {
248: JComboBox comboBox = new JComboBox(new Vector(items));
249: comboBox.setSelectedItem(selectedItem);
250: addRow(fieldName, new JLabel(fieldName), comboBox, null,
251: toolTipText);
252: return comboBox;
253: }
254:
255: public JLabel addLabel(String text) {
256: //Take advantage of #addRow's special rule for JLabels: they span all
257: //the columns of the GridBagLayout. [Jon Aquino]
258: JLabel lbl = new JLabel(text);
259: addRow(lbl);
260: return lbl;
261: }
262:
263: public void addRow(JComponent c) {
264: addRow("DUMMY", new JLabel(""), c, null, null);
265: }
266:
267: public void addSeparator() {
268: JPanel separator = new JPanel();
269: separator.setBackground(Color.black);
270: separator.setPreferredSize(new Dimension(1, 1));
271: addRow(separator);
272: }
273:
274: private JTextField addNumericField(String fieldName,
275: String initialValue, int approxWidthInChars,
276: EnableCheck[] enableChecks, String toolTipText) {
277: JTextField fld = addTextField(fieldName, initialValue,
278: approxWidthInChars, enableChecks, toolTipText);
279: fld.setHorizontalAlignment(JTextField.RIGHT);
280: return fld;
281: }
282:
283: public JTextField addIntegerField(String fieldName,
284: int initialValue, int approxWidthInChars, String toolTipText) {
285: return addNumericField(fieldName, String.valueOf(initialValue),
286: approxWidthInChars,
287: new EnableCheck[] { createIntegerCheck(fieldName) },
288: toolTipText);
289: }
290:
291: public JTextField addPositiveIntegerField(String fieldName,
292: int initialValue, int approxWidthInChars) {
293: return addNumericField(fieldName, String.valueOf(initialValue),
294: approxWidthInChars, new EnableCheck[] {
295: createIntegerCheck(fieldName),
296: createPositiveCheck(fieldName) }, null);
297: }
298:
299: public JTextField addDoubleField(String fieldName,
300: double initialValue, int approxWidthInChars) {
301: return addNumericField(fieldName, String.valueOf(initialValue),
302: approxWidthInChars,
303: new EnableCheck[] { createDoubleCheck(fieldName) },
304: null);
305: }
306:
307: public JTextField addDoubleField(String fieldName,
308: double initialValue, int approxWidthInChars,
309: String toolTipText) {
310: return addNumericField(fieldName, String.valueOf(initialValue),
311: approxWidthInChars,
312: new EnableCheck[] { createDoubleCheck(fieldName) },
313: toolTipText);
314: }
315:
316: public JTextField addPositiveDoubleField(String fieldName,
317: double initialValue, int approxWidthInChars) {
318: return addNumericField(fieldName, String.valueOf(initialValue),
319: approxWidthInChars, new EnableCheck[] {
320: createDoubleCheck(fieldName),
321: createPositiveCheck(fieldName) }, null);
322: }
323:
324: public JTextField addNonNegativeDoubleField(String fieldName,
325: double initialValue, int approxWidthInChars) {
326: return addNumericField(fieldName, String.valueOf(initialValue),
327: approxWidthInChars, new EnableCheck[] {
328: createDoubleCheck(fieldName),
329: createNonNegativeCheck(fieldName) }, null);
330: }
331:
332: public static void main(String[] args) {
333: MultiInputDialog d = new MultiInputDialog(null, "Title!", true);
334: d.addLabel("Yay!");
335: d.addLayerComboBox("LayerField", null, "ToolTip",
336: new LayerManager());
337: d.setVisible(true);
338: System.out.println(d.getLayer("LayerField"));
339: System.exit(0);
340: }
341:
342: public JComboBox addLayerComboBox(String fieldName,
343: Layer initialValue, LayerManager layerManager) {
344: return addLayerComboBox(fieldName, initialValue, null,
345: layerManager);
346: }
347:
348: public JComboBox addLayerComboBox(String fieldName,
349: Layer initialValue, String toolTipText,
350: LayerManager layerManager) {
351: return addLayerComboBox(fieldName, initialValue, toolTipText,
352: layerManager.getLayers());
353: }
354:
355: public JComboBox addEditableLayerComboBox(String fieldName,
356: Layer initialValue, String toolTipText,
357: LayerManager layerManager) {
358: return addLayerComboBox(fieldName, initialValue, toolTipText,
359: layerManager.getEditableLayers());
360: }
361:
362: public JComboBox addLayerComboBox(String fieldName,
363: Layer initialValue, String toolTipText, Collection layers) {
364: addComboBox(fieldName, initialValue, layers, toolTipText);
365: getComboBox(fieldName).setRenderer(layerListCellRenderer);
366: return getComboBox(fieldName);
367: }
368:
369: public JCheckBox addCheckBox(String fieldName, boolean initialValue) {
370: return addCheckBox(fieldName, initialValue, null);
371: }
372:
373: public JCheckBox addCheckBox(String fieldName,
374: boolean initialValue, String toolTipText) {
375: JCheckBox checkBox = new JCheckBox(fieldName, initialValue);
376: addRow(fieldName, new JLabel(""), checkBox, null, toolTipText);
377: return checkBox;
378: }
379:
380: public void setSideBarImage(Icon icon) {
381: //Add imageLabel only if #setSideBarImage is called. Otherwise the margin
382: //above the description will be too tall. [Jon Aquino]
383: imagePanel.add(imageLabel, new GridBagConstraints(0, 0, 1, 1,
384: 0.0, 0.0, GridBagConstraints.NORTHWEST,
385: GridBagConstraints.HORIZONTAL,
386: new Insets(10, 10, 0, 10), 0, 0));
387: imagePanel.setVisible(true);
388: imageLabel.setIcon(icon);
389: }
390:
391: public void setSideBarDescription(String description) {
392: imagePanel.setVisible(true);
393: descriptionTextArea.setText(description);
394: }
395:
396: public boolean wasOKPressed() {
397: return okCancelPanel.wasOKPressed();
398: }
399:
400: //Experience suggests that one should avoid using weights when using the
401: //GridBagLayout. I find that nonzero weights can cause layout bugs that are
402: //hard to track down. [Jon Aquino]
403: void jbInit() throws Exception {
404: verticalSeparatorPanel.setBackground(Color.black);
405: imageLabel.setText(I18N
406: .get("ui.MultiInputDialog.images-goes-here"));
407: descriptionTextArea.setOpaque(false);
408: okCancelPanel
409: .addActionListener(new java.awt.event.ActionListener() {
410: public void actionPerformed(ActionEvent e) {
411: okCancelPanel_actionPerformed(e);
412: }
413: });
414: this
415: .addComponentListener(new java.awt.event.ComponentAdapter() {
416: public void componentShown(ComponentEvent e) {
417: this _componentShown(e);
418: }
419: });
420: outerMainPanel.setLayout(gridBagLayout6);
421: outerMainPanel.setAlignmentX((float) 0.7);
422: this .setResizable(true);
423: this .getContentPane().setLayout(borderLayout2);
424: imagePanel.setBorder(BorderFactory.createEtchedBorder());
425: imagePanel.setLayout(gridBagLayout3);
426: mainPanel.setLayout(mainPanelGridBagLayout);
427: innerMainPanel.setLayout(gridBagLayout5);
428: innerMainPanel2.setLayout(gridBagLayout7);
429: descriptionTextArea.setEnabled(false);
430: descriptionTextArea.setEditable(false);
431: descriptionTextArea.setText(I18N
432: .get("ui.MultiInputDialog.description-goes-here"));
433: descriptionTextArea.setLineWrap(true);
434: descriptionTextArea.setWrapStyleWord(true);
435: strutPanel.setMaximumSize(new Dimension(SIDEBAR_WIDTH, 1));
436: strutPanel.setMinimumSize(new Dimension(SIDEBAR_WIDTH, 1));
437: strutPanel.setPreferredSize(new Dimension(SIDEBAR_WIDTH, 1));
438: verticalSeparatorPanel.setPreferredSize(new Dimension(1, 1));
439: this .getContentPane().add(okCancelPanel, BorderLayout.SOUTH);
440: this .getContentPane().add(outerMainPanel, BorderLayout.CENTER);
441: imagePanel.add(descriptionTextArea, new GridBagConstraints(0,
442: 1, 1, 1, 0.0, 1.0, GridBagConstraints.NORTHWEST,
443: GridBagConstraints.BOTH, new Insets(10, 10, 10, 10), 0,
444: 0));
445: imagePanel.add(strutPanel, new GridBagConstraints(0, 3, 1, 1,
446: 0.0, 0.0, GridBagConstraints.CENTER,
447: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
448: 0, 0));
449: outerMainPanel.add(mainPanel, new GridBagConstraints(2, 0, 1,
450: 1, 1.0, 1.0, GridBagConstraints.NORTHWEST,
451: GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
452: mainPanel.add(innerMainPanel, new GridBagConstraints(1, 0, 1,
453: 2, 1.0, 1.0, GridBagConstraints.NORTHWEST,
454: GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10,
455: 10), 0, 0));
456: mainPanel.add(innerMainPanel2, new GridBagConstraints(3, 0, 1,
457: 1, 1.0, 1.0, GridBagConstraints.NORTHWEST,
458: GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10,
459: 10), 0, 0));
460: mainPanel.add(verticalSeparatorPanel, new GridBagConstraints(2,
461: 0, 1, 1, 0.0, 1.0, GridBagConstraints.CENTER,
462: GridBagConstraints.VERTICAL, new Insets(0, 0, 0, 0), 0,
463: 0));
464: outerMainPanel.add(imagePanel, new GridBagConstraints(1, 0, 1,
465: 1, 0.0, 0.0, GridBagConstraints.NORTHWEST,
466: GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
467: descriptionTextArea.setFont(imageLabel.getFont());
468: descriptionTextArea.setDisabledTextColor(imageLabel
469: .getForeground());
470: }
471:
472: /**
473: * If the dialog contains a single tabbed panel, it looks better to have a 0
474: * inset.
475: */
476: public void setInset(int inset) {
477: setInset(inset, innerMainPanel);
478: setInset(inset, innerMainPanel2);
479: }
480:
481: private void setInset(int inset, JComponent component) {
482: GridBagLayout layout = (GridBagLayout) component.getParent()
483: .getLayout();
484: GridBagConstraints constraints = layout
485: .getConstraints(component);
486: constraints.insets = new Insets(inset, inset, inset, inset);
487: layout.setConstraints(component, constraints);
488: }
489:
490: void okCancelPanel_actionPerformed(ActionEvent e) {
491: if (!okCancelPanel.wasOKPressed() || isInputValid()) {
492: setVisible(false);
493: return;
494: }
495: reportValidationError(firstValidationErrorMessage());
496: }
497:
498: void this _componentShown(ComponentEvent e) {
499: okCancelPanel.setOKPressed(false);
500: }
501:
502: private boolean isInputValid() {
503: return firstValidationErrorMessage() == null;
504: }
505:
506: private void reportValidationError(String errorMessage) {
507: JOptionPane.showMessageDialog(this , errorMessage, "JUMP",
508: JOptionPane.ERROR_MESSAGE);
509: }
510:
511: private String firstValidationErrorMessage() {
512: for (Iterator i = fieldNameToEnableCheckListMap.keySet()
513: .iterator(); i.hasNext();) {
514: String fieldName = (String) i.next();
515: for (Iterator j = fieldNameToEnableCheckListMap.getItems(
516: fieldName).iterator(); j.hasNext();) {
517: EnableCheck enableCheck = (EnableCheck) j.next();
518: String message = enableCheck.check(null);
519: if (message != null) {
520: return message;
521: }
522: }
523: }
524: return null;
525: }
526:
527: /**
528: * This method can be called once only.
529: */
530: public void startNewColumn() {
531: if (innerMainPanel2.isVisible()) {
532: Assert
533: .shouldNeverReachHere("#startNewColumn can be called once only");
534: }
535: currentMainPanel = innerMainPanel2;
536: innerMainPanel2.setVisible(true);
537: verticalSeparatorPanel.setVisible(true);
538: }
539:
540: public void addRow(String fieldName, JComponent label,
541: JComponent component, EnableCheck[] enableChecks,
542: String toolTipText) {
543: if (toolTipText != null) {
544: label.setToolTipText(toolTipText);
545: }
546: fieldNameToLabelMap.put(fieldName, label);
547: fieldNameToComponentMap.put(fieldName, component);
548: if (enableChecks != null) {
549: addEnableChecks(fieldName, Arrays.asList(enableChecks));
550: }
551: int componentX;
552: int componentWidth;
553: int labelX;
554: int labelWidth;
555: if (component instanceof JCheckBox
556: || component instanceof JLabel
557: || component instanceof JPanel) {
558: componentX = 1;
559: componentWidth = 3;
560: labelX = 4;
561: labelWidth = 1;
562: } else {
563: labelX = 1;
564: labelWidth = 1;
565: componentX = 2;
566: componentWidth = 1;
567: }
568: currentMainPanel.add(label, new GridBagConstraints(labelX,
569: rowCount, labelWidth, 1, 0.0, 0.0,
570: GridBagConstraints.WEST, GridBagConstraints.NONE,
571: new Insets(0, 0, 5, 10), 0, 0));
572: //HORIZONTAL especially needed by separator. [Jon Aquino]
573: currentMainPanel
574: .add(
575: component,
576: new GridBagConstraints(
577: componentX,
578: rowCount,
579: componentWidth,
580: 1,
581: 0,
582: 0.0,
583: GridBagConstraints.WEST,
584: component instanceof JPanel ? GridBagConstraints.HORIZONTAL
585: : GridBagConstraints.NONE,
586: new Insets(0, 0, 5, 0), 0, 0));
587: rowCount++;
588: }
589:
590: public void addEnableChecks(String fieldName,
591: Collection enableChecks) {
592: fieldNameToEnableCheckListMap.addItems(fieldName, enableChecks);
593: }
594:
595: public void indentLabel(String comboBoxFieldName) {
596: getLabel(comboBoxFieldName).setBorder(
597: BorderFactory.createMatteBorder(0,
598: (int) new JCheckBox().getPreferredSize()
599: .getWidth(), 0, 0, getLabel(
600: comboBoxFieldName).getBackground()));
601: }
602: }
|