Back to index

wims  3.65+svn20090927
Query.java
Go to the documentation of this file.
00001 /* Query dialog.
00002 
00003  Copyright (c) 1998-2007 The Regents of the University of California.
00004  All rights reserved.
00005  Permission is hereby granted, without written agreement and without
00006  license or royalty fees, to use, copy, modify, and distribute this
00007  software and its documentation for any purpose, provided that the above
00008  copyright notice and the following two paragraphs appear in all copies
00009  of this software.
00010 
00011  IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
00012  FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
00013  ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
00014  THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
00015  SUCH DAMAGE.
00016 
00017  THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
00018  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00019  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
00020  PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
00021  CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
00022  ENHANCEMENTS, OR MODIFICATIONS.
00023 
00024  PT_COPYRIGHT_VERSION_2
00025  COPYRIGHTENDKEY
00026 
00027  */
00028 package ptolemy.gui;
00029 
00030 import java.awt.Color;
00031 import java.awt.Component;
00032 import java.awt.Dimension;
00033 import java.awt.FlowLayout;
00034 import java.awt.Font;
00035 import java.awt.GridBagConstraints;
00036 import java.awt.GridBagLayout;
00037 import java.awt.Insets;
00038 import java.awt.Toolkit;
00039 import java.awt.event.ActionEvent;
00040 import java.awt.event.ActionListener;
00041 import java.awt.event.FocusEvent;
00042 import java.awt.event.FocusListener;
00043 import java.awt.event.ItemEvent;
00044 import java.awt.event.ItemListener;
00045 import java.io.File;
00046 import java.io.IOException;
00047 import java.net.URI;
00048 import java.util.Enumeration;
00049 import java.util.HashMap;
00050 import java.util.HashSet;
00051 import java.util.Iterator;
00052 import java.util.Map;
00053 import java.util.NoSuchElementException;
00054 import java.util.Set;
00055 import java.util.StringTokenizer;
00056 import java.util.Vector;
00057 
00058 import javax.swing.BorderFactory;
00059 import javax.swing.Box;
00060 import javax.swing.BoxLayout;
00061 import javax.swing.ButtonGroup;
00062 import javax.swing.JButton;
00063 import javax.swing.JCheckBox;
00064 import javax.swing.JColorChooser;
00065 import javax.swing.JComboBox;
00066 import javax.swing.JComponent;
00067 import javax.swing.JFileChooser;
00068 import javax.swing.JLabel;
00069 import javax.swing.JPanel;
00070 import javax.swing.JPasswordField;
00071 import javax.swing.JRadioButton;
00072 import javax.swing.JScrollPane;
00073 import javax.swing.JSlider;
00074 import javax.swing.JTextArea;
00075 import javax.swing.JTextField;
00076 import javax.swing.JToggleButton;
00077 import javax.swing.ScrollPaneConstants;
00078 import javax.swing.event.ChangeEvent;
00079 import javax.swing.event.ChangeListener;
00080 import javax.swing.plaf.basic.BasicComboBoxEditor;
00081 
00082 // Avoid importing any packages from ptolemy.* here so that we
00083 // can ship Ptplot.
00086 
00105 public class Query extends JPanel {
00108     public Query() {
00109         _grid = new GridBagLayout();
00110         _constraints = new GridBagConstraints();
00111         _constraints.fill = GridBagConstraints.HORIZONTAL;
00112 
00113         // If the next line is commented out, then the PtolemyApplet
00114         // model parameters will have an entry that is less than one
00115         // character wide unless the window is made to be fairly large.
00116         _constraints.weightx = 1.0;
00117         _constraints.anchor = GridBagConstraints.NORTHWEST;
00118         _entryPanel.setLayout(_grid);
00119 
00120         // It's not clear whether the following has any real significance...
00121         // _entryPanel.setOpaque(true);
00122         setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
00123 
00124         _entryPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
00125 
00126         // Add a message panel into which a message can be placed using
00127         // setMessage().
00128         _messageArea = new JTextArea("");
00129         _messageArea.setFont(new Font("SansSerif", Font.PLAIN, 12));
00130         _messageArea.setEditable(false);
00131         _messageArea.setLineWrap(true);
00132         _messageArea.setWrapStyleWord(true);
00133 
00134         // It seems like setLineWrap is somewhat broken.  Really,
00135         // setLineWrap works best with scrollbars.  We have
00136         // a couple of choices: use scrollbars or hack in something
00137         // that guesses the number of lines.  Note that to
00138         // use scrollbars, the tutorial at
00139         // http://java.sun.com/docs/books/tutorial/uiswing/components/simpletext.html#textarea
00140         // suggests: "If you put a text area in a scroll pane, be
00141         // sure to set the scroll pane's preferred size or use a
00142         // text area constructor that sets the number of rows and
00143         // columns for the text area."
00144         _messageArea.setBackground(null);
00145 
00146         _messageArea.setAlignmentX(Component.LEFT_ALIGNMENT);
00147 
00148         _messageScrollPane = new JScrollPane(_messageArea);
00149         _messageScrollPane
00150                 .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
00151 
00152         // Get rid of the border.
00153         _messageScrollPane.setBorder(BorderFactory.createEmptyBorder());
00154         _messageScrollPane.getViewport().setBackground(null);
00155 
00156         // Useful for debugging:
00157         //_messageScrollPane.setBorder(
00158         //                    BorderFactory.createLineBorder(Color.pink));
00159         // We add the _messageScrollPane when we first use it.
00160         _entryScrollPane = new JScrollPane(_entryPanel);
00161 
00162         // Get rid of the border.
00163         _entryScrollPane.setBorder(BorderFactory.createEmptyBorder());
00164         _entryScrollPane.getViewport().setBackground(null);
00165         _entryScrollPane.setBackground(null);
00166         add(_entryScrollPane);
00167 
00168         // Setting the background to null allegedly means it inherits the
00169         // background color from the container.
00170         _entryPanel.setBackground(null);
00171     }
00172 
00175 
00181     public void addCheckBox(String name, String label, boolean defaultValue) {
00182         JLabel lbl = new JLabel(label + ": ");
00183         lbl.setBackground(_background);
00184 
00185         JCheckBox checkbox = new JCheckBox();
00186         checkbox.setBackground(_background);
00187         checkbox.setOpaque(false);
00188         checkbox.setSelected(defaultValue);
00189         _addPair(name, lbl, checkbox, checkbox);
00190 
00191         // Add the listener last so that there is no notification
00192         // of the first value.
00193         checkbox.addItemListener(new QueryItemListener(name));
00194     }
00195 
00202     public void addChoice(String name, String label, String[] values,
00203             String defaultChoice) {
00204         addChoice(name, label, values, defaultChoice, false);
00205     }
00206 
00215     public void addChoice(String name, String label, String[] values,
00216             String defaultChoice, boolean editable) {
00217         addChoice(name, label, values, defaultChoice, editable, Color.white,
00218                 Color.black);
00219     }
00220 
00231     public void addChoice(String name, String label, String[] values,
00232             String defaultChoice, boolean editable, final Color background,
00233             final Color foreground) {
00234         JLabel lbl = new JLabel(label + ": ");
00235         lbl.setBackground(_background);
00236 
00237         JComboBox combobox = new JComboBox(values);
00238         combobox.setEditable(editable);
00239 
00240         // NOTE: Typical of Swing, the following does not set
00241         // the background color. So we have to specify a
00242         // custom editor.  #$(#&$#(@#!!
00243         // combobox.setBackground(background);
00244         combobox.setEditor(new BasicComboBoxEditor() {
00245             public Component getEditorComponent() {
00246                 Component result = super.getEditorComponent();
00247                 result.setBackground(background);
00248                 result.setForeground(foreground);
00249                 return result;
00250             }
00251         });
00252         combobox.setSelectedItem(defaultChoice);
00253         _addPair(name, lbl, combobox, combobox);
00254 
00255         // Add the listener last so that there is no notification
00256         // of the first value.
00257         combobox.addItemListener(new QueryItemListener(name));
00258     }
00259 
00265     public void addColorChooser(String name, String label, String defaultColor) {
00266         JLabel lbl = new JLabel(label + ": ");
00267         lbl.setBackground(_background);
00268 
00269         QueryColorChooser colorChooser = new QueryColorChooser(name,
00270                 defaultColor);
00271         _addPair(name, lbl, colorChooser, colorChooser);
00272     }
00273 
00280     public void addDisplay(String name, String label, String theValue) {
00281         JLabel lbl = new JLabel(label + ": ");
00282         lbl.setBackground(_background);
00283 
00284         // NOTE: JLabel would be a reasonable choice here, but at
00285         // least in the current version of swing, JLabel.setText() does
00286         // not work.
00287         JTextArea displayField = new JTextArea(theValue, 1, 10);
00288         displayField.setEditable(false);
00289         displayField.setBackground(_background);
00290         _addPair(name, lbl, displayField, displayField);
00291     }
00292 
00303     public void addFileChooser(String name, String label, String defaultName,
00304             URI base, File startingDirectory) {
00305         addFileChooser(name, label, defaultName, base, startingDirectory, true,
00306                 false, Color.white, Color.black);
00307     }
00308 
00320     public void addFileChooser(String name, String label, String defaultName,
00321             URI base, File startingDirectory, boolean allowFiles,
00322             boolean allowDirectories) {
00323         addFileChooser(name, label, defaultName, base, startingDirectory,
00324                 allowFiles, allowDirectories, Color.white, Color.black);
00325     }
00326 
00337     public void addFileChooser(String name, String label, String defaultName,
00338             URI base, File startingDirectory, Color background, Color foreground) {
00339         addFileChooser(name, label, defaultName, base, startingDirectory, true,
00340                 false, background, foreground);
00341     }
00342 
00355     public void addFileChooser(String name, String label, String defaultName,
00356             URI base, File startingDirectory, boolean allowFiles,
00357             boolean allowDirectories, Color background, Color foreground) {
00358         JLabel lbl = new JLabel(label + ": ");
00359         lbl.setBackground(_background);
00360 
00361         QueryFileChooser fileChooser = new QueryFileChooser(name, defaultName,
00362                 base, startingDirectory, allowFiles, allowDirectories,
00363                 background, foreground);
00364         _addPair(name, lbl, fileChooser, fileChooser);
00365     }
00366 
00375     public void addLine(String name, String label, String defaultValue) {
00376         addLine(name, label, defaultValue, Color.white, Color.black);
00377     }
00378 
00389     public void addLine(String name, String label, String defaultValue,
00390             Color background, Color foreground) {
00391         JLabel lbl = new JLabel(label + ": ");
00392         lbl.setBackground(_background);
00393 
00394         JTextField entryBox = new JTextField(defaultValue, _width);
00395         entryBox.setBackground(background);
00396         entryBox.setForeground(foreground);
00397         _addPair(name, lbl, entryBox, entryBox);
00398 
00399         // Add the listener last so that there is no notification
00400         // of the first value.
00401         entryBox.addActionListener(new QueryActionListener(name));
00402 
00403         // Add a listener for loss of focus.  When the entry gains
00404         // and then loses focus, listeners are notified of an update,
00405         // but only if the value has changed since the last notification.
00406         // FIXME: Unfortunately, Java calls this listener some random
00407         // time after the window has been closed.  It is not even a
00408         // a queued event when the window is closed.  Thus, we have
00409         // a subtle bug where if you enter a value in a line, do not
00410         // hit return, and then click on the X to close the window,
00411         // the value is restored to the original, and then sometime
00412         // later, the focus is lost and the entered value becomes
00413         // the value of the parameter.  I don't know of any workaround.
00414         entryBox.addFocusListener(new QueryFocusListener(name));
00415     }
00416 
00428     public void addPassword(String name, String label, String defaultValue) {
00429         addPassword(name, label, defaultValue, Color.white, Color.black);
00430     }
00431 
00452     public void addPassword(String name, String label, String defaultValue,
00453             Color background, Color foreground) {
00454         JLabel lbl = new JLabel(label + ": ");
00455         lbl.setBackground(_background);
00456 
00457         JPasswordField entryBox = new JPasswordField(defaultValue, _width);
00458         entryBox.setBackground(background);
00459         entryBox.setForeground(foreground);
00460         _addPair(name, lbl, entryBox, entryBox);
00461 
00462         // Add the listener last so that there is no notification
00463         // of the first value.
00464         entryBox.addActionListener(new QueryActionListener(name));
00465 
00466         // Add a listener for loss of focus.  When the entry gains
00467         // and then loses focus, listeners are notified of an update,
00468         // but only if the value has changed since the last notification.
00469         // FIXME: Unfortunately, Java calls this listener some random
00470         // time after the window has been closed.  It is not even a
00471         // a queued event when the window is closed.  Thus, we have
00472         // a subtle bug where if you enter a value in a line, do not
00473         // hit return, and then click on the X to close the window,
00474         // the value is restored to the original, and then sometime
00475         // later, the focus is lost and the entered value becomes
00476         // the value of the parameter.  I don't know of any workaround.
00477         entryBox.addFocusListener(new QueryFocusListener(name));
00478     }
00479 
00494     public void addQueryListener(QueryListener listener) {
00495         if (_listeners == null) {
00496             _listeners = new Vector();
00497         }
00498 
00499         if (_listeners.contains(listener)) {
00500             return;
00501         }
00502 
00503         _listeners.add(listener);
00504     }
00505 
00513     public void addRadioButtons(String name, String label, String[] values,
00514             String defaultValue) {
00515         JLabel lbl = new JLabel(label + ": ");
00516         lbl.setBackground(_background);
00517 
00518         FlowLayout flow = new FlowLayout();
00519         flow.setAlignment(FlowLayout.LEFT);
00520 
00521         // This must be a JPanel, not a Panel, or the scroll bars won't work.
00522         JPanel buttonPanel = new JPanel(flow);
00523 
00524         ButtonGroup group = new ButtonGroup();
00525         QueryActionListener listener = new QueryActionListener(name);
00526 
00527         // Regrettably, ButtonGroup provides no method to find out
00528         // which button is selected, so we have to go through a
00529         // song and dance here...
00530         JRadioButton[] buttons = new JRadioButton[values.length];
00531 
00532         for (int i = 0; i < values.length; i++) {
00533             JRadioButton checkbox = new JRadioButton(values[i]);
00534             buttons[i] = checkbox;
00535             checkbox.setBackground(_background);
00536 
00537             // The following (essentially) undocumented method does nothing...
00538             // checkbox.setContentAreaFilled(true);
00539             checkbox.setOpaque(false);
00540 
00541             if (values[i].equals(defaultValue)) {
00542                 checkbox.setSelected(true);
00543             }
00544 
00545             group.add(checkbox);
00546             buttonPanel.add(checkbox);
00547 
00548             // Add the listener last so that there is no notification
00549             // of the first value.
00550             checkbox.addActionListener(listener);
00551         }
00552 
00553         _addPair(name, lbl, buttonPanel, buttons);
00554     }
00555 
00564     public void addSelectButtons(String name, String label, String[] values,
00565             Set initiallySelected) {
00566         JLabel lbl = new JLabel(label + ": ");
00567         lbl.setBackground(_background);
00568 
00569         FlowLayout flow = new FlowLayout();
00570         flow.setAlignment(FlowLayout.LEFT);
00571 
00572         // This must be a JPanel, not a Panel, or the scroll bars won't work.
00573         JPanel buttonPanel = new JPanel(flow);
00574 
00575         QueryActionListener listener = new QueryActionListener(name);
00576 
00577         if (initiallySelected == null) {
00578             initiallySelected = new HashSet();
00579         }
00580 
00581         JRadioButton[] buttons = new JRadioButton[values.length];
00582 
00583         for (int i = 0; i < values.length; i++) {
00584             JRadioButton checkbox = new JRadioButton(values[i]);
00585             buttons[i] = checkbox;
00586             checkbox.setBackground(_background);
00587 
00588             // The following (essentially) undocumented method does nothing...
00589             // checkbox.setContentAreaFilled(true);
00590             checkbox.setOpaque(false);
00591 
00592             if (initiallySelected.contains(values[i])) {
00593                 checkbox.setSelected(true);
00594             }
00595 
00596             buttonPanel.add(checkbox);
00597 
00598             // Add the listener last so that there is no notification
00599             // of the first value.
00600             checkbox.addActionListener(listener);
00601         }
00602 
00603         _addPair(name, lbl, buttonPanel, buttons);
00604     }
00605 
00616     public void addSlider(String name, String label, int defaultValue,
00617             int minimum, int maximum) throws IllegalArgumentException {
00618         JLabel lbl = new JLabel(label + ": ");
00619 
00620         if (minimum > maximum) {
00621             int temp = minimum;
00622             minimum = maximum;
00623             maximum = temp;
00624         }
00625 
00626         if ((defaultValue > maximum) || (defaultValue < minimum)) {
00627             throw new IllegalArgumentException("Desired default " + "value \""
00628                     + defaultValue + "\" does not fall "
00629                     + "between the minimum and maximum.");
00630         }
00631 
00632         JSlider slider = new JSlider(minimum, maximum, defaultValue);
00633         _addPair(name, lbl, slider, slider);
00634         slider.addChangeListener(new SliderListener(name));
00635     }
00636 
00642     public void addTextArea(String name, String label, String theValue) {
00643         addTextArea(name, label, theValue, Color.white, Color.black, _height,
00644                 _width);
00645     }
00646 
00654     public void addTextArea(String name, String label, String theValue,
00655             Color background, Color foreground) {
00656         addTextArea(name, label, theValue, background, foreground, _height,
00657                 _width);
00658     }
00659 
00670     public void addTextArea(String name, String label, String theValue,
00671             Color background, Color foreground, int height, int width) {
00672         JLabel lbl = new JLabel(label + ": ");
00673         lbl.setBackground(_background);
00674 
00675         JTextArea textArea = new JTextArea(theValue, height, width);
00676         textArea.setEditable(true);
00677         textArea.setBackground(background);
00678         textArea.setForeground(foreground);
00679 
00680         QueryScrollPane textPane = new QueryScrollPane(textArea);
00681         _addPair(name, lbl, textPane, textPane);
00682         textArea.addFocusListener(new QueryFocusListener(name));
00683     }
00684 
00698     public boolean booleanValue(String name) throws NoSuchElementException,
00699             IllegalArgumentException {
00700         return getBooleanValue(name);
00701     }
00702 
00720     public double doubleValue(String name) throws IllegalArgumentException,
00721             NoSuchElementException, NumberFormatException {
00722         return getDoubleValue(name);
00723     }
00724 
00737     public boolean getBooleanValue(String name) throws NoSuchElementException,
00738             IllegalArgumentException {
00739         Object result = _entries.get(name);
00740 
00741         if (result == null) {
00742             throw new NoSuchElementException("No item named \"" + name
00743                     + "\" in the query box.");
00744         }
00745 
00746         if (result instanceof JToggleButton) {
00747             return ((JToggleButton) result).isSelected();
00748         } else {
00749             throw new IllegalArgumentException("Item named \"" + name
00750                     + "\" is not a radio button, and hence does not have "
00751                     + "a boolean value.");
00752         }
00753     }
00754 
00770     public char[] getCharArrayValue(String name) throws NoSuchElementException,
00771             IllegalArgumentException {
00772         Object result = _entries.get(name);
00773 
00774         if (result == null) {
00775             throw new NoSuchElementException("No item named \"" + name
00776                     + "\" in the query box.");
00777         }
00778 
00779         if (result instanceof JPasswordField) {
00780             // Calling JPasswordField.getText() is deprecated
00781             return ((JPasswordField) result).getPassword();
00782         } else {
00783             return getStringValue(name).toCharArray();
00784         }
00785     }
00786 
00803     public double getDoubleValue(String name) throws IllegalArgumentException,
00804             NoSuchElementException, NumberFormatException {
00805         Object result = _entries.get(name);
00806 
00807         if (result == null) {
00808             throw new NoSuchElementException("No item named \"" + name
00809                     + " \" in the query box.");
00810         }
00811 
00812         if (result instanceof JPasswordField) {
00813             // Note that JPasswordField extends JTextField, so
00814             // we should check for JPasswordField first.
00815             throw new IllegalArgumentException("For security reasons, "
00816                     + "calling getDoubleValue() on a password field is "
00817                     + "not permitted.  Instead, call getCharArrayValue()");
00818         } else if (result instanceof JTextField) {
00819             return (Double.valueOf(((JTextField) result).getText()))
00820                     .doubleValue();
00821         } else {
00822             throw new IllegalArgumentException("Item named \"" + name
00823                     + "\" is not a text line, and hence cannot be converted "
00824                     + "to a double value.");
00825         }
00826     }
00827 
00845     public int getIntValue(String name) throws IllegalArgumentException,
00846             NoSuchElementException, NumberFormatException {
00847         Object result = _entries.get(name);
00848 
00849         if (result == null) {
00850             throw new NoSuchElementException("No item named \"" + name
00851                     + " \" in the query box.");
00852         }
00853 
00854         if (result instanceof JPasswordField) {
00855             // Note that JPasswordField extends JTextField, so
00856             // we should check for JPasswordField first.
00857             throw new IllegalArgumentException("For security reasons, "
00858                     + "calling getIntValue() on a password field is "
00859                     + "not permitted.  Instead, call getCharArrayValue()");
00860         } else if (result instanceof JTextField) {
00861             return (Integer.valueOf(((JTextField) result).getText()))
00862                     .intValue();
00863         } else if (result instanceof JSlider) {
00864             return ((JSlider) result).getValue();
00865         } else if (result instanceof JComboBox) {
00866             return ((JComboBox) result).getSelectedIndex();
00867         } else if (result instanceof JToggleButton[]) {
00868             // Regrettably, ButtonGroup gives no way to determine
00869             // which button is selected, so we have to search...
00870             JToggleButton[] buttons = (JToggleButton[]) result;
00871 
00872             for (int i = 0; i < buttons.length; i++) {
00873                 if (buttons[i].isSelected()) {
00874                     return i;
00875                 }
00876             }
00877 
00878             // In theory, we shouldn't get here, but the compiler
00879             // is unhappy without a return.
00880             return -1;
00881         } else {
00882             throw new IllegalArgumentException("Item named \"" + name
00883                     + "\" is not a text line or slider, and hence "
00884                     + "cannot be converted to " + "an integer value.");
00885         }
00886     }
00887 
00894     public Dimension getMaximumSize() {
00895         // Unfortunately, if we don't have a message, then we end up with
00896         // an empty space that is difficult to control the size of, which
00897         // requires us to set the maximum size to be the same as
00898         // the preferred size
00899         // If you change this, be sure to try applets that have both
00900         // horizontal and vertical layout.
00901         Dimension preferred = getPreferredSize();
00902         preferred.width = Short.MAX_VALUE;
00903         return preferred;
00904     }
00905 
00920     public String getStringValue(String name) throws NoSuchElementException,
00921             IllegalArgumentException {
00922         Object result = _entries.get(name);
00923 
00924         if (result == null) {
00925             throw new NoSuchElementException("No item named \"" + name
00926                     + " \" in the query box.");
00927         }
00928 
00929         if (result instanceof JTextField) {
00930             return ((JTextField) result).getText();
00931         } else if (result instanceof QueryColorChooser) {
00932             return ((QueryColorChooser) result).getSelectedColor();
00933         } else if (result instanceof QueryFileChooser) {
00934             return ((QueryFileChooser) result).getSelectedFileName();
00935         } else if (result instanceof JTextArea) {
00936             return ((JTextArea) result).getText();
00937         } else if (result instanceof JToggleButton) {
00938             // JRadioButton and JCheckButton are subclasses of JToggleButton
00939             JToggleButton toggleButton = (JToggleButton) result;
00940 
00941             if (toggleButton.isSelected()) {
00942                 return "true";
00943             } else {
00944                 return "false";
00945             }
00946         } else if (result instanceof JSlider) {
00947             return "" + ((JSlider) result).getValue();
00948         } else if (result instanceof JComboBox) {
00949             return (String) (((JComboBox) result).getSelectedItem());
00950         } else if (result instanceof JToggleButton[]) {
00951             // JRadioButton and JCheckButton are subclasses of JToggleButton
00952             // Regrettably, ButtonGroup gives no way to determine
00953             // which button is selected, so we have to search...
00954             JToggleButton[] buttons = (JToggleButton[]) result;
00955             StringBuffer toReturn = null;
00956 
00957             for (int i = 0; i < buttons.length; i++) {
00958                 if (buttons[i].isSelected()) {
00959                     if (toReturn == null) {
00960                         toReturn = new StringBuffer(buttons[i].getText());
00961                     } else {
00962                         toReturn.append(", " + buttons[i].getText());
00963                     }
00964                 }
00965             }
00966 
00967             if (toReturn == null) {
00968                 toReturn = new StringBuffer();
00969             }
00970 
00971             return toReturn.toString();
00972         } else if (result instanceof QueryScrollPane) {
00973             return ((QueryScrollPane) result).getText();
00974         } else {
00975             throw new IllegalArgumentException("Query class cannot generate"
00976                     + " a string representation for entries of type "
00977                     + result.getClass());
00978         }
00979     }
00980 
00988     public int getTextHeight() {
00989         return _height;
00990     }
00991 
00998     public int getTextWidth() {
00999         return _width;
01000     }
01001 
01020     public int intValue(String name) throws IllegalArgumentException,
01021             NoSuchElementException, NumberFormatException {
01022         return getIntValue(name);
01023     }
01024 
01028     public void notifyListeners() {
01029         Iterator names = _entries.keySet().iterator();
01030 
01031         while (names.hasNext()) {
01032             String name = (String) names.next();
01033             _notifyListeners(name);
01034         }
01035     }
01036 
01042     public void removeQueryListener(QueryListener listener) {
01043         if (_listeners == null) {
01044             return;
01045         }
01046 
01047         _listeners.remove(listener);
01048     }
01049 
01063     public void set(String name, String value) throws NoSuchElementException,
01064             IllegalArgumentException {
01065         Object result = _entries.get(name);
01066 
01067         if (result == null) {
01068             throw new NoSuchElementException("No item named \"" + name
01069                     + " \" in the query box.");
01070         }
01071 
01072         // FIXME: Surely there is a better way to do this...
01073         // We should define a set of inner classes, one for each entry type.
01074         // Currently, this has to be updated each time a new entry type
01075         // is added.
01076         if (result instanceof JTextField) {
01077             ((JTextField) result).setText(value);
01078         } else if (result instanceof JTextArea) {
01079             ((JTextArea) result).setText(value);
01080         } else if (result instanceof QueryScrollPane) {
01081             ((QueryScrollPane) result).setText(value);
01082         } else if (result instanceof JToggleButton) {
01083             // JRadioButton and JCheckButton are subclasses of JToggleButton
01084             Boolean flag = Boolean.valueOf(value);
01085             setBoolean(name, flag.booleanValue());
01086         } else if (result instanceof JSlider) {
01087             Integer parsed = Integer.valueOf(value);
01088             ((JSlider) result).setValue(parsed.intValue());
01089         } else if (result instanceof JComboBox) {
01090             ((JComboBox) result).setSelectedItem(value);
01091         } else if (result instanceof JToggleButton[]) {
01092             // First, parse the value, which may be a comma-separated list.
01093             Set selectedValues = new HashSet();
01094             StringTokenizer tokenizer = new StringTokenizer(value, ",");
01095 
01096             while (tokenizer.hasMoreTokens()) {
01097                 selectedValues.add(tokenizer.nextToken().trim());
01098             }
01099 
01100             JToggleButton[] buttons = (JToggleButton[]) result;
01101 
01102             for (int i = 0; i < buttons.length; i++) {
01103                 if (selectedValues.contains(buttons[i].getText())) {
01104                     buttons[i].setSelected(true);
01105                 } else {
01106                     buttons[i].setSelected(false);
01107                 }
01108             }
01109         } else if (result instanceof QueryColorChooser) {
01110             ((QueryColorChooser) result).setColor(value);
01111         } else if (result instanceof QueryFileChooser) {
01112             ((QueryFileChooser) result).setFileName(value);
01113         } else {
01114             throw new IllegalArgumentException("Query class cannot set"
01115                     + " a string representation for entries of type "
01116                     + result.getClass());
01117         }
01118 
01119         // Record the new value as if it was the previously notified
01120         // value.  Thus, any future change from this value will trigger
01121         // notification.
01122         _previous.put(name, value);
01123     }
01124 
01136     public void setAndNotify(String name, String value)
01137             throws NoSuchElementException, IllegalArgumentException {
01138         set(name, value);
01139         _notifyListeners(name);
01140     }
01141 
01145     public void setBackground(Color color) {
01146         super.setBackground(color);
01147         _background = color;
01148 
01149         // Set the background of any components that already exist.
01150         Component[] components = getComponents();
01151 
01152         for (int i = 0; i < components.length; i++) {
01153             if (!(components[i] instanceof JTextField)) {
01154                 components[i].setBackground(_background);
01155             }
01156         }
01157     }
01158 
01171     public void setBoolean(String name, boolean value)
01172             throws NoSuchElementException, IllegalArgumentException {
01173         Object result = _entries.get(name);
01174 
01175         if (result == null) {
01176             throw new NoSuchElementException("No item named \"" + name
01177                     + "\" in the query box.");
01178         }
01179 
01180         if (result instanceof JToggleButton) {
01181             // JRadioButton and JCheckButton are subclasses of JToggleButton
01182             ((JToggleButton) result).setSelected(value);
01183         } else {
01184             throw new IllegalArgumentException("Item named \"" + name
01185                     + "\" is not a radio button, and hence does not have "
01186                     + "a boolean value.");
01187         }
01188 
01189         _notifyListeners(name);
01190     }
01191 
01199     public void setColumns(int columns) {
01200         if (columns <= 0) {
01201             throw new IllegalArgumentException(
01202                     "Query.setColumns() requires a strictly positive "
01203                             + "argument.");
01204         }
01205 
01206         _columns = columns;
01207     }
01208 
01221     public void setDisplay(String name, String value)
01222             throws NoSuchElementException, IllegalArgumentException {
01223         Object result = _entries.get(name);
01224 
01225         if (result == null) {
01226             throw new NoSuchElementException("No item named \"" + name
01227                     + " \" in the query box.");
01228         }
01229 
01230         if (result instanceof JTextArea) {
01231             JTextArea label = (JTextArea) result;
01232             label.setText(value);
01233         } else {
01234             throw new IllegalArgumentException("Item named \"" + name
01235                     + "\" is not a display, and hence cannot be set using "
01236                     + "setDisplay().");
01237         }
01238 
01239         _notifyListeners(name);
01240     }
01241 
01248     public void setEnabled(String name, boolean value) {
01249         Object result = _entries.get(name);
01250 
01251         if (result == null) {
01252             throw new NoSuchElementException("No item named \"" + name
01253                     + " \" in the query box.");
01254         }
01255 
01256         if (result instanceof JComponent) {
01257             ((JComponent) result).setEnabled(value);
01258         } else if (result instanceof JToggleButton[]) {
01259             JToggleButton[] buttons = (JToggleButton[]) result;
01260 
01261             for (int i = 0; i < buttons.length; i++) {
01262                 buttons[i].setEnabled(value);
01263             }
01264         }
01265     }
01266 
01278     public void setLine(String name, String value) {
01279         Object result = _entries.get(name);
01280 
01281         if (result == null) {
01282             throw new NoSuchElementException("No item named \"" + name
01283                     + " \" in the query box.");
01284         }
01285 
01286         if (result instanceof JTextField) {
01287             JTextField line = (JTextField) result;
01288             line.setText(value);
01289         } else {
01290             throw new IllegalArgumentException("Item named \"" + name
01291                     + "\" is not a line, and hence cannot be set using "
01292                     + "setLine().");
01293         }
01294 
01295         _notifyListeners(name);
01296     }
01297 
01301     public void setMessage(String message) {
01302         if (!_messageScrollPaneAdded) {
01303             _messageScrollPaneAdded = true;
01304             add(_messageScrollPane, 1);
01305 
01306             // Add a spacer.
01307             add(Box.createRigidArea(new Dimension(0, 10)), 2);
01308         }
01309 
01310         _messageArea.setText(message);
01311 
01312         // I'm not sure why we need to add 1 here?
01313         int lineCount = _messageArea.getLineCount() + 1;
01314 
01315         // Keep the line count to less than 30 lines.  If
01316         // we have more than 30 lines, we get a scroll bar.
01317         if (lineCount > 30) {
01318             lineCount = 30;
01319         }
01320 
01321         _messageArea.setRows(lineCount);
01322         _messageArea.setColumns(_width);
01323 
01324         // In case size has changed.
01325         validate();
01326     }
01327 
01339     public void setSlider(String name, int value) {
01340         Object result = _entries.get(name);
01341 
01342         if (result == null) {
01343             throw new NoSuchElementException("No item named \"" + name
01344                     + " \" in the query box.");
01345         }
01346 
01347         if (result instanceof JSlider) {
01348             JSlider theSlider = (JSlider) result;
01349 
01350             // Set the new slider position.
01351             theSlider.setValue(value);
01352         } else {
01353             throw new IllegalArgumentException("Item named \"" + name
01354                     + "\" is not a slider, and hence cannot be set using "
01355                     + "setSlider().");
01356         }
01357 
01358         _notifyListeners(name);
01359     }
01360 
01368     public void setTextHeight(int characters) {
01369         _height = characters;
01370     }
01371 
01378     public void setTextWidth(int characters) {
01379         _width = characters;
01380     }
01381 
01386     public void setToolTip(String name, String tip) {
01387         JLabel label = (JLabel) _labels.get(name);
01388 
01389         if (label != null) {
01390             label.setToolTipText(tip);
01391         }
01392     }
01393 
01402     public static Color stringToColor(String description) {
01403         String[] specArray = description.split("[{},]");
01404         float red = 0f;
01405         float green = 0f;
01406         float blue = 0f;
01407         float alpha = 1.0f;
01408 
01409         // If any exceptions occur during the attempt to parse,
01410         // then just use the default color.
01411         try {
01412             int i = 0;
01413 
01414             // Ignore any blank strings that this simple parsing produces.
01415             while (specArray[i].trim().equals("")) {
01416                 i++;
01417             }
01418 
01419             if (specArray.length > i) {
01420                 red = Float.parseFloat(specArray[i]);
01421             }
01422 
01423             i++;
01424 
01425             while (specArray[i].trim().equals("")) {
01426                 i++;
01427             }
01428 
01429             if (specArray.length > i) {
01430                 green = Float.parseFloat(specArray[i]);
01431             }
01432 
01433             i++;
01434 
01435             while (specArray[i].trim().equals("")) {
01436                 i++;
01437             }
01438 
01439             if (specArray.length > i) {
01440                 blue = Float.parseFloat(specArray[i]);
01441             }
01442 
01443             i++;
01444 
01445             while (specArray[i].trim().equals("")) {
01446                 i++;
01447             }
01448 
01449             if (specArray.length > i) {
01450                 alpha = Float.parseFloat(specArray[i]);
01451             }
01452         } catch (Exception ex) {
01453             // Ignore and use default color.
01454         }
01455         return new Color(red, green, blue, alpha);
01456     }
01457 
01473     public String stringValue(String name) throws NoSuchElementException,
01474             IllegalArgumentException {
01475         return getStringValue(name);
01476     }
01477 
01480 
01482     public static final int DEFAULT_ENTRY_HEIGHT = 10;
01483 
01485     public static final int DEFAULT_ENTRY_WIDTH = 30;
01486 
01489 
01496     protected void _addPair(String name, JLabel label, Component widget,
01497             Object entry) {
01498         // Surely there is a better layout manager in swing...
01499         // Note that Box and BoxLayout do not work because they do not
01500         // support gridded layout.
01501         _constraints.gridwidth = 1;
01502         _constraints.insets = _leftPadding;
01503         _grid.setConstraints(label, _constraints);
01504         _entryPanel.add(label);
01505 
01506         _constraints.insets = _noPadding;
01507 
01508         if ((_columns > 1) && (((_entries.size() + 1) % _columns) != 0)) {
01509             _constraints.gridwidth = 1;
01510         } else {
01511             _constraints.gridwidth = GridBagConstraints.REMAINDER;
01512         }
01513 
01514         _grid.setConstraints(widget, _constraints);
01515         _entryPanel.add(widget);
01516 
01517         _entries.put(name, entry);
01518         _labels.put(name, label);
01519         _previous.put(name, getStringValue(name));
01520 
01521         Dimension preferredSize = _entryPanel.getPreferredSize();
01522 
01523         // Add some slop to the width to take in to account
01524         // the width of the vertical scrollbar.
01525         preferredSize.width += 25;
01526 
01527         // Applets seem to need this, see CT/SigmaDelta
01528         _widgetsHeight += widget.getPreferredSize().height;
01529         preferredSize.height = _widgetsHeight;
01530 
01531         Toolkit tk = Toolkit.getDefaultToolkit();
01532 
01533         if (preferredSize.height > tk.getScreenSize().height) {
01534             // Fudge factor to keep this window smaller than the screen
01535             // height.  CGSUnitBase and the Code Generator are good tests.
01536             preferredSize.height = (int) (tk.getScreenSize().height * 0.75);
01537             _entryScrollPane.setPreferredSize(preferredSize);
01538         }
01539 
01540         _entryScrollPane.setPreferredSize(preferredSize);
01541 
01542         // Call revalidate for the scrollbar.
01543         _entryPanel.revalidate();
01544     }
01545 
01548 
01553     protected Color _background = null;
01554 
01556     protected GridBagConstraints _constraints;
01557 
01559     protected GridBagLayout _grid;
01560 
01562     protected Vector _listeners;
01563 
01566 
01574     void _notifyListeners(String name) {
01575         if (_listeners != null) {
01576             String previous = (String) _previous.get(name);
01577             String newValue = getStringValue(name);
01578 
01579             if (newValue.equals(previous)) {
01580                 return;
01581             }
01582 
01583             // Store the new value to prevent repeated notification.
01584             // This must be done before listeners are notified, because
01585             // the notified listeners might do something that again triggers
01586             // notification, and we do not want that notification to occur
01587             // if the value has not changed.
01588             _previous.put(name, newValue);
01589 
01590             Enumeration listeners = _listeners.elements();
01591 
01592             while (listeners.hasMoreElements()) {
01593                 QueryListener queryListener = (QueryListener) (listeners
01594                         .nextElement());
01595                 queryListener.changed(name);
01596             }
01597         }
01598     }
01599 
01602     // The number of columns.
01603     private int _columns = 1;
01604 
01605     // The hashtable of items in the query.
01606     private Map _entries = new HashMap();
01607 
01608     // A panel within which the entries are placed.
01609     private JPanel _entryPanel = new JPanel();
01610 
01611     // A scroll pane that contains the _entryPanel.
01612     private JScrollPane _entryScrollPane;
01613 
01614     // The number of lines in a text box.
01615     private int _height = DEFAULT_ENTRY_HEIGHT;
01616 
01617     // The hashtable of labels in the query.
01618     private Map _labels = new HashMap();
01619 
01620     // Left padding insets.
01621     private Insets _leftPadding = new Insets(0, 10, 0, 0);
01622 
01623     // Area for messages.
01624     private JTextArea _messageArea = null;
01625 
01626     // A scroll pane that contains the _messageArea.
01627     private JScrollPane _messageScrollPane;
01628 
01629     // True if we have added the _messageScrollPane
01630     private boolean _messageScrollPaneAdded = false;
01631 
01632     // No padding insets.
01633     private Insets _noPadding = new Insets(0, 0, 0, 0);
01634 
01635     // The hashtable of previous values, indexed by entry name.
01636     private Map _previous = new HashMap();
01637 
01638     // The sum of the height of the widgets added using _addPair
01639     // If you adjust this, try the GR/Pendulum demo, which has
01640     // only one parameter.
01641     private int _widgetsHeight = 20;
01642 
01643     // The number of horizontal characters in a text box.
01644     private int _width = DEFAULT_ENTRY_WIDTH;
01645 
01648 
01651     class QueryActionListener implements ActionListener {
01652         public QueryActionListener(String name) {
01653             _name = name;
01654         }
01655 
01657         public void actionPerformed(ActionEvent e) {
01658             _notifyListeners(_name);
01659         }
01660 
01661         private String _name;
01662     }
01663 
01666     class QueryColorChooser extends Box implements ActionListener {
01667         public QueryColorChooser(String name, String defaultColor) {
01668             super(BoxLayout.X_AXIS);
01669             //_defaultColor = defaultColor;
01670             _entryBox = new JTextField(defaultColor, _width);
01671 
01672             JButton button = new JButton("Choose");
01673             button.addActionListener(this);
01674             add(_entryBox);
01675             add(button);
01676 
01677             // Add the listener last so that there is no notification
01678             // of the first value.
01679             _entryBox.addActionListener(new QueryActionListener(name));
01680 
01681             // Add a listener for loss of focus.  When the entry gains
01682             // and then loses focus, listeners are notified of an update,
01683             // but only if the value has changed since the last notification.
01684             // FIXME: Unfortunately, Java calls this listener some random
01685             // time after the window has been closed.  It is not even a
01686             // a queued event when the window is closed.  Thus, we have
01687             // a subtle bug where if you enter a value in a line, do not
01688             // hit return, and then click on the X to close the window,
01689             // the value is restored to the original, and then sometime
01690             // later, the focus is lost and the entered value becomes
01691             // the value of the parameter.  I don't know of any workaround.
01692             _entryBox.addFocusListener(new QueryFocusListener(name));
01693 
01694             _name = name;
01695         }
01696 
01697         public void actionPerformed(ActionEvent e) {
01698             // Read the current color from the text field.
01699             String spec = getSelectedColor().trim();
01700             Color newColor = JColorChooser.showDialog(Query.this,
01701                     "Choose Color", stringToColor(spec));
01702 
01703             if (newColor != null) {
01704                 float[] components = newColor.getRGBComponents(null);
01705                 StringBuffer string = new StringBuffer("{");
01706 
01707                 // Use the syntax of arrays.
01708                 for (int j = 0; j < components.length; j++) {
01709                     string.append(components[j]);
01710 
01711                     if (j < (components.length - 1)) {
01712                         string.append(",");
01713                     } else {
01714                         string.append("}");
01715                     }
01716                 }
01717 
01718                 _entryBox.setText(string.toString());
01719                 _notifyListeners(_name);
01720             }
01721         }
01722 
01723         public String getSelectedColor() {
01724             return _entryBox.getText();
01725         }
01726 
01727         public void setColor(String name) {
01728             _entryBox.setText(name);
01729         }
01730 
01731         private JTextField _entryBox;
01732 
01733         private String _name;
01734 
01735         //private String _defaultColor;
01736     }
01737 
01740     class QueryFileChooser extends Box implements ActionListener {
01741         public QueryFileChooser(String name, String defaultName, URI base,
01742                 File startingDirectory, boolean allowFiles,
01743                 boolean allowDirectories) {
01744             this(name, defaultName, base, startingDirectory, allowFiles,
01745                     allowDirectories, Color.white, Color.black);
01746         }
01747 
01748         public QueryFileChooser(String name, String defaultName, URI base,
01749                 File startingDirectory, boolean allowFiles,
01750                 boolean allowDirectories, Color background, Color foreground) {
01751             super(BoxLayout.X_AXIS);
01752             _base = base;
01753             _startingDirectory = startingDirectory;
01754 
01755             if (!allowFiles && !allowDirectories) {
01756                 throw new IllegalArgumentException(
01757                         "QueryFileChooser: nothing to be chosen.");
01758             }
01759 
01760             _allowFiles = allowFiles;
01761             _allowDirectories = allowDirectories;
01762             _entryBox = new JTextField(defaultName, _width);
01763             _entryBox.setBackground(background);
01764             _entryBox.setForeground(foreground);
01765 
01766             JButton button = new JButton("Browse");
01767             button.addActionListener(this);
01768             add(_entryBox);
01769             add(button);
01770 
01771             // Add the listener last so that there is no notification
01772             // of the first value.
01773             _entryBox.addActionListener(new QueryActionListener(name));
01774 
01775             // Add a listener for loss of focus.  When the entry gains
01776             // and then loses focus, listeners are notified of an update,
01777             // but only if the value has changed since the last notification.
01778             // FIXME: Unfortunately, Java calls this listener some random
01779             // time after the window has been closed.  It is not even a
01780             // a queued event when the window is closed.  Thus, we have
01781             // a subtle bug where if you enter a value in a line, do not
01782             // hit return, and then click on the X to close the window,
01783             // the value is restored to the original, and then sometime
01784             // later, the focus is lost and the entered value becomes
01785             // the value of the parameter.  I don't know of any workaround.
01786             _entryBox.addFocusListener(new QueryFocusListener(name));
01787 
01788             _name = name;
01789         }
01790 
01791         public void actionPerformed(ActionEvent e) {
01792             // NOTE: If the last argument is null, then choose a default dir.
01793             JFileChooser fileChooser = new JFileChooser(_startingDirectory);
01794             fileChooser.setApproveButtonText("Select");
01795 
01796             // FIXME: The following doesn't have any effect.
01797             fileChooser.setApproveButtonMnemonic('S');
01798 
01799             if (_allowFiles && _allowDirectories) {
01800                 fileChooser
01801                         .setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
01802             } else if (_allowFiles && !_allowDirectories) {
01803                 // This is the default.
01804                 fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
01805             } else if (!_allowFiles && _allowDirectories) {
01806                 fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
01807             } else {
01808                 // Usually, we would use InternalErrorException here,
01809                 // but if we do, then this package would depend on kernel.util,
01810                 // which causes problems when we ship Ptplot.
01811                 throw new RuntimeException(
01812                         "QueryFileChooser: nothing to be chosen.");
01813             }
01814 
01815             int returnValue = fileChooser.showOpenDialog(Query.this);
01816 
01817             if (returnValue == JFileChooser.APPROVE_OPTION) {
01818                 if (_base == null) {
01819                     // Absolute file name.
01820                     try {
01821                         _entryBox.setText(fileChooser.getSelectedFile()
01822                                 .getCanonicalPath());
01823                     } catch (IOException ex) {
01824                         // If we can't get a path, then just use the name.
01825                         _entryBox.setText(fileChooser.getSelectedFile()
01826                                 .getName());
01827                     }
01828                 } else {
01829                     // Relative file name.
01830                     File selectedFile = fileChooser.getSelectedFile();
01831 
01832                     // FIXME: There is a bug here under Windows XP
01833                     // at least... Sometimes, the drive ID (like c:)
01834                     // is lower case, and sometimes it's upper case.
01835                     // When we open a MoML file, it's upper case.
01836                     // When we do "save as", it's lower case.
01837                     // This despite the fact that both use the same
01838                     // file browser to determine the file name.
01839                     // Beats me... Consequence is that if you save as,
01840                     // then the following relativize call doesn't work
01841                     // until you close and reopen the file.
01842                     try {
01843                         selectedFile = selectedFile.getCanonicalFile();
01844                     } catch (IOException ex) {
01845                         // Ignore, since we can't do much about it anyway.
01846                     }
01847 
01848                     URI relativeURI = _base.relativize(selectedFile.toURI());
01849                     _entryBox.setText(relativeURI.toString());
01850                 }
01851 
01852                 _notifyListeners(_name);
01853             }
01854         }
01855 
01856         public String getSelectedFileName() {
01857             return _entryBox.getText();
01858         }
01859 
01860         public void setFileName(String name) {
01861             _entryBox.setText(name);
01862         }
01863 
01864         private URI _base;
01865 
01866         private JTextField _entryBox;
01867 
01868         private String _name;
01869 
01870         private File _startingDirectory;
01871 
01872         private boolean _allowFiles;
01873 
01874         private boolean _allowDirectories;
01875     }
01876 
01879     class QueryFocusListener implements FocusListener {
01880         public QueryFocusListener(String name) {
01881             _name = name;
01882         }
01883 
01884         public void focusGained(FocusEvent e) {
01885             // Nothing to do.
01886         }
01887 
01888         public void focusLost(FocusEvent e) {
01889             // NOTE: Java's lame AWT has no reliable way
01890             // to take action on window closing, so this focus lost
01891             // notification is the only reliable way we have of reacting
01892             // to a closing window.  If the previous
01893             // notification was an erroneous one and the value has not
01894             // changed, then no further notification occurs.
01895             // This could be a problem for some users of this class.
01896             _notifyListeners(_name);
01897         }
01898 
01899         private String _name;
01900     }
01901 
01904     class QueryItemListener implements ItemListener {
01905         public QueryItemListener(String name) {
01906             _name = name;
01907         }
01908 
01910         public void itemStateChanged(ItemEvent e) {
01911             _notifyListeners(_name);
01912         }
01913 
01914         private String _name;
01915     }
01916 
01918     static class QueryScrollPane extends JScrollPane {
01919         // FindBugs suggests making this class static so as to decrease
01920         // the size of instances and avoid dangling references.
01921 
01922         public JTextArea textArea;
01923 
01924         QueryScrollPane(JTextArea c) {
01925             super(c);
01926             textArea = c;
01927         }
01928 
01929         public String getText() {
01930             String retval = textArea.getText();
01931             return retval;
01932         }
01933 
01934         public void setText(String s) {
01935             textArea.setText(s);
01936         }
01937     }
01938 
01941     class SliderListener implements ChangeListener {
01942         public SliderListener(String name) {
01943             _name = name;
01944         }
01945 
01947         public void stateChanged(ChangeEvent event) {
01948             _notifyListeners(_name);
01949         }
01950 
01951         private String _name;
01952     }
01953 }