Java/Swing JFC/BoundedRangeModel

Материал из Java эксперт
Перейти к: навигация, поиск

implements BoundedRangeModel

   <source lang="java">

/*

* 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());
   }
 }

}

</source>