jdk/src/share/classes/javax/swing/JSlider.java
changeset 2 90ce3da70b43
child 438 2ae294e4518c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/swing/JSlider.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1503 @@
+/*
+ * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package javax.swing;
+
+import javax.swing.border.*;
+import javax.swing.event.*;
+import javax.swing.plaf.*;
+import javax.accessibility.*;
+
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.io.IOException;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.util.*;
+import java.beans.*;
+
+
+/**
+ * A component that lets the user graphically select a value by sliding
+ * a knob within a bounded interval.
+ * <p>
+ * The slider can show both
+ * major tick marks, and minor tick marks between the major ones.  The number of
+ * values between the tick marks is controlled with
+ * <code>setMajorTickSpacing</code> and <code>setMinorTickSpacing</code>.
+ * Painting of tick marks is controlled by {@code setPaintTicks}.
+ * <p>
+ * Sliders can also print text labels at regular intervals (or at
+ * arbitrary locations) along the slider track.  Painting of labels is
+ * controlled by {@code setLabelTable} and {@code setPaintLabels}.
+ * <p>
+ * For further information and examples see
+ * <a
+ href="http://java.sun.com/docs/books/tutorial/uiswing/components/slider.html">How to Use Sliders</a>,
+ * a section in <em>The Java Tutorial.</em>
+ * <p>
+ * <strong>Warning:</strong> Swing is not thread safe. For more
+ * information see <a
+ * href="package-summary.html#threading">Swing's Threading
+ * Policy</a>.
+ * <p>
+ * <strong>Warning:</strong>
+ * Serialized objects of this class will not be compatible with
+ * future Swing releases. The current serialization support is
+ * appropriate for short term storage or RMI between applications running
+ * the same version of Swing.  As of 1.4, support for long term storage
+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
+ * has been added to the <code>java.beans</code> package.
+ * Please see {@link java.beans.XMLEncoder}.
+ *
+ * @beaninfo
+ *      attribute: isContainer false
+ *    description: A component that supports selecting a integer value from a range.
+ *
+ * @author David Kloba
+ */
+public class JSlider extends JComponent implements SwingConstants, Accessible {
+    /**
+     * @see #getUIClassID
+     * @see #readObject
+     */
+    private static final String uiClassID = "SliderUI";
+
+    private boolean paintTicks = false;
+    private boolean paintTrack = true;
+    private boolean paintLabels = false;
+    private boolean isInverted = false;
+
+    /**
+     * The data model that handles the numeric maximum value,
+     * minimum value, and current-position value for the slider.
+     */
+    protected BoundedRangeModel sliderModel;
+
+    /**
+     * The number of values between the major tick marks -- the
+     * larger marks that break up the minor tick marks.
+     */
+    protected int majorTickSpacing;
+
+    /**
+     * The number of values between the minor tick marks -- the
+     * smaller marks that occur between the major tick marks.
+     * @see #setMinorTickSpacing
+     */
+    protected int minorTickSpacing;
+
+    /**
+     * If true, the knob (and the data value it represents)
+     * resolve to the closest tick mark next to where the user
+     * positioned the knob.  The default is false.
+     * @see #setSnapToTicks
+     */
+    protected boolean snapToTicks = false;
+
+    /**
+     * If true, the knob (and the data value it represents)
+     * resolve to the closest slider value next to where the user
+     * positioned the knob.
+     */
+    boolean snapToValue = true;
+
+    /**
+     * Whether the slider is horizontal or vertical
+     * The default is horizontal.
+     *
+     * @see #setOrientation
+     */
+    protected int orientation;
+
+
+    /**
+     * {@code Dictionary} of what labels to draw at which values
+     */
+    private Dictionary labelTable;
+
+
+    /**
+     * The changeListener (no suffix) is the listener we add to the
+     * slider's model.  This listener is initialized to the
+     * {@code ChangeListener} returned from {@code createChangeListener},
+     * which by default just forwards events
+     * to {@code ChangeListener}s (if any) added directly to the slider.
+     *
+     * @see #addChangeListener
+     * @see #createChangeListener
+     */
+    protected ChangeListener changeListener = createChangeListener();
+
+
+    /**
+     * Only one <code>ChangeEvent</code> is needed per slider instance since the
+     * event's only (read-only) state is the source property.  The source
+     * of events generated here is always "this". The event is lazily
+     * created the first time that an event notification is fired.
+     *
+     * @see #fireStateChanged
+     */
+    protected transient ChangeEvent changeEvent = null;
+
+
+    private void checkOrientation(int orientation) {
+        switch (orientation) {
+        case VERTICAL:
+        case HORIZONTAL:
+            break;
+        default:
+            throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL");
+        }
+    }
+
+
+    /**
+     * Creates a horizontal slider with the range 0 to 100 and
+     * an initial value of 50.
+     */
+    public JSlider() {
+        this(HORIZONTAL, 0, 100, 50);
+    }
+
+
+    /**
+     * Creates a slider using the specified orientation with the
+     * range {@code 0} to {@code 100} and an initial value of {@code 50}.
+     * The orientation can be
+     * either <code>SwingConstants.VERTICAL</code> or
+     * <code>SwingConstants.HORIZONTAL</code>.
+     *
+     * @param  orientation  the orientation of the slider
+     * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL}
+     * @see #setOrientation
+     */
+    public JSlider(int orientation) {
+        this(orientation, 0, 100, 50);
+    }
+
+
+    /**
+     * Creates a horizontal slider using the specified min and max
+     * with an initial value equal to the average of the min plus max.
+     * <p>
+     * The <code>BoundedRangeModel</code> that holds the slider's data
+     * handles any issues that may arise from improperly setting the
+     * minimum and maximum values on the slider.  See the
+     * {@code BoundedRangeModel} documentation for details.
+     *
+     * @param min  the minimum value of the slider
+     * @param max  the maximum value of the slider
+     *
+     * @see BoundedRangeModel
+     * @see #setMinimum
+     * @see #setMaximum
+     */
+    public JSlider(int min, int max) {
+        this(HORIZONTAL, min, max, (min + max) / 2);
+    }
+
+
+    /**
+     * Creates a horizontal slider using the specified min, max and value.
+     * <p>
+     * The <code>BoundedRangeModel</code> that holds the slider's data
+     * handles any issues that may arise from improperly setting the
+     * minimum, initial, and maximum values on the slider.  See the
+     * {@code BoundedRangeModel} documentation for details.
+     *
+     * @param min  the minimum value of the slider
+     * @param max  the maximum value of the slider
+     * @param value  the initial value of the slider
+     *
+     * @see BoundedRangeModel
+     * @see #setMinimum
+     * @see #setMaximum
+     * @see #setValue
+     */
+    public JSlider(int min, int max, int value) {
+        this(HORIZONTAL, min, max, value);
+    }
+
+
+    /**
+     * Creates a slider with the specified orientation and the
+     * specified minimum, maximum, and initial values.
+     * The orientation can be
+     * either <code>SwingConstants.VERTICAL</code> or
+     * <code>SwingConstants.HORIZONTAL</code>.
+     * <p>
+     * The <code>BoundedRangeModel</code> that holds the slider's data
+     * handles any issues that may arise from improperly setting the
+     * minimum, initial, and maximum values on the slider.  See the
+     * {@code BoundedRangeModel} documentation for details.
+     *
+     * @param orientation  the orientation of the slider
+     * @param min  the minimum value of the slider
+     * @param max  the maximum value of the slider
+     * @param value  the initial value of the slider
+     *
+     * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL}
+     *
+     * @see BoundedRangeModel
+     * @see #setOrientation
+     * @see #setMinimum
+     * @see #setMaximum
+     * @see #setValue
+     */
+    public JSlider(int orientation, int min, int max, int value)
+    {
+        checkOrientation(orientation);
+        this.orientation = orientation;
+        sliderModel = new DefaultBoundedRangeModel(value, 0, min, max);
+        sliderModel.addChangeListener(changeListener);
+        updateUI();
+    }
+
+
+    /**
+     * Creates a horizontal slider using the specified
+     * BoundedRangeModel.
+     */
+    public JSlider(BoundedRangeModel brm)
+    {
+        this.orientation = JSlider.HORIZONTAL;
+        setModel(brm);
+        sliderModel.addChangeListener(changeListener);
+        updateUI();
+    }
+
+
+    /**
+     * Gets the UI object which implements the L&F for this component.
+     *
+     * @return the SliderUI object that implements the Slider L&F
+     */
+    public SliderUI getUI() {
+        return(SliderUI)ui;
+    }
+
+
+    /**
+     * Sets the UI object which implements the L&F for this component.
+     *
+     * @param ui the SliderUI L&F object
+     * @see UIDefaults#getUI
+     * @beaninfo
+     *        bound: true
+     *       hidden: true
+     *    attribute: visualUpdate true
+     *  description: The UI object that implements the slider's LookAndFeel.
+     */
+    public void setUI(SliderUI ui) {
+        super.setUI(ui);
+    }
+
+
+    /**
+     * Resets the UI property to a value from the current look and feel.
+     *
+     * @see JComponent#updateUI
+     */
+    public void updateUI() {
+        setUI((SliderUI)UIManager.getUI(this));
+        // The labels preferred size may be derived from the font
+        // of the slider, so we must update the UI of the slider first, then
+        // that of labels.  This way when setSize is called the right
+        // font is used.
+        updateLabelUIs();
+    }
+
+
+    /**
+     * Returns the name of the L&F class that renders this component.
+     *
+     * @return "SliderUI"
+     * @see JComponent#getUIClassID
+     * @see UIDefaults#getUI
+     */
+    public String getUIClassID() {
+        return uiClassID;
+    }
+
+
+    /**
+     * We pass Change events along to the listeners with the
+     * the slider (instead of the model itself) as the event source.
+     */
+    private class ModelListener implements ChangeListener, Serializable {
+        public void stateChanged(ChangeEvent e) {
+            fireStateChanged();
+        }
+    }
+
+
+    /**
+     * Subclasses that want to handle {@code ChangeEvent}s
+     * from the model differently
+     * can override this to return
+     * an instance of a custom <code>ChangeListener</code> implementation.
+     * The default {@code ChangeListener} simply calls the
+     * {@code fireStateChanged} method to forward {@code ChangeEvent}s
+     * to the {@code ChangeListener}s that have been added directly to the
+     * slider.
+     * @see #changeListener
+     * @see #fireStateChanged
+     * @see javax.swing.event.ChangeListener
+     * @see javax.swing.BoundedRangeModel
+     */
+    protected ChangeListener createChangeListener() {
+        return new ModelListener();
+    }
+
+
+    /**
+     * Adds a ChangeListener to the slider.
+     *
+     * @param l the ChangeListener to add
+     * @see #fireStateChanged
+     * @see #removeChangeListener
+     */
+    public void addChangeListener(ChangeListener l) {
+        listenerList.add(ChangeListener.class, l);
+    }
+
+
+    /**
+     * Removes a ChangeListener from the slider.
+     *
+     * @param l the ChangeListener to remove
+     * @see #fireStateChanged
+     * @see #addChangeListener
+
+     */
+    public void removeChangeListener(ChangeListener l) {
+        listenerList.remove(ChangeListener.class, l);
+    }
+
+
+    /**
+     * Returns an array of all the <code>ChangeListener</code>s added
+     * to this JSlider with addChangeListener().
+     *
+     * @return all of the <code>ChangeListener</code>s added or an empty
+     *         array if no listeners have been added
+     * @since 1.4
+     */
+    public ChangeListener[] getChangeListeners() {
+        return (ChangeListener[])listenerList.getListeners(
+                ChangeListener.class);
+    }
+
+
+    /**
+     * Send a {@code ChangeEvent}, whose source is this {@code JSlider}, to
+     * all {@code ChangeListener}s that have registered interest in
+     * {@code ChangeEvent}s.
+     * This method is called each time a {@code ChangeEvent} is received from
+     * the model.
+     * <p>
+     * The event instance is created if necessary, and stored in
+     * {@code changeEvent}.
+     *
+     * @see #addChangeListener
+     * @see EventListenerList
+     */
+    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);
+            }
+        }
+    }
+
+
+    /**
+     * Returns the {@code BoundedRangeModel} that handles the slider's three
+     * fundamental properties: minimum, maximum, value.
+     *
+     * @return the data model for this component
+     * @see #setModel
+     * @see    BoundedRangeModel
+     */
+    public BoundedRangeModel getModel() {
+        return sliderModel;
+    }
+
+
+    /**
+     * Sets the {@code BoundedRangeModel} that handles the slider's three
+     * fundamental properties: minimum, maximum, value.
+     *<p>
+     * Attempts to pass a {@code null} model to this method result in
+     * undefined behavior, and, most likely, exceptions.
+     *
+     * @param  newModel the new, {@code non-null} <code>BoundedRangeModel</code> to use
+     *
+     * @see #getModel
+     * @see    BoundedRangeModel
+     * @beaninfo
+     *       bound: true
+     * description: The sliders BoundedRangeModel.
+     */
+    public void setModel(BoundedRangeModel newModel)
+    {
+        BoundedRangeModel oldModel = getModel();
+
+        if (oldModel != null) {
+            oldModel.removeChangeListener(changeListener);
+        }
+
+        sliderModel = newModel;
+
+        if (newModel != null) {
+            newModel.addChangeListener(changeListener);
+
+            if (accessibleContext != null) {
+                accessibleContext.firePropertyChange(
+                                                    AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
+                                                    (oldModel == null
+                                                     ? null : new Integer(oldModel.getValue())),
+                                                    (newModel == null
+                                                     ? null : new Integer(newModel.getValue())));
+            }
+        }
+
+        firePropertyChange("model", oldModel, sliderModel);
+    }
+
+
+    /**
+     * Returns the slider's current value
+     * from the {@code BoundedRangeModel}.
+     *
+     * @return  the current value of the slider
+     * @see     #setValue
+     * @see     BoundedRangeModel#getValue
+     */
+    public int getValue() {
+        return getModel().getValue();
+    }
+
+    /**
+     * Sets the slider's current value to {@code n}.  This method
+     * forwards the new value to the model.
+     * <p>
+     * The data model (an instance of {@code BoundedRangeModel})
+     * handles any mathematical
+     * issues arising from assigning faulty values.  See the
+     * {@code BoundedRangeModel} documentation for details.
+     * <p>
+     * If the new value is different from the previous value,
+     * all change listeners are notified.
+     *
+     * @param   n       the new value
+     * @see     #getValue
+     * @see     #addChangeListener
+     * @see     BoundedRangeModel#setValue
+     * @beaninfo
+     *   preferred: true
+     * description: The sliders current value.
+     */
+    public void setValue(int n) {
+        BoundedRangeModel m = getModel();
+        int oldValue = m.getValue();
+        if (oldValue == n) {
+            return;
+        }
+        m.setValue(n);
+
+        if (accessibleContext != null) {
+            accessibleContext.firePropertyChange(
+                                                AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
+                                                new Integer(oldValue),
+                                                new Integer(m.getValue()));
+        }
+    }
+
+
+    /**
+     * Returns the minimum value supported by the slider
+     * from the <code>BoundedRangeModel</code>.
+     *
+     * @return the value of the model's minimum property
+     * @see #setMinimum
+     * @see     BoundedRangeModel#getMinimum
+     */
+    public int getMinimum() {
+        return getModel().getMinimum();
+    }
+
+
+    /**
+     * Sets the slider's minimum value to {@code minimum}.  This method
+     * forwards the new minimum value to the model.
+     * <p>
+     * The data model (an instance of {@code BoundedRangeModel})
+     * handles any mathematical
+     * issues arising from assigning faulty values.  See the
+     * {@code BoundedRangeModel} documentation for details.
+     * <p>
+     * If the new minimum value is different from the previous minimum value,
+     * all change listeners are notified.
+     *
+     * @param minimum  the new minimum
+     * @see #getMinimum
+     * @see    #addChangeListener
+     * @see BoundedRangeModel#setMinimum
+     * @beaninfo
+     *       bound: true
+     *   preferred: true
+     * description: The sliders minimum value.
+     */
+    public void setMinimum(int minimum) {
+        int oldMin = getModel().getMinimum();
+        getModel().setMinimum(minimum);
+        firePropertyChange( "minimum", new Integer( oldMin ), new Integer( minimum ) );
+    }
+
+
+    /**
+     * Returns the maximum value supported by the slider
+     * from the <code>BoundedRangeModel</code>.
+     *
+     * @return the value of the model's maximum property
+     * @see #setMaximum
+     * @see BoundedRangeModel#getMaximum
+     */
+    public int getMaximum() {
+        return getModel().getMaximum();
+    }
+
+
+    /**
+     * Sets the slider's maximum value to {@code maximum}.  This method
+     * forwards the new maximum value to the model.
+     * <p>
+     * The data model (an instance of {@code BoundedRangeModel})
+     * handles any mathematical
+     * issues arising from assigning faulty values.  See the
+     * {@code BoundedRangeModel} documentation for details.
+     * <p>
+     * If the new maximum value is different from the previous maximum value,
+     * all change listeners are notified.
+     *
+     * @param maximum  the new maximum
+     * @see #getMaximum
+     * @see #addChangeListener
+     * @see BoundedRangeModel#setMaximum
+     * @beaninfo
+     *       bound: true
+     *   preferred: true
+     * description: The sliders maximum value.
+     */
+    public void setMaximum(int maximum) {
+        int oldMax = getModel().getMaximum();
+        getModel().setMaximum(maximum);
+        firePropertyChange( "maximum", new Integer( oldMax ), new Integer( maximum ) );
+    }
+
+
+    /**
+     * Returns the {@code valueIsAdjusting} property from the model.  For
+     * details on how this is used, see the {@code setValueIsAdjusting}
+     * documentation.
+     *
+     * @return the value of the model's {@code valueIsAdjusting} property
+     * @see #setValueIsAdjusting
+     */
+    public boolean getValueIsAdjusting() {
+        return getModel().getValueIsAdjusting();
+    }
+
+
+    /**
+     * Sets the model's {@code valueIsAdjusting} property.  Slider look and
+     * feel implementations should set this property to {@code true} when
+     * a knob drag begins, and to {@code false} when the drag ends.  The
+     * slider model will not generate {@code ChangeEvent}s while
+     * {@code valueIsAdjusting} is {@code true}.
+     *
+     * @param b the new value for the {@code valueIsAdjusting} property
+     * @see   #getValueIsAdjusting
+     * @see   BoundedRangeModel#setValueIsAdjusting
+     * @beaninfo
+     *      expert: true
+     * description: True if the slider knob is being dragged.
+     */
+    public void setValueIsAdjusting(boolean b) {
+        BoundedRangeModel m = getModel();
+        boolean oldValue = m.getValueIsAdjusting();
+        m.setValueIsAdjusting(b);
+
+        if ((oldValue != b) && (accessibleContext != null)) {
+            accessibleContext.firePropertyChange(
+                                                AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+                                                ((oldValue) ? AccessibleState.BUSY : null),
+                                                ((b) ? AccessibleState.BUSY : null));
+        }
+    }
+
+
+    /**
+     * Returns the "extent" from the <code>BoundedRangeModel</code>.
+     * This respresents the range of values "covered" by the knob.
+     *
+     * @return an int representing the extent
+     * @see #setExtent
+     * @see BoundedRangeModel#getExtent
+     */
+    public int getExtent() {
+        return getModel().getExtent();
+    }
+
+
+    /**
+     * Sets the size of the range "covered" by the knob.  Most look
+     * and feel implementations will change the value by this amount
+     * if the user clicks on either side of the knob.  This method just
+     * forwards the new extent value to the model.
+     * <p>
+     * The data model (an instance of {@code BoundedRangeModel})
+     * handles any mathematical
+     * issues arising from assigning faulty values.  See the
+     * {@code BoundedRangeModel} documentation for details.
+     * <p>
+     * If the new extent value is different from the previous extent value,
+     * all change listeners are notified.
+     *
+     * @param extent the new extent
+     * @see   #getExtent
+     * @see   BoundedRangeModel#setExtent
+     * @beaninfo
+     *      expert: true
+     * description: Size of the range covered by the knob.
+     */
+    public void setExtent(int extent) {
+        getModel().setExtent(extent);
+    }
+
+
+    /**
+     * Return this slider's vertical or horizontal orientation.
+     * @return {@code SwingConstants.VERTICAL} or
+     *  {@code SwingConstants.HORIZONTAL}
+     * @see #setOrientation
+     */
+    public int getOrientation() {
+        return orientation;
+    }
+
+
+    /**
+     * Set the slider's orientation to either {@code SwingConstants.VERTICAL} or
+     * {@code SwingConstants.HORIZONTAL}.
+     *
+     * @param orientation {@code HORIZONTAL} or {@code VERTICAL}
+     * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL}
+     * @see #getOrientation
+     * @beaninfo
+     *    preferred: true
+     *        bound: true
+     *    attribute: visualUpdate true
+     *  description: Set the scrollbars orientation to either VERTICAL or HORIZONTAL.
+     *         enum: VERTICAL JSlider.VERTICAL
+     *               HORIZONTAL JSlider.HORIZONTAL
+     *
+     */
+    public void setOrientation(int orientation)
+    {
+        checkOrientation(orientation);
+        int oldValue = this.orientation;
+        this.orientation = orientation;
+        firePropertyChange("orientation", oldValue, orientation);
+
+        if ((oldValue != orientation) && (accessibleContext != null)) {
+            accessibleContext.firePropertyChange(
+                                                AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+                                                ((oldValue == VERTICAL)
+                                                 ? AccessibleState.VERTICAL : AccessibleState.HORIZONTAL),
+                                                ((orientation == VERTICAL)
+                                                 ? AccessibleState.VERTICAL : AccessibleState.HORIZONTAL));
+        }
+        if (orientation != oldValue) {
+            revalidate();
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     *
+     * @since 1.6
+     */
+    public void setFont(Font font) {
+        super.setFont(font);
+        updateLabelSizes();
+    }
+
+
+    /**
+     * Returns the dictionary of what labels to draw at which values.
+     *
+     * @return the <code>Dictionary</code> containing labels and
+     *    where to draw them
+     */
+    public Dictionary getLabelTable() {
+/*
+        if ( labelTable == null && getMajorTickSpacing() > 0 ) {
+            setLabelTable( createStandardLabels( getMajorTickSpacing() ) );
+        }
+*/
+        return labelTable;
+    }
+
+
+    /**
+     * Used to specify what label will be drawn at any given value.
+     * The key-value pairs are of this format:
+     * <code>{ Integer value, java.swing.JComponent label }</code>.
+     * <p>
+     * An easy way to generate a standard table of value labels is by using the
+     * {@code createStandardLabels} method.
+     * <p>
+     * Once the labels have been set, this method calls {@link #updateLabelUIs}.
+     * Note that the labels are only painted if the {@code paintLabels}
+     * property is {@code true}.
+     *
+     * @param labels new {@code Dictionary} of labels, or {@code null} to
+     *        remove all labels
+     * @see #createStandardLabels(int)
+     * @see #getLabelTable
+     * @see #setPaintLabels
+     * @beaninfo
+     *       hidden: true
+     *        bound: true
+     *    attribute: visualUpdate true
+     *  description: Specifies what labels will be drawn for any given value.
+     */
+    public void setLabelTable( Dictionary labels ) {
+        Dictionary oldTable = labelTable;
+        labelTable = labels;
+        updateLabelUIs();
+        firePropertyChange("labelTable", oldTable, labelTable );
+        if (labels != oldTable) {
+            revalidate();
+            repaint();
+        }
+    }
+
+
+    /**
+     * Updates the UIs for the labels in the label table by calling
+     * {@code updateUI} on each label.  The UIs are updated from
+     * the current look and feel.  The labels are also set to their
+     * preferred size.
+     *
+     * @see #setLabelTable
+     * @see JComponent#updateUI
+     */
+    protected void updateLabelUIs() {
+        if ( getLabelTable() == null ) {
+            return;
+        }
+        Enumeration labels = getLabelTable().keys();
+        while ( labels.hasMoreElements() ) {
+            Object value = getLabelTable().get( labels.nextElement() );
+            if ( value instanceof JComponent ) {
+                JComponent component = (JComponent)value;
+                component.updateUI();
+                component.setSize( component.getPreferredSize()  );
+            }
+        }
+    }
+
+    private void updateLabelSizes() {
+        Dictionary labelTable = getLabelTable();
+        if (labelTable != null) {
+            Enumeration labels = labelTable.elements();
+            while (labels.hasMoreElements()) {
+                Object value = labels.nextElement();
+                if (value instanceof JComponent) {
+                    JComponent component = (JComponent)value;
+                    component.setSize(component.getPreferredSize());
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Creates a {@code Hashtable} of numerical text labels, starting at the
+     * slider minimum, and using the increment specified.
+     * For example, if you call <code>createStandardLabels( 10 )</code>
+     * and the slider minimum is zero,
+     * then labels will be created for the values 0, 10, 20, 30, and so on.
+     * <p>
+     * For the labels to be drawn on the slider, the returned {@code Hashtable}
+     * must be passed into {@code setLabelTable}, and {@code setPaintLabels}
+     * must be set to {@code true}.
+     * <p>
+     * For further details on the makeup of the returned {@code Hashtable}, see
+     * the {@code setLabelTable} documentation.
+     *
+     * @param  increment  distance between labels in the generated hashtable
+     * @return a new {@code Hashtable} of labels
+     * @see #setLabelTable
+     * @see #setPaintLabels
+     * @throws IllegalArgumentException if {@code increment} is less than or
+     *          equal to zero
+     */
+    public Hashtable createStandardLabels( int increment ) {
+        return createStandardLabels( increment, getMinimum() );
+    }
+
+
+    /**
+     * Creates a {@code Hashtable} of numerical text labels, starting at the
+     * starting point specified, and using the increment specified.
+     * For example, if you call
+     * <code>createStandardLabels( 10, 2 )</code>,
+     * then labels will be created for the values 2, 12, 22, 32, and so on.
+     * <p>
+     * For the labels to be drawn on the slider, the returned {@code Hashtable}
+     * must be passed into {@code setLabelTable}, and {@code setPaintLabels}
+     * must be set to {@code true}.
+     * <p>
+     * For further details on the makeup of the returned {@code Hashtable}, see
+     * the {@code setLabelTable} documentation.
+     *
+     * @param  increment  distance between labels in the generated hashtable
+     * @param  start      value at which the labels will begin
+     * @return a new {@code Hashtable} of labels
+     * @see #setLabelTable
+     * @see #setPaintLabels
+     * @exception IllegalArgumentException if {@code start} is
+     *          out of range, or if {@code increment} is less than or equal
+     *          to zero
+     */
+    public Hashtable createStandardLabels( int increment, int start ) {
+        if ( start > getMaximum() || start < getMinimum() ) {
+            throw new IllegalArgumentException( "Slider label start point out of range." );
+        }
+
+        if ( increment <= 0 ) {
+            throw new IllegalArgumentException( "Label incremement must be > 0" );
+        }
+
+        class SmartHashtable extends Hashtable implements PropertyChangeListener {
+            int increment = 0;
+            int start = 0;
+            boolean startAtMin = false;
+
+            class LabelUIResource extends JLabel implements UIResource {
+                public LabelUIResource( String text, int alignment ) {
+                    super( text, alignment );
+                    setName("Slider.label");
+                }
+
+                public Font getFont() {
+                    Font font = super.getFont();
+                    if (font != null && !(font instanceof UIResource)) {
+                        return font;
+                    }
+                    return JSlider.this.getFont();
+                }
+
+                public Color getForeground() {
+                    Color fg = super.getForeground();
+                    if (fg != null && !(fg instanceof UIResource)) {
+                        return fg;
+                    }
+                    if (!(JSlider.this.getForeground() instanceof UIResource)) {
+                        return JSlider.this.getForeground();
+                    }
+                    return fg;
+                }
+            }
+
+            public SmartHashtable( int increment, int start ) {
+                super();
+                this.increment = increment;
+                this.start = start;
+                startAtMin = start == getMinimum();
+                createLabels();
+            }
+
+            public void propertyChange( PropertyChangeEvent e ) {
+                if ( e.getPropertyName().equals( "minimum" ) && startAtMin ) {
+                    start = getMinimum();
+                }
+
+                if ( e.getPropertyName().equals( "minimum" ) ||
+                     e.getPropertyName().equals( "maximum" ) ) {
+
+                    Enumeration keys = getLabelTable().keys();
+                    Object key = null;
+                    Hashtable hashtable = new Hashtable();
+
+                    // Save the labels that were added by the developer
+                    while ( keys.hasMoreElements() ) {
+                        key = keys.nextElement();
+                        Object value = getLabelTable().get( key );
+                        if ( !(value instanceof LabelUIResource) ) {
+                            hashtable.put( key, value );
+                        }
+                    }
+
+                    clear();
+                    createLabels();
+
+                    // Add the saved labels
+                    keys = hashtable.keys();
+                    while ( keys.hasMoreElements() ) {
+                        key = keys.nextElement();
+                        put( key, hashtable.get( key ) );
+                    }
+
+                    ((JSlider)e.getSource()).setLabelTable( this );
+                }
+            }
+
+            void createLabels() {
+                for ( int labelIndex = start; labelIndex <= getMaximum(); labelIndex += increment ) {
+                    put( new Integer( labelIndex ), new LabelUIResource( ""+labelIndex, JLabel.CENTER ) );
+                }
+            }
+        }
+
+        SmartHashtable table = new SmartHashtable( increment, start );
+
+        if ( getLabelTable() != null && (getLabelTable() instanceof PropertyChangeListener) ) {
+            removePropertyChangeListener( (PropertyChangeListener)getLabelTable() );
+        }
+
+        addPropertyChangeListener( table );
+
+        return table;
+    }
+
+
+    /**
+     * Returns true if the value-range shown for the slider is reversed,
+     *
+     * @return true if the slider values are reversed from their normal order
+     * @see #setInverted
+     */
+    public boolean getInverted() {
+        return isInverted;
+    }
+
+
+    /**
+     * Specify true to reverse the value-range shown for the slider and false to
+     * put the value range in the normal order.  The order depends on the
+     * slider's <code>ComponentOrientation</code> property.  Normal (non-inverted)
+     * horizontal sliders with a <code>ComponentOrientation</code> value of
+     * <code>LEFT_TO_RIGHT</code> have their maximum on the right.
+     * Normal horizontal sliders with a <code>ComponentOrientation</code> value of
+     * <code>RIGHT_TO_LEFT</code> have their maximum on the left.  Normal vertical
+     * sliders have their maximum on the top.  These labels are reversed when the
+     * slider is inverted.
+     * <p>
+     * By default, the value of this property is {@code false}.
+     *
+     * @param b  true to reverse the slider values from their normal order
+     * @beaninfo
+     *        bound: true
+     *    attribute: visualUpdate true
+     *  description: If true reverses the slider values from their normal order
+     *
+     */
+    public void setInverted( boolean b ) {
+        boolean oldValue = isInverted;
+        isInverted = b;
+        firePropertyChange("inverted", oldValue, isInverted);
+        if (b != oldValue) {
+            repaint();
+        }
+    }
+
+
+    /**
+     * This method returns the major tick spacing.  The number that is returned
+     * represents the distance, measured in values, between each major tick mark.
+     * If you have a slider with a range from 0 to 50 and the major tick spacing
+     * is set to 10, you will get major ticks next to the following values:
+     * 0, 10, 20, 30, 40, 50.
+     *
+     * @return the number of values between major ticks
+     * @see #setMajorTickSpacing
+     */
+    public int getMajorTickSpacing() {
+        return majorTickSpacing;
+    }
+
+
+    /**
+     * This method sets the major tick spacing.  The number that is passed in
+     * represents the distance, measured in values, between each major tick mark.
+     * If you have a slider with a range from 0 to 50 and the major tick spacing
+     * is set to 10, you will get major ticks next to the following values:
+     * 0, 10, 20, 30, 40, 50.
+     * <p>
+     * In order for major ticks to be painted, {@code setPaintTicks} must be
+     * set to {@code true}.
+     * <p>
+     * This method will also set up a label table for you.
+     * If there is not already a label table, and the major tick spacing is
+     * {@code > 0}, and {@code getPaintLabels} returns
+     * {@code true}, a standard label table will be generated (by calling
+     * {@code createStandardLabels}) with labels at the major tick marks.
+     * For the example above, you would get text labels: "0",
+     * "10", "20", "30", "40", "50".
+     * The label table is then set on the slider by calling
+     * {@code setLabelTable}.
+     *
+     * @param  n  new value for the {@code majorTickSpacing} property
+     * @see #getMajorTickSpacing
+     * @see #setPaintTicks
+     * @see #setLabelTable
+     * @see #createStandardLabels(int)
+     * @beaninfo
+     *        bound: true
+     *    attribute: visualUpdate true
+     *  description: Sets the number of values between major tick marks.
+     *
+     */
+    public void setMajorTickSpacing(int n) {
+        int oldValue = majorTickSpacing;
+        majorTickSpacing = n;
+        if ( labelTable == null && getMajorTickSpacing() > 0 && getPaintLabels() ) {
+            setLabelTable( createStandardLabels( getMajorTickSpacing() ) );
+        }
+        firePropertyChange("majorTickSpacing", oldValue, majorTickSpacing);
+        if (majorTickSpacing != oldValue && getPaintTicks()) {
+            repaint();
+        }
+    }
+
+
+
+    /**
+     * This method returns the minor tick spacing.  The number that is returned
+     * represents the distance, measured in values, between each minor tick mark.
+     * If you have a slider with a range from 0 to 50 and the minor tick spacing
+     * is set to 10, you will get minor ticks next to the following values:
+     * 0, 10, 20, 30, 40, 50.
+     *
+     * @return the number of values between minor ticks
+     * @see #getMinorTickSpacing
+     */
+    public int getMinorTickSpacing() {
+        return minorTickSpacing;
+    }
+
+
+    /**
+     * This method sets the minor tick spacing.  The number that is passed in
+     * represents the distance, measured in values, between each minor tick mark.
+     * If you have a slider with a range from 0 to 50 and the minor tick spacing
+     * is set to 10, you will get minor ticks next to the following values:
+     * 0, 10, 20, 30, 40, 50.
+     * <p>
+     * In order for minor ticks to be painted, {@code setPaintTicks} must be
+     * set to {@code true}.
+     *
+     * @param  n  new value for the {@code minorTickSpacing} property
+     * @see #getMinorTickSpacing
+     * @see #setPaintTicks
+     * @beaninfo
+     *        bound: true
+     *    attribute: visualUpdate true
+     *  description: Sets the number of values between minor tick marks.
+     */
+    public void setMinorTickSpacing(int n) {
+        int oldValue = minorTickSpacing;
+        minorTickSpacing = n;
+        firePropertyChange("minorTickSpacing", oldValue, minorTickSpacing);
+        if (minorTickSpacing != oldValue && getPaintTicks()) {
+            repaint();
+        }
+    }
+
+
+    /**
+     * Returns true if the knob (and the data value it represents)
+     * resolve to the closest tick mark next to where the user
+     * positioned the knob.
+     *
+     * @return true if the value snaps to the nearest tick mark, else false
+     * @see #setSnapToTicks
+     */
+    public boolean getSnapToTicks() {
+        return snapToTicks;
+    }
+
+
+    /**
+     * Returns true if the knob (and the data value it represents)
+     * resolve to the closest slider value next to where the user
+     * positioned the knob.
+     *
+     * @return true if the value snaps to the nearest slider value, else false
+     * @see #setSnapToValue
+     */
+    boolean getSnapToValue() {
+        return snapToValue;
+    }
+
+
+    /**
+     * Specifying true makes the knob (and the data value it represents)
+     * resolve to the closest tick mark next to where the user
+     * positioned the knob.
+     * By default, this property is {@code false}.
+     *
+     * @param b  true to snap the knob to the nearest tick mark
+     * @see #getSnapToTicks
+     * @beaninfo
+     *       bound: true
+     * description: If true snap the knob to the nearest tick mark.
+     */
+    public void setSnapToTicks(boolean b) {
+        boolean oldValue = snapToTicks;
+        snapToTicks = b;
+        firePropertyChange("snapToTicks", oldValue, snapToTicks);
+    }
+
+
+    /**
+     * Specifying true makes the knob (and the data value it represents)
+     * resolve to the closest slider value next to where the user
+     * positioned the knob. If the {@code snapToTicks} property has also been
+     * set to {@code true}, the snap-to-ticks behavior will prevail.
+     * By default, the snapToValue property is {@code true}.
+     *
+     * @param b  true to snap the knob to the nearest slider value
+     * @see #getSnapToValue
+     * @see #setSnapToTicks
+     * @beaninfo
+     *       bound: true
+     * description: If true snap the knob to the nearest slider value.
+     */
+    void setSnapToValue(boolean b) {
+        boolean oldValue = snapToValue;
+        snapToValue = b;
+        firePropertyChange("snapToValue", oldValue, snapToValue);
+    }
+
+
+    /**
+     * Tells if tick marks are to be painted.
+     * @return true if tick marks are painted, else false
+     * @see #setPaintTicks
+     */
+    public boolean getPaintTicks() {
+        return paintTicks;
+    }
+
+
+    /**
+     * Determines whether tick marks are painted on the slider.
+     * By default, this property is {@code false}.
+     *
+     * @param  b  whether or not tick marks should be painted
+     * @see #getPaintTicks
+     * @beaninfo
+     *        bound: true
+     *    attribute: visualUpdate true
+     *  description: If true tick marks are painted on the slider.
+     */
+    public void setPaintTicks(boolean b) {
+        boolean oldValue = paintTicks;
+        paintTicks = b;
+        firePropertyChange("paintTicks", oldValue, paintTicks);
+        if (paintTicks != oldValue) {
+            revalidate();
+            repaint();
+        }
+    }
+
+    /**
+     * Tells if the track (area the slider slides in) is to be painted.
+     * @return true if track is painted, else false
+     * @see #setPaintTrack
+     */
+    public boolean getPaintTrack() {
+        return paintTrack;
+    }
+
+
+    /**
+     * Determines whether the track is painted on the slider.
+     * By default, this property is {@code true}.
+     *
+     * @param  b  whether or not to paint the slider track
+     * @see #getPaintTrack
+     * @beaninfo
+     *        bound: true
+     *    attribute: visualUpdate true
+     *  description: If true, the track is painted on the slider.
+     */
+    public void setPaintTrack(boolean b) {
+        boolean oldValue = paintTrack;
+        paintTrack = b;
+        firePropertyChange("paintTrack", oldValue, paintTrack);
+        if (paintTrack != oldValue) {
+            repaint();
+        }
+    }
+
+
+    /**
+     * Tells if labels are to be painted.
+     * @return true if labels are painted, else false
+     * @see #setPaintLabels
+     */
+    public boolean getPaintLabels() {
+        return paintLabels;
+    }
+
+
+    /**
+     * Determines whether labels are painted on the slider.
+     * <p>
+     * This method will also set up a label table for you.
+     * If there is not already a label table, and the major tick spacing is
+     * {@code > 0},
+     * a standard label table will be generated (by calling
+     * {@code createStandardLabels}) with labels at the major tick marks.
+     * The label table is then set on the slider by calling
+     * {@code setLabelTable}.
+     * <p>
+     * By default, this property is {@code false}.
+     *
+     * @param  b  whether or not to paint labels
+     * @see #getPaintLabels
+     * @see #getLabelTable
+     * @see #createStandardLabels(int)
+     * @beaninfo
+     *        bound: true
+     *    attribute: visualUpdate true
+     *  description: If true labels are painted on the slider.
+     */
+    public void setPaintLabels(boolean b) {
+        boolean oldValue = paintLabels;
+        paintLabels = b;
+        if ( labelTable == null && getMajorTickSpacing() > 0 ) {
+            setLabelTable( createStandardLabels( getMajorTickSpacing() ) );
+        }
+        firePropertyChange("paintLabels", oldValue, paintLabels);
+        if (paintLabels != oldValue) {
+            revalidate();
+            repaint();
+        }
+    }
+
+
+    /**
+     * See readObject() and writeObject() in JComponent for more
+     * information about serialization in Swing.
+     */
+    private void writeObject(ObjectOutputStream s) throws IOException {
+        s.defaultWriteObject();
+        if (getUIClassID().equals(uiClassID)) {
+            byte count = JComponent.getWriteObjCounter(this);
+            JComponent.setWriteObjCounter(this, --count);
+            if (count == 0 && ui != null) {
+                ui.installUI(this);
+            }
+        }
+    }
+
+
+    /**
+     * Returns a string representation of this JSlider. This method
+     * is intended to be used only for debugging purposes, and the
+     * content and format of the returned string may vary between
+     * implementations. The returned string may be empty but may not
+     * be <code>null</code>.
+     *
+     * @return  a string representation of this JSlider.
+     */
+    protected String paramString() {
+        String paintTicksString = (paintTicks ?
+                                   "true" : "false");
+        String paintTrackString = (paintTrack ?
+                                   "true" : "false");
+        String paintLabelsString = (paintLabels ?
+                                    "true" : "false");
+        String isInvertedString = (isInverted ?
+                                   "true" : "false");
+        String snapToTicksString = (snapToTicks ?
+                                    "true" : "false");
+        String snapToValueString = (snapToValue ?
+                                    "true" : "false");
+        String orientationString = (orientation == HORIZONTAL ?
+                                    "HORIZONTAL" : "VERTICAL");
+
+        return super.paramString() +
+        ",isInverted=" + isInvertedString +
+        ",majorTickSpacing=" + majorTickSpacing +
+        ",minorTickSpacing=" + minorTickSpacing +
+        ",orientation=" + orientationString +
+        ",paintLabels=" + paintLabelsString +
+        ",paintTicks=" + paintTicksString +
+        ",paintTrack=" + paintTrackString +
+        ",snapToTicks=" + snapToTicksString +
+        ",snapToValue=" + snapToValueString;
+    }
+
+
+/////////////////
+// Accessibility support
+////////////////
+
+    /**
+     * Gets the AccessibleContext associated with this JSlider.
+     * For sliders, the AccessibleContext takes the form of an
+     * AccessibleJSlider.
+     * A new AccessibleJSlider instance is created if necessary.
+     *
+     * @return an AccessibleJSlider that serves as the
+     *         AccessibleContext of this JSlider
+     */
+    public AccessibleContext getAccessibleContext() {
+        if (accessibleContext == null) {
+            accessibleContext = new AccessibleJSlider();
+        }
+        return accessibleContext;
+    }
+
+    /**
+     * This class implements accessibility support for the
+     * <code>JSlider</code> class.  It provides an implementation of the
+     * Java Accessibility API appropriate to slider user-interface elements.
+     * <p>
+     * <strong>Warning:</strong>
+     * Serialized objects of this class will not be compatible with
+     * future Swing releases. The current serialization support is
+     * appropriate for short term storage or RMI between applications running
+     * the same version of Swing.  As of 1.4, support for long term storage
+     * of all JavaBeans<sup><font size="-2">TM</font></sup>
+     * has been added to the <code>java.beans</code> package.
+     * Please see {@link java.beans.XMLEncoder}.
+     */
+    protected class AccessibleJSlider extends AccessibleJComponent
+    implements AccessibleValue {
+
+        /**
+         * Get the state set of this object.
+         *
+         * @return an instance of AccessibleState containing the current state
+         * of the object
+         * @see AccessibleState
+         */
+        public AccessibleStateSet getAccessibleStateSet() {
+            AccessibleStateSet states = super.getAccessibleStateSet();
+            if (getValueIsAdjusting()) {
+                states.add(AccessibleState.BUSY);
+            }
+            if (getOrientation() == VERTICAL) {
+                states.add(AccessibleState.VERTICAL);
+            }
+            else {
+                states.add(AccessibleState.HORIZONTAL);
+            }
+            return states;
+        }
+
+        /**
+         * Get the role of this object.
+         *
+         * @return an instance of AccessibleRole describing the role of the object
+         */
+        public AccessibleRole getAccessibleRole() {
+            return AccessibleRole.SLIDER;
+        }
+
+        /**
+         * Get the AccessibleValue associated with this object.  In the
+         * implementation of the Java Accessibility API for this class,
+         * return this object, which is responsible for implementing the
+         * AccessibleValue interface on behalf of itself.
+         *
+         * @return this object
+         */
+        public AccessibleValue getAccessibleValue() {
+            return this;
+        }
+
+        /**
+         * Get the accessible value of this object.
+         *
+         * @return The current value of this object.
+         */
+        public Number getCurrentAccessibleValue() {
+            return new Integer(getValue());
+        }
+
+        /**
+         * Set the value of this object as a Number.
+         *
+         * @return True if the value was set.
+         */
+        public boolean setCurrentAccessibleValue(Number n) {
+            // TIGER - 4422535
+            if (n == null) {
+                return false;
+            }
+            setValue(n.intValue());
+            return true;
+        }
+
+        /**
+         * Get the minimum accessible value of this object.
+         *
+         * @return The minimum value of this object.
+         */
+        public Number getMinimumAccessibleValue() {
+            return new Integer(getMinimum());
+        }
+
+        /**
+         * Get the maximum accessible value of this object.
+         *
+         * @return The maximum value of this object.
+         */
+        public Number getMaximumAccessibleValue() {
+            // TIGER - 4422362
+            BoundedRangeModel model = JSlider.this.getModel();
+            return new Integer(model.getMaximum() - model.getExtent());
+        }
+    } // AccessibleJSlider
+}