Mega Code Archive

 
Categories / Java / Swing JFC
 

Implements BoundedRangeModel

/*  * Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  All rights reserved.  *  * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions  * are met:  *  *   - Redistributions of source code must retain the above copyright  *     notice, this list of conditions and the following disclaimer.  *  *   - Redistributions in binary form must reproduce the above copyright  *     notice, this list of conditions and the following disclaimer in the  *     documentation and/or other materials provided with the distribution.  *  *   - Neither the name of Sun Microsystems nor the names of its  *     contributors may be used to endorse or promote products derived  *     from this software without specific prior written permission.  *  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  */ /*  * A application that requires the following files:  *   ConversionPanel.java  *   ConverterRangeModel.java  *   FollowerRangeModel.java  *   Unit.java  */ import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.NumberFormat; import javax.swing.BorderFactory; import javax.swing.BoundedRangeModel; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JComboBox; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.EventListenerList; import javax.swing.text.NumberFormatter; public class Converter {   ConversionPanel metricPanel, usaPanel;   Unit[] metricDistances = new Unit[3];   Unit[] usaDistances = new Unit[4];   final static boolean MULTICOLORED = false;   // Specify the look and feel to use. Valid values:   // null (use the default), "Metal", "System", "Motif", "GTK+"   final static String LOOKANDFEEL = null;   ConverterRangeModel dataModel = new ConverterRangeModel();   JPanel mainPane;   /**    * Create the ConversionPanels (one for metric, another for U.S.). I used    * "U.S." because although Imperial and U.S. distance measurements are the    * same, this program could be extended to include volume measurements, which    * aren't the same.    */   public Converter() {     // Create Unit objects for metric distances, and then     // instantiate a ConversionPanel with these Units.     metricDistances[0] = new Unit("Centimeters", 0.01);     metricDistances[1] = new Unit("Meters", 1.0);     metricDistances[2] = new Unit("Kilometers", 1000.0);     metricPanel = new ConversionPanel(this, "Metric System", metricDistances,         dataModel);     // Create Unit objects for U.S. distances, and then     // instantiate a ConversionPanel with these Units.     usaDistances[0] = new Unit("Inches", 0.0254);     usaDistances[1] = new Unit("Feet", 0.305);     usaDistances[2] = new Unit("Yards", 0.914);     usaDistances[3] = new Unit("Miles", 1613.0);     usaPanel = new ConversionPanel(this, "U.S. System", usaDistances,         new FollowerRangeModel(dataModel));     // Create a JPanel, and add the ConversionPanels to it.     mainPane = new JPanel();     mainPane.setLayout(new BoxLayout(mainPane, BoxLayout.PAGE_AXIS));     if (MULTICOLORED) {       mainPane.setOpaque(true);       mainPane.setBackground(new Color(255, 0, 0));     }     mainPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));     mainPane.add(Box.createRigidArea(new Dimension(0, 5)));     mainPane.add(metricPanel);     mainPane.add(Box.createRigidArea(new Dimension(0, 5)));     mainPane.add(usaPanel);     mainPane.add(Box.createGlue());     resetMaxValues(true);   }   public void resetMaxValues(boolean resetCurrentValues) {     double metricMultiplier = metricPanel.getMultiplier();     double usaMultiplier = usaPanel.getMultiplier();     int maximum = ConversionPanel.MAX;     if (metricMultiplier > usaMultiplier) {       maximum = (int) (ConversionPanel.MAX * (usaMultiplier / metricMultiplier));     }     dataModel.setMaximum(maximum);     if (resetCurrentValues) {       dataModel.setDoubleValue(maximum);     }   }   private static void initLookAndFeel() {     String lookAndFeel = null;     if (LOOKANDFEEL != null) {       if (LOOKANDFEEL.equals("Metal")) {         lookAndFeel = UIManager.getCrossPlatformLookAndFeelClassName();       } else if (LOOKANDFEEL.equals("System")) {         lookAndFeel = UIManager.getSystemLookAndFeelClassName();       } else if (LOOKANDFEEL.equals("Motif")) {         lookAndFeel = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";       } else if (LOOKANDFEEL.equals("GTK+")) { // new in 1.4.2         lookAndFeel = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";       } else {         System.err.println("Unexpected value of LOOKANDFEEL specified: "             + LOOKANDFEEL);         lookAndFeel = UIManager.getCrossPlatformLookAndFeelClassName();       }       try {         UIManager.setLookAndFeel(lookAndFeel);       } catch (ClassNotFoundException e) {         System.err.println("Couldn't find class for specified look and feel:"             + lookAndFeel);         System.err             .println("Did you include the L&F library in the class path?");         System.err.println("Using the default look and feel.");       } catch (UnsupportedLookAndFeelException e) {         System.err.println("Can't use the specified look and feel ("             + lookAndFeel + ") on this platform.");         System.err.println("Using the default look and feel.");       } catch (Exception e) {         System.err.println("Couldn't get specified look and feel ("             + lookAndFeel + "), for some reason.");         System.err.println("Using the default look and feel.");         e.printStackTrace();       }     }   }   /**    * Create the GUI and show it. For thread safety, this method should be    * invoked from the event-dispatching thread.    */   private static void createAndShowGUI() {     // Set the look and feel.     initLookAndFeel();     // Create and set up the window.     JFrame frame = new JFrame("Converter");     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     // Create and set up the content pane.     Converter converter = new Converter();     converter.mainPane.setOpaque(true); // content panes must be opaque     frame.setContentPane(converter.mainPane);     // Display the window.     frame.pack();     frame.setVisible(true);   }   public static void main(String[] args) {     // Schedule a job for the event-dispatching thread:     // creating and showing this application's GUI.     javax.swing.SwingUtilities.invokeLater(new Runnable() {       public void run() {         createAndShowGUI();       }     });   } } /*  * Copyright (c) 1995 - 2008 Sun Microsystems, Inc. All rights reserved.  *   * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions are met:  *  - Redistributions of source code must retain the above copyright notice,  * this list of conditions and the following disclaimer.  *  - Redistributions in binary form must reproduce the above copyright notice,  * this list of conditions and the following disclaimer in the documentation  * and/or other materials provided with the distribution.  *  - Neither the name of Sun Microsystems nor the names of its contributors may  * be used to endorse or promote products derived from this software without  * specific prior written permission.  *   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  * POSSIBILITY OF SUCH DAMAGE.  */ /*  * Works in 1.1+Swing, 1.4, and all releases in between. Used by the Converter  * example.  */ /**  * Based on the source code for DefaultBoundedRangeModel, this class stores its  * value as a double, rather than an int. The minimum value and extent are  * always 0.  */ class ConverterRangeModel implements BoundedRangeModel {   protected ChangeEvent changeEvent = null;   protected EventListenerList listenerList = new EventListenerList();   protected int maximum = 10000;   protected int minimum = 0;   protected int extent = 0;   protected double value = 0.0;   protected double multiplier = 1.0;   protected boolean isAdjusting = false;   public ConverterRangeModel() {   }   public double getMultiplier() {     return multiplier;   }   public void setMultiplier(double multiplier) {     this.multiplier = multiplier;     fireStateChanged();   }   public int getMaximum() {     return maximum;   }   public void setMaximum(int newMaximum) {     setRangeProperties(value, extent, minimum, newMaximum, isAdjusting);   }   public int getMinimum() {     return (int) minimum;   }   public void setMinimum(int newMinimum) {     System.out.println("In ConverterRangeModel setMinimum");     // Do nothing.   }   public int getValue() {     return (int) getDoubleValue();   }   public void setValue(int newValue) {     setDoubleValue((double) newValue);   }   public double getDoubleValue() {     return value;   }   public void setDoubleValue(double newValue) {     setRangeProperties(newValue, extent, minimum, maximum, isAdjusting);   }   public int getExtent() {     return (int) extent;   }   public void setExtent(int newExtent) {     // Do nothing.   }   public boolean getValueIsAdjusting() {     return isAdjusting;   }   public void setValueIsAdjusting(boolean b) {     setRangeProperties(value, extent, minimum, maximum, b);   }   public void setRangeProperties(int newValue, int newExtent, int newMin,       int newMax, boolean newAdjusting) {     setRangeProperties((double) newValue, newExtent, newMin, newMax,         newAdjusting);   }   public void setRangeProperties(double newValue, int unusedExtent,       int unusedMin, int newMax, boolean newAdjusting) {     if (newMax <= minimum) {       newMax = minimum + 1;     }     if (Math.round(newValue) > newMax) { // allow some rounding error       newValue = newMax;     }     boolean changeOccurred = false;     if (newValue != value) {       value = newValue;       changeOccurred = true;     }     if (newMax != maximum) {       maximum = newMax;       changeOccurred = true;     }     if (newAdjusting != isAdjusting) {       maximum = newMax;       isAdjusting = newAdjusting;       changeOccurred = true;     }     if (changeOccurred) {       fireStateChanged();     }   }   /*    * The rest of this is event handling code copied from    * DefaultBoundedRangeModel.    */   public void addChangeListener(ChangeListener l) {     listenerList.add(ChangeListener.class, l);   }   public void removeChangeListener(ChangeListener l) {     listenerList.remove(ChangeListener.class, l);   }   protected void fireStateChanged() {     Object[] listeners = listenerList.getListenerList();     for (int i = listeners.length - 2; i >= 0; i -= 2) {       if (listeners[i] == ChangeListener.class) {         if (changeEvent == null) {           changeEvent = new ChangeEvent(this);         }         ((ChangeListener) listeners[i + 1]).stateChanged(changeEvent);       }     }   } } /*  * Copyright (c) 1995 - 2008 Sun Microsystems, Inc. All rights reserved.  *   * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions are met:  *  - Redistributions of source code must retain the above copyright notice,  * this list of conditions and the following disclaimer.  *  - Redistributions in binary form must reproduce the above copyright notice,  * this list of conditions and the following disclaimer in the documentation  * and/or other materials provided with the distribution.  *  - Neither the name of Sun Microsystems nor the names of its contributors may  * be used to endorse or promote products derived from this software without  * specific prior written permission.  *   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  * POSSIBILITY OF SUCH DAMAGE.  */ /*  * Works in 1.1+Swing, 1.4, and all releases in between. Used by the Converter  * example.  */ /**  * Implements a model whose data is actually in another model (the "source  * model"). The follower model adjusts the values obtained from the source model  * (or set in the follower model) to be in a different unit of measure.  *   */ class FollowerRangeModel extends ConverterRangeModel implements ChangeListener {   ConverterRangeModel sourceModel; // the real model   /** Creates a FollowerRangeModel that gets its state from sourceModel. */   public FollowerRangeModel(ConverterRangeModel sourceModel) {     this.sourceModel = sourceModel;     sourceModel.addChangeListener(this);   }   // The only method in the ChangeListener interface.   public void stateChanged(ChangeEvent e) {     fireStateChanged();   }   public int getMaximum() {     int modelMax = sourceModel.getMaximum();     double multiplyBy = sourceModel.getMultiplier() / this.getMultiplier();     return (int) (modelMax * multiplyBy);   }   public void setMaximum(int newMaximum) {     sourceModel         .setMaximum((int) (newMaximum * (this.getMultiplier() / sourceModel             .getMultiplier())));   }   public int getValue() {     return (int) getDoubleValue();   }   public void setValue(int newValue) {     setDoubleValue((double) newValue);   }   public double getDoubleValue() {     return sourceModel.getDoubleValue() * sourceModel.getMultiplier()         / this.getMultiplier();   }   public void setDoubleValue(double newValue) {     sourceModel.setDoubleValue(newValue * this.getMultiplier()         / sourceModel.getMultiplier());   }   public int getExtent() {     return super.getExtent();   }   public void setExtent(int newExtent) {     super.setExtent(newExtent);   }   public void setRangeProperties(int value, int extent, int min, int max,       boolean adjusting) {     double multiplyBy = this.getMultiplier() / sourceModel.getMultiplier();     sourceModel.setRangeProperties(value * multiplyBy, extent, min,         (int) (max * multiplyBy), adjusting);   } } /*  * Copyright (c) 1995 - 2008 Sun Microsystems, Inc. All rights reserved.  *   * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions are met:  *  - Redistributions of source code must retain the above copyright notice,  * this list of conditions and the following disclaimer.  *  - Redistributions in binary form must reproduce the above copyright notice,  * this list of conditions and the following disclaimer in the documentation  * and/or other materials provided with the distribution.  *  - Neither the name of Sun Microsystems nor the names of its contributors may  * be used to endorse or promote products derived from this software without  * specific prior written permission.  *   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  * POSSIBILITY OF SUCH DAMAGE.  */ /*  * Works in 1.1+Swing, 1.4, and all releases in between. Used by the Converter  * example.  */ class Unit {   String description;   double multiplier;   Unit(String description, double multiplier) {     super();     this.description = description;     this.multiplier = multiplier;   }   public String toString() {     String s = "Meters/" + description + " = " + multiplier;     return s;   } } /*  * Copyright (c) 1995 - 2008 Sun Microsystems, Inc. All rights reserved.  *   * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions are met:  *  - Redistributions of source code must retain the above copyright notice,  * this list of conditions and the following disclaimer.  *  - Redistributions in binary form must reproduce the above copyright notice,  * this list of conditions and the following disclaimer in the documentation  * and/or other materials provided with the distribution.  *  - Neither the name of Sun Microsystems nor the names of its contributors may  * be used to endorse or promote products derived from this software without  * specific prior written permission.  *   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  * POSSIBILITY OF SUCH DAMAGE.  */ /*  * A 1.4 class used by the Converter example.  */ class ConversionPanel extends JPanel implements ActionListener, ChangeListener,     PropertyChangeListener {   JFormattedTextField textField;   JComboBox unitChooser;   JSlider slider;   ConverterRangeModel sliderModel;   Converter controller;   Unit[] units;   String title;   NumberFormat numberFormat;   final static boolean MULTICOLORED = false;   final static int MAX = 10000;   ConversionPanel(Converter myController, String myTitle, Unit[] myUnits,       ConverterRangeModel myModel) {     if (MULTICOLORED) {       setOpaque(true);       setBackground(new Color(0, 255, 255));     }     setBorder(BorderFactory.createCompoundBorder(BorderFactory         .createTitledBorder(myTitle), BorderFactory.createEmptyBorder(5, 5, 5,         5)));     // Save arguments in instance variables.     controller = myController;     units = myUnits;     title = myTitle;     sliderModel = myModel;     // Create the text field format, and then the text field.     numberFormat = NumberFormat.getNumberInstance();     numberFormat.setMaximumFractionDigits(2);     NumberFormatter formatter = new NumberFormatter(numberFormat);     formatter.setAllowsInvalid(false);     formatter.setCommitsOnValidEdit(true);// seems to be a no-op --     // aha -- it changes the value property but doesn't cause the result to     // be parsed (that happens on focus loss/return, I think).     //     textField = new JFormattedTextField(formatter);     textField.setColumns(10);     textField.setValue(new Double(sliderModel.getDoubleValue()));     textField.addPropertyChangeListener(this);     // Add the combo box.     unitChooser = new JComboBox();     for (int i = 0; i < units.length; i++) { // Populate it.       unitChooser.addItem(units[i].description);     }     unitChooser.setSelectedIndex(0);     sliderModel.setMultiplier(units[0].multiplier);     unitChooser.addActionListener(this);     // Add the slider.     slider = new JSlider(sliderModel);     sliderModel.addChangeListener(this);     // Make the text field/slider group a fixed size     // to make stacked ConversionPanels nicely aligned.     JPanel unitGroup = new JPanel() {       public Dimension getMinimumSize() {         return getPreferredSize();       }       public Dimension getPreferredSize() {         return new Dimension(150, super.getPreferredSize().height);       }       public Dimension getMaximumSize() {         return getPreferredSize();       }     };     unitGroup.setLayout(new BoxLayout(unitGroup, BoxLayout.PAGE_AXIS));     if (MULTICOLORED) {       unitGroup.setOpaque(true);       unitGroup.setBackground(new Color(0, 0, 255));     }     unitGroup.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));     unitGroup.add(textField);     unitGroup.add(slider);     // Create a subpanel so the combo box isn't too tall     // and is sufficiently wide.     JPanel chooserPanel = new JPanel();     chooserPanel.setLayout(new BoxLayout(chooserPanel, BoxLayout.PAGE_AXIS));     if (MULTICOLORED) {       chooserPanel.setOpaque(true);       chooserPanel.setBackground(new Color(255, 0, 255));     }     chooserPanel.add(unitChooser);     chooserPanel.add(Box.createHorizontalStrut(100));     // Put everything together.     setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));     add(unitGroup);     add(chooserPanel);     unitGroup.setAlignmentY(TOP_ALIGNMENT);     chooserPanel.setAlignmentY(TOP_ALIGNMENT);   }   // Don't allow this panel to get taller than its preferred size.   // BoxLayout pays attention to maximum size, though most layout   // managers don't.   public Dimension getMaximumSize() {     return new Dimension(Integer.MAX_VALUE, getPreferredSize().height);   }   /**    * Returns the multiplier (units/meter) for the currently selected unit of    * measurement.    */   public double getMultiplier() {     return sliderModel.getMultiplier();   }   public double getValue() {     return sliderModel.getDoubleValue();   }   /** Updates the text field when the main data model is updated. */   public void stateChanged(ChangeEvent e) {     int min = sliderModel.getMinimum();     int max = sliderModel.getMaximum();     double value = sliderModel.getDoubleValue();     NumberFormatter formatter = (NumberFormatter) textField.getFormatter();     formatter.setMinimum(new Double(min));     formatter.setMaximum(new Double(max));     textField.setValue(new Double(value));   }   /**    * Responds to the user choosing a new unit from the combo box.    */   public void actionPerformed(ActionEvent e) {     // Combo box event. Set new maximums for the sliders.     int i = unitChooser.getSelectedIndex();     sliderModel.setMultiplier(units[i].multiplier);     controller.resetMaxValues(false);   }   /**    * Detects when the value of the text field (not necessarily the same number    * as you'd get from getText) changes.    */   public void propertyChange(PropertyChangeEvent e) {     if ("value".equals(e.getPropertyName())) {       Number value = (Number) e.getNewValue();       sliderModel.setDoubleValue(value.doubleValue());     }   } }