1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing;
  27 
  28 import javax.swing.event.*;
  29 import javax.swing.plaf.*;
  30 import javax.accessibility.*;
  31 
  32 import java.io.Serializable;
  33 import java.io.ObjectOutputStream;
  34 import java.io.IOException;
  35 
  36 import java.awt.*;
  37 import java.util.*;
  38 import java.beans.*;
  39 
  40 
  41 /**
  42  * A component that lets the user graphically select a value by sliding
  43  * a knob within a bounded interval. The knob is always positioned
  44  * at the points that match integer values within the specified interval.
  45  * <p>
  46  * The slider can show both
  47  * major tick marks, and minor tick marks between the major ones.  The number of
  48  * values between the tick marks is controlled with
  49  * <code>setMajorTickSpacing</code> and <code>setMinorTickSpacing</code>.
  50  * Painting of tick marks is controlled by {@code setPaintTicks}.
  51  * <p>
  52  * Sliders can also print text labels at regular intervals (or at
  53  * arbitrary locations) along the slider track.  Painting of labels is
  54  * controlled by {@code setLabelTable} and {@code setPaintLabels}.
  55  * <p>
  56  * For further information and examples see
  57  * <a
  58  href="http://docs.oracle.com/javase/tutorial/uiswing/components/slider.html">How to Use Sliders</a>,
  59  * a section in <em>The Java Tutorial.</em>
  60  * <p>
  61  * <strong>Warning:</strong> Swing is not thread safe. For more
  62  * information see <a
  63  * href="package-summary.html#threading">Swing's Threading
  64  * Policy</a>.
  65  * <p>
  66  * <strong>Warning:</strong>
  67  * Serialized objects of this class will not be compatible with
  68  * future Swing releases. The current serialization support is
  69  * appropriate for short term storage or RMI between applications running
  70  * the same version of Swing.  As of 1.4, support for long term storage
  71  * of all JavaBeans&trade;
  72  * has been added to the <code>java.beans</code> package.
  73  * Please see {@link java.beans.XMLEncoder}.
  74  *
  75  * @beaninfo
  76  *      attribute: isContainer false
  77  *    description: A component that supports selecting a integer value from a range.
  78  *
  79  * @author David Kloba
  80  */
  81 @SuppressWarnings("serial") // Same-version serialization only
  82 public class JSlider extends JComponent implements SwingConstants, Accessible {
  83     /**
  84      * @see #getUIClassID
  85      * @see #readObject
  86      */
  87     private static final String uiClassID = "SliderUI";
  88 
  89     private boolean paintTicks = false;
  90     private boolean paintTrack = true;
  91     private boolean paintLabels = false;
  92     private boolean isInverted = false;
  93 
  94     /**
  95      * The data model that handles the numeric maximum value,
  96      * minimum value, and current-position value for the slider.
  97      */
  98     protected BoundedRangeModel sliderModel;
  99 
 100     /**
 101      * The number of values between the major tick marks -- the
 102      * larger marks that break up the minor tick marks.
 103      */
 104     protected int majorTickSpacing;
 105 
 106     /**
 107      * The number of values between the minor tick marks -- the
 108      * smaller marks that occur between the major tick marks.
 109      * @see #setMinorTickSpacing
 110      */
 111     protected int minorTickSpacing;
 112 
 113     /**
 114      * If true, the knob (and the data value it represents)
 115      * resolve to the closest tick mark next to where the user
 116      * positioned the knob.  The default is false.
 117      * @see #setSnapToTicks
 118      */
 119     protected boolean snapToTicks = false;
 120 
 121     /**
 122      * If true, the knob (and the data value it represents)
 123      * resolve to the closest slider value next to where the user
 124      * positioned the knob.
 125      */
 126     boolean snapToValue = true;
 127 
 128     /**
 129      * Whether the slider is horizontal or vertical
 130      * The default is horizontal.
 131      *
 132      * @see #setOrientation
 133      */
 134     protected int orientation;
 135 
 136 
 137     /**
 138      * {@code Dictionary} of what labels to draw at which values
 139      */
 140     private Dictionary labelTable;
 141 
 142 
 143     /**
 144      * The changeListener (no suffix) is the listener we add to the
 145      * slider's model.  This listener is initialized to the
 146      * {@code ChangeListener} returned from {@code createChangeListener},
 147      * which by default just forwards events
 148      * to {@code ChangeListener}s (if any) added directly to the slider.
 149      *
 150      * @see #addChangeListener
 151      * @see #createChangeListener
 152      */
 153     protected ChangeListener changeListener = createChangeListener();
 154 
 155 
 156     /**
 157      * Only one <code>ChangeEvent</code> is needed per slider instance since the
 158      * event's only (read-only) state is the source property.  The source
 159      * of events generated here is always "this". The event is lazily
 160      * created the first time that an event notification is fired.
 161      *
 162      * @see #fireStateChanged
 163      */
 164     protected transient ChangeEvent changeEvent = null;
 165 
 166 
 167     private void checkOrientation(int orientation) {
 168         switch (orientation) {
 169         case VERTICAL:
 170         case HORIZONTAL:
 171             break;
 172         default:
 173             throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL");
 174         }
 175     }
 176 
 177 
 178     /**
 179      * Creates a horizontal slider with the range 0 to 100 and
 180      * an initial value of 50.
 181      */
 182     public JSlider() {
 183         this(HORIZONTAL, 0, 100, 50);
 184     }
 185 
 186 
 187     /**
 188      * Creates a slider using the specified orientation with the
 189      * range {@code 0} to {@code 100} and an initial value of {@code 50}.
 190      * The orientation can be
 191      * either <code>SwingConstants.VERTICAL</code> or
 192      * <code>SwingConstants.HORIZONTAL</code>.
 193      *
 194      * @param  orientation  the orientation of the slider
 195      * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL}
 196      * @see #setOrientation
 197      */
 198     public JSlider(int orientation) {
 199         this(orientation, 0, 100, 50);
 200     }
 201 
 202 
 203     /**
 204      * Creates a horizontal slider using the specified min and max
 205      * with an initial value equal to the average of the min plus max.
 206      * <p>
 207      * The <code>BoundedRangeModel</code> that holds the slider's data
 208      * handles any issues that may arise from improperly setting the
 209      * minimum and maximum values on the slider.  See the
 210      * {@code BoundedRangeModel} documentation for details.
 211      *
 212      * @param min  the minimum value of the slider
 213      * @param max  the maximum value of the slider
 214      *
 215      * @see BoundedRangeModel
 216      * @see #setMinimum
 217      * @see #setMaximum
 218      */
 219     public JSlider(int min, int max) {
 220         this(HORIZONTAL, min, max, (min + max) / 2);
 221     }
 222 
 223 
 224     /**
 225      * Creates a horizontal slider using the specified min, max and value.
 226      * <p>
 227      * The <code>BoundedRangeModel</code> that holds the slider's data
 228      * handles any issues that may arise from improperly setting the
 229      * minimum, initial, and maximum values on the slider.  See the
 230      * {@code BoundedRangeModel} documentation for details.
 231      *
 232      * @param min  the minimum value of the slider
 233      * @param max  the maximum value of the slider
 234      * @param value  the initial value of the slider
 235      *
 236      * @see BoundedRangeModel
 237      * @see #setMinimum
 238      * @see #setMaximum
 239      * @see #setValue
 240      */
 241     public JSlider(int min, int max, int value) {
 242         this(HORIZONTAL, min, max, value);
 243     }
 244 
 245 
 246     /**
 247      * Creates a slider with the specified orientation and the
 248      * specified minimum, maximum, and initial values.
 249      * The orientation can be
 250      * either <code>SwingConstants.VERTICAL</code> or
 251      * <code>SwingConstants.HORIZONTAL</code>.
 252      * <p>
 253      * The <code>BoundedRangeModel</code> that holds the slider's data
 254      * handles any issues that may arise from improperly setting the
 255      * minimum, initial, and maximum values on the slider.  See the
 256      * {@code BoundedRangeModel} documentation for details.
 257      *
 258      * @param orientation  the orientation of the slider
 259      * @param min  the minimum value of the slider
 260      * @param max  the maximum value of the slider
 261      * @param value  the initial value of the slider
 262      *
 263      * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL}
 264      *
 265      * @see BoundedRangeModel
 266      * @see #setOrientation
 267      * @see #setMinimum
 268      * @see #setMaximum
 269      * @see #setValue
 270      */
 271     public JSlider(int orientation, int min, int max, int value)
 272     {
 273         checkOrientation(orientation);
 274         this.orientation = orientation;
 275         setModel(new DefaultBoundedRangeModel(value, 0, min, max));
 276         updateUI();
 277     }
 278 
 279 
 280     /**
 281      * Creates a horizontal slider using the specified
 282      * BoundedRangeModel.
 283      */
 284     public JSlider(BoundedRangeModel brm)
 285     {
 286         this.orientation = JSlider.HORIZONTAL;
 287         setModel(brm);
 288         updateUI();
 289     }
 290 
 291 
 292     /**
 293      * Gets the UI object which implements the L&amp;F for this component.
 294      *
 295      * @return the SliderUI object that implements the Slider L&amp;F
 296      */
 297     public SliderUI getUI() {
 298         return(SliderUI)ui;
 299     }
 300 
 301 
 302     /**
 303      * Sets the UI object which implements the L&amp;F for this component.
 304      *
 305      * @param ui the SliderUI L&amp;F object
 306      * @see UIDefaults#getUI
 307      * @beaninfo
 308      *        bound: true
 309      *       hidden: true
 310      *    attribute: visualUpdate true
 311      *  description: The UI object that implements the slider's LookAndFeel.
 312      */
 313     public void setUI(SliderUI ui) {
 314         super.setUI(ui);
 315     }
 316 
 317 
 318     /**
 319      * Resets the UI property to a value from the current look and feel.
 320      *
 321      * @see JComponent#updateUI
 322      */
 323     public void updateUI() {
 324         setUI((SliderUI)UIManager.getUI(this));
 325         // The labels preferred size may be derived from the font
 326         // of the slider, so we must update the UI of the slider first, then
 327         // that of labels.  This way when setSize is called the right
 328         // font is used.
 329         updateLabelUIs();
 330     }
 331 
 332 
 333     /**
 334      * Returns the name of the L&amp;F class that renders this component.
 335      *
 336      * @return "SliderUI"
 337      * @see JComponent#getUIClassID
 338      * @see UIDefaults#getUI
 339      */
 340     public String getUIClassID() {
 341         return uiClassID;
 342     }
 343 
 344 
 345     /**
 346      * We pass Change events along to the listeners with the
 347      * the slider (instead of the model itself) as the event source.
 348      */
 349     private class ModelListener implements ChangeListener, Serializable {
 350         public void stateChanged(ChangeEvent e) {
 351             fireStateChanged();
 352         }
 353     }
 354 
 355 
 356     /**
 357      * Subclasses that want to handle {@code ChangeEvent}s
 358      * from the model differently
 359      * can override this to return
 360      * an instance of a custom <code>ChangeListener</code> implementation.
 361      * The default {@code ChangeListener} simply calls the
 362      * {@code fireStateChanged} method to forward {@code ChangeEvent}s
 363      * to the {@code ChangeListener}s that have been added directly to the
 364      * slider.
 365      * @see #changeListener
 366      * @see #fireStateChanged
 367      * @see javax.swing.event.ChangeListener
 368      * @see javax.swing.BoundedRangeModel
 369      */
 370     protected ChangeListener createChangeListener() {
 371         return new ModelListener();
 372     }
 373 
 374 
 375     /**
 376      * Adds a ChangeListener to the slider.
 377      *
 378      * @param l the ChangeListener to add
 379      * @see #fireStateChanged
 380      * @see #removeChangeListener
 381      */
 382     public void addChangeListener(ChangeListener l) {
 383         listenerList.add(ChangeListener.class, l);
 384     }
 385 
 386 
 387     /**
 388      * Removes a ChangeListener from the slider.
 389      *
 390      * @param l the ChangeListener to remove
 391      * @see #fireStateChanged
 392      * @see #addChangeListener
 393 
 394      */
 395     public void removeChangeListener(ChangeListener l) {
 396         listenerList.remove(ChangeListener.class, l);
 397     }
 398 
 399 
 400     /**
 401      * Returns an array of all the <code>ChangeListener</code>s added
 402      * to this JSlider with addChangeListener().
 403      *
 404      * @return all of the <code>ChangeListener</code>s added or an empty
 405      *         array if no listeners have been added
 406      * @since 1.4
 407      */
 408     public ChangeListener[] getChangeListeners() {
 409         return listenerList.getListeners(ChangeListener.class);
 410     }
 411 
 412 
 413     /**
 414      * Send a {@code ChangeEvent}, whose source is this {@code JSlider}, to
 415      * all {@code ChangeListener}s that have registered interest in
 416      * {@code ChangeEvent}s.
 417      * This method is called each time a {@code ChangeEvent} is received from
 418      * the model.
 419      * <p>
 420      * The event instance is created if necessary, and stored in
 421      * {@code changeEvent}.
 422      *
 423      * @see #addChangeListener
 424      * @see EventListenerList
 425      */
 426     protected void fireStateChanged() {
 427         Object[] listeners = listenerList.getListenerList();
 428         for (int i = listeners.length - 2; i >= 0; i -= 2) {
 429             if (listeners[i]==ChangeListener.class) {
 430                 if (changeEvent == null) {
 431                     changeEvent = new ChangeEvent(this);
 432                 }
 433                 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
 434             }
 435         }
 436     }
 437 
 438 
 439     /**
 440      * Returns the {@code BoundedRangeModel} that handles the slider's three
 441      * fundamental properties: minimum, maximum, value.
 442      *
 443      * @return the data model for this component
 444      * @see #setModel
 445      * @see    BoundedRangeModel
 446      */
 447     public BoundedRangeModel getModel() {
 448         return sliderModel;
 449     }
 450 
 451 
 452     /**
 453      * Sets the {@code BoundedRangeModel} that handles the slider's three
 454      * fundamental properties: minimum, maximum, value.
 455      *<p>
 456      * Attempts to pass a {@code null} model to this method result in
 457      * undefined behavior, and, most likely, exceptions.
 458      *
 459      * @param  newModel the new, {@code non-null} <code>BoundedRangeModel</code> to use
 460      *
 461      * @see #getModel
 462      * @see    BoundedRangeModel
 463      * @beaninfo
 464      *       bound: true
 465      * description: The sliders BoundedRangeModel.
 466      */
 467     public void setModel(BoundedRangeModel newModel)
 468     {
 469         BoundedRangeModel oldModel = getModel();
 470 
 471         if (oldModel != null) {
 472             oldModel.removeChangeListener(changeListener);
 473         }
 474 
 475         sliderModel = newModel;
 476 
 477         if (newModel != null) {
 478             newModel.addChangeListener(changeListener);
 479         }
 480 
 481         if (accessibleContext != null) {
 482             accessibleContext.firePropertyChange(
 483                                                 AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
 484                                                 (oldModel == null
 485                                                  ? null : Integer.valueOf(oldModel.getValue())),
 486                                                 (newModel == null
 487                                                  ? null : Integer.valueOf(newModel.getValue())));
 488         }
 489 
 490         firePropertyChange("model", oldModel, sliderModel);
 491     }
 492 
 493 
 494     /**
 495      * Returns the slider's current value
 496      * from the {@code BoundedRangeModel}.
 497      *
 498      * @return  the current value of the slider
 499      * @see     #setValue
 500      * @see     BoundedRangeModel#getValue
 501      */
 502     public int getValue() {
 503         return getModel().getValue();
 504     }
 505 
 506     /**
 507      * Sets the slider's current value to {@code n}.  This method
 508      * forwards the new value to the model.
 509      * <p>
 510      * The data model (an instance of {@code BoundedRangeModel})
 511      * handles any mathematical
 512      * issues arising from assigning faulty values.  See the
 513      * {@code BoundedRangeModel} documentation for details.
 514      * <p>
 515      * If the new value is different from the previous value,
 516      * all change listeners are notified.
 517      *
 518      * @param   n       the new value
 519      * @see     #getValue
 520      * @see     #addChangeListener
 521      * @see     BoundedRangeModel#setValue
 522      * @beaninfo
 523      *   preferred: true
 524      * description: The sliders current value.
 525      */
 526     public void setValue(int n) {
 527         BoundedRangeModel m = getModel();
 528         int oldValue = m.getValue();
 529         if (oldValue == n) {
 530             return;
 531         }
 532         m.setValue(n);
 533 
 534         if (accessibleContext != null) {
 535             accessibleContext.firePropertyChange(
 536                                                 AccessibleContext.ACCESSIBLE_VALUE_PROPERTY,
 537                                                 Integer.valueOf(oldValue),
 538                                                 Integer.valueOf(m.getValue()));
 539         }
 540     }
 541 
 542 
 543     /**
 544      * Returns the minimum value supported by the slider
 545      * from the <code>BoundedRangeModel</code>.
 546      *
 547      * @return the value of the model's minimum property
 548      * @see #setMinimum
 549      * @see     BoundedRangeModel#getMinimum
 550      */
 551     public int getMinimum() {
 552         return getModel().getMinimum();
 553     }
 554 
 555 
 556     /**
 557      * Sets the slider's minimum value to {@code minimum}.  This method
 558      * forwards the new minimum value to the model.
 559      * <p>
 560      * The data model (an instance of {@code BoundedRangeModel})
 561      * handles any mathematical
 562      * issues arising from assigning faulty values.  See the
 563      * {@code BoundedRangeModel} documentation for details.
 564      * <p>
 565      * If the new minimum value is different from the previous minimum value,
 566      * all change listeners are notified.
 567      *
 568      * @param minimum  the new minimum
 569      * @see #getMinimum
 570      * @see    #addChangeListener
 571      * @see BoundedRangeModel#setMinimum
 572      * @beaninfo
 573      *       bound: true
 574      *   preferred: true
 575      * description: The sliders minimum value.
 576      */
 577     public void setMinimum(int minimum) {
 578         int oldMin = getModel().getMinimum();
 579         getModel().setMinimum(minimum);
 580         firePropertyChange( "minimum", Integer.valueOf( oldMin ), Integer.valueOf( minimum ) );
 581     }
 582 
 583 
 584     /**
 585      * Returns the maximum value supported by the slider
 586      * from the <code>BoundedRangeModel</code>.
 587      *
 588      * @return the value of the model's maximum property
 589      * @see #setMaximum
 590      * @see BoundedRangeModel#getMaximum
 591      */
 592     public int getMaximum() {
 593         return getModel().getMaximum();
 594     }
 595 
 596 
 597     /**
 598      * Sets the slider's maximum value to {@code maximum}.  This method
 599      * forwards the new maximum value to the model.
 600      * <p>
 601      * The data model (an instance of {@code BoundedRangeModel})
 602      * handles any mathematical
 603      * issues arising from assigning faulty values.  See the
 604      * {@code BoundedRangeModel} documentation for details.
 605      * <p>
 606      * If the new maximum value is different from the previous maximum value,
 607      * all change listeners are notified.
 608      *
 609      * @param maximum  the new maximum
 610      * @see #getMaximum
 611      * @see #addChangeListener
 612      * @see BoundedRangeModel#setMaximum
 613      * @beaninfo
 614      *       bound: true
 615      *   preferred: true
 616      * description: The sliders maximum value.
 617      */
 618     public void setMaximum(int maximum) {
 619         int oldMax = getModel().getMaximum();
 620         getModel().setMaximum(maximum);
 621         firePropertyChange( "maximum", Integer.valueOf( oldMax ), Integer.valueOf( maximum ) );
 622     }
 623 
 624 
 625     /**
 626      * Returns the {@code valueIsAdjusting} property from the model.  For
 627      * details on how this is used, see the {@code setValueIsAdjusting}
 628      * documentation.
 629      *
 630      * @return the value of the model's {@code valueIsAdjusting} property
 631      * @see #setValueIsAdjusting
 632      */
 633     public boolean getValueIsAdjusting() {
 634         return getModel().getValueIsAdjusting();
 635     }
 636 
 637 
 638     /**
 639      * Sets the model's {@code valueIsAdjusting} property.  Slider look and
 640      * feel implementations should set this property to {@code true} when
 641      * a knob drag begins, and to {@code false} when the drag ends.
 642      *
 643      * @param b the new value for the {@code valueIsAdjusting} property
 644      * @see   #getValueIsAdjusting
 645      * @see   BoundedRangeModel#setValueIsAdjusting
 646      * @beaninfo
 647      *      expert: true
 648      * description: True if the slider knob is being dragged.
 649      */
 650     public void setValueIsAdjusting(boolean b) {
 651         BoundedRangeModel m = getModel();
 652         boolean oldValue = m.getValueIsAdjusting();
 653         m.setValueIsAdjusting(b);
 654 
 655         if ((oldValue != b) && (accessibleContext != null)) {
 656             accessibleContext.firePropertyChange(
 657                                                 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
 658                                                 ((oldValue) ? AccessibleState.BUSY : null),
 659                                                 ((b) ? AccessibleState.BUSY : null));
 660         }
 661     }
 662 
 663 
 664     /**
 665      * Returns the "extent" from the <code>BoundedRangeModel</code>.
 666      * This represents the range of values "covered" by the knob.
 667      *
 668      * @return an int representing the extent
 669      * @see #setExtent
 670      * @see BoundedRangeModel#getExtent
 671      */
 672     public int getExtent() {
 673         return getModel().getExtent();
 674     }
 675 
 676 
 677     /**
 678      * Sets the size of the range "covered" by the knob.  Most look
 679      * and feel implementations will change the value by this amount
 680      * if the user clicks on either side of the knob.  This method just
 681      * forwards the new extent value to the model.
 682      * <p>
 683      * The data model (an instance of {@code BoundedRangeModel})
 684      * handles any mathematical
 685      * issues arising from assigning faulty values.  See the
 686      * {@code BoundedRangeModel} documentation for details.
 687      * <p>
 688      * If the new extent value is different from the previous extent value,
 689      * all change listeners are notified.
 690      *
 691      * @param extent the new extent
 692      * @see   #getExtent
 693      * @see   BoundedRangeModel#setExtent
 694      * @beaninfo
 695      *      expert: true
 696      * description: Size of the range covered by the knob.
 697      */
 698     public void setExtent(int extent) {
 699         getModel().setExtent(extent);
 700     }
 701 
 702 
 703     /**
 704      * Return this slider's vertical or horizontal orientation.
 705      * @return {@code SwingConstants.VERTICAL} or
 706      *  {@code SwingConstants.HORIZONTAL}
 707      * @see #setOrientation
 708      */
 709     public int getOrientation() {
 710         return orientation;
 711     }
 712 
 713 
 714     /**
 715      * Set the slider's orientation to either {@code SwingConstants.VERTICAL} or
 716      * {@code SwingConstants.HORIZONTAL}.
 717      *
 718      * @param orientation {@code HORIZONTAL} or {@code VERTICAL}
 719      * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL}
 720      * @see #getOrientation
 721      * @beaninfo
 722      *    preferred: true
 723      *        bound: true
 724      *    attribute: visualUpdate true
 725      *  description: Set the scrollbars orientation to either VERTICAL or HORIZONTAL.
 726      *         enum: VERTICAL JSlider.VERTICAL
 727      *               HORIZONTAL JSlider.HORIZONTAL
 728      *
 729      */
 730     public void setOrientation(int orientation)
 731     {
 732         checkOrientation(orientation);
 733         int oldValue = this.orientation;
 734         this.orientation = orientation;
 735         firePropertyChange("orientation", oldValue, orientation);
 736 
 737         if ((oldValue != orientation) && (accessibleContext != null)) {
 738             accessibleContext.firePropertyChange(
 739                                                 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
 740                                                 ((oldValue == VERTICAL)
 741                                                  ? AccessibleState.VERTICAL : AccessibleState.HORIZONTAL),
 742                                                 ((orientation == VERTICAL)
 743                                                  ? AccessibleState.VERTICAL : AccessibleState.HORIZONTAL));
 744         }
 745         if (orientation != oldValue) {
 746             revalidate();
 747         }
 748     }
 749 
 750 
 751     /**
 752      * {@inheritDoc}
 753      *
 754      * @since 1.6
 755      */
 756     public void setFont(Font font) {
 757         super.setFont(font);
 758         updateLabelSizes();
 759     }
 760 
 761     /**
 762      * {@inheritDoc}
 763      * @since 1.7
 764      */
 765     public boolean imageUpdate(Image img, int infoflags, int x, int y, int w, int h) {
 766         if (!isShowing()) {
 767             return false;
 768         }
 769 
 770         // Check that there is a label with such image
 771         Enumeration elements = labelTable.elements();
 772 
 773         while (elements.hasMoreElements()) {
 774             Component component = (Component) elements.nextElement();
 775 
 776             if (component instanceof JLabel) {
 777                 JLabel label = (JLabel) component;
 778 
 779                 if (SwingUtilities.doesIconReferenceImage(label.getIcon(), img) ||
 780                         SwingUtilities.doesIconReferenceImage(label.getDisabledIcon(), img)) {
 781                     return super.imageUpdate(img, infoflags, x, y, w, h);
 782                 }
 783             }
 784         }
 785 
 786         return false;
 787     }
 788 
 789     /**
 790      * Returns the dictionary of what labels to draw at which values.
 791      *
 792      * @return the <code>Dictionary</code> containing labels and
 793      *    where to draw them
 794      */
 795     public Dictionary getLabelTable() {
 796 /*
 797         if ( labelTable == null && getMajorTickSpacing() > 0 ) {
 798             setLabelTable( createStandardLabels( getMajorTickSpacing() ) );
 799         }
 800 */
 801         return labelTable;
 802     }
 803 
 804 
 805     /**
 806      * Used to specify what label will be drawn at any given value.
 807      * The key-value pairs are of this format:
 808      * <code>{ Integer value, java.swing.JComponent label }</code>.
 809      * <p>
 810      * An easy way to generate a standard table of value labels is by using the
 811      * {@code createStandardLabels} method.
 812      * <p>
 813      * Once the labels have been set, this method calls {@link #updateLabelUIs}.
 814      * Note that the labels are only painted if the {@code paintLabels}
 815      * property is {@code true}.
 816      *
 817      * @param labels new {@code Dictionary} of labels, or {@code null} to
 818      *        remove all labels
 819      * @see #createStandardLabels(int)
 820      * @see #getLabelTable
 821      * @see #setPaintLabels
 822      * @beaninfo
 823      *       hidden: true
 824      *        bound: true
 825      *    attribute: visualUpdate true
 826      *  description: Specifies what labels will be drawn for any given value.
 827      */
 828     public void setLabelTable( Dictionary labels ) {
 829         Dictionary oldTable = labelTable;
 830         labelTable = labels;
 831         updateLabelUIs();
 832         firePropertyChange("labelTable", oldTable, labelTable );
 833         if (labels != oldTable) {
 834             revalidate();
 835             repaint();
 836         }
 837     }
 838 
 839 
 840     /**
 841      * Updates the UIs for the labels in the label table by calling
 842      * {@code updateUI} on each label.  The UIs are updated from
 843      * the current look and feel.  The labels are also set to their
 844      * preferred size.
 845      *
 846      * @see #setLabelTable
 847      * @see JComponent#updateUI
 848      */
 849     protected void updateLabelUIs() {
 850         Dictionary labelTable = getLabelTable();
 851 
 852         if (labelTable == null) {
 853             return;
 854         }
 855         Enumeration labels = labelTable.keys();
 856         while ( labels.hasMoreElements() ) {
 857             JComponent component = (JComponent) labelTable.get(labels.nextElement());
 858             component.updateUI();
 859             component.setSize(component.getPreferredSize());
 860         }
 861     }
 862 
 863     private void updateLabelSizes() {
 864         Dictionary labelTable = getLabelTable();
 865         if (labelTable != null) {
 866             Enumeration labels = labelTable.elements();
 867             while (labels.hasMoreElements()) {
 868                 JComponent component = (JComponent) labels.nextElement();
 869                 component.setSize(component.getPreferredSize());
 870             }
 871         }
 872     }
 873 
 874 
 875     /**
 876      * Creates a {@code Hashtable} of numerical text labels, starting at the
 877      * slider minimum, and using the increment specified.
 878      * For example, if you call <code>createStandardLabels( 10 )</code>
 879      * and the slider minimum is zero,
 880      * then labels will be created for the values 0, 10, 20, 30, and so on.
 881      * <p>
 882      * For the labels to be drawn on the slider, the returned {@code Hashtable}
 883      * must be passed into {@code setLabelTable}, and {@code setPaintLabels}
 884      * must be set to {@code true}.
 885      * <p>
 886      * For further details on the makeup of the returned {@code Hashtable}, see
 887      * the {@code setLabelTable} documentation.
 888      *
 889      * @param  increment  distance between labels in the generated hashtable
 890      * @return a new {@code Hashtable} of labels
 891      * @see #setLabelTable
 892      * @see #setPaintLabels
 893      * @throws IllegalArgumentException if {@code increment} is less than or
 894      *          equal to zero
 895      */
 896     public Hashtable createStandardLabels( int increment ) {
 897         return createStandardLabels( increment, getMinimum() );
 898     }
 899 
 900 
 901     /**
 902      * Creates a {@code Hashtable} of numerical text labels, starting at the
 903      * starting point specified, and using the increment specified.
 904      * For example, if you call
 905      * <code>createStandardLabels( 10, 2 )</code>,
 906      * then labels will be created for the values 2, 12, 22, 32, and so on.
 907      * <p>
 908      * For the labels to be drawn on the slider, the returned {@code Hashtable}
 909      * must be passed into {@code setLabelTable}, and {@code setPaintLabels}
 910      * must be set to {@code true}.
 911      * <p>
 912      * For further details on the makeup of the returned {@code Hashtable}, see
 913      * the {@code setLabelTable} documentation.
 914      *
 915      * @param  increment  distance between labels in the generated hashtable
 916      * @param  start      value at which the labels will begin
 917      * @return a new {@code Hashtable} of labels
 918      * @see #setLabelTable
 919      * @see #setPaintLabels
 920      * @exception IllegalArgumentException if {@code start} is
 921      *          out of range, or if {@code increment} is less than or equal
 922      *          to zero
 923      */
 924     public Hashtable createStandardLabels( int increment, int start ) {
 925         if ( start > getMaximum() || start < getMinimum() ) {
 926             throw new IllegalArgumentException( "Slider label start point out of range." );
 927         }
 928 
 929         if ( increment <= 0 ) {
 930             throw new IllegalArgumentException( "Label incremement must be > 0" );
 931         }
 932 
 933         class SmartHashtable extends Hashtable<Object, Object> implements PropertyChangeListener {
 934             int increment = 0;
 935             int start = 0;
 936             boolean startAtMin = false;
 937 
 938             class LabelUIResource extends JLabel implements UIResource {
 939                 public LabelUIResource( String text, int alignment ) {
 940                     super( text, alignment );
 941                     setName("Slider.label");
 942                 }
 943 
 944                 public Font getFont() {
 945                     Font font = super.getFont();
 946                     if (font != null && !(font instanceof UIResource)) {
 947                         return font;
 948                     }
 949                     return JSlider.this.getFont();
 950                 }
 951 
 952                 public Color getForeground() {
 953                     Color fg = super.getForeground();
 954                     if (fg != null && !(fg instanceof UIResource)) {
 955                         return fg;
 956                     }
 957                     if (!(JSlider.this.getForeground() instanceof UIResource)) {
 958                         return JSlider.this.getForeground();
 959                     }
 960                     return fg;
 961                 }
 962             }
 963 
 964             public SmartHashtable( int increment, int start ) {
 965                 super();
 966                 this.increment = increment;
 967                 this.start = start;
 968                 startAtMin = start == getMinimum();
 969                 createLabels();
 970             }
 971 
 972             public void propertyChange( PropertyChangeEvent e ) {
 973                 if ( e.getPropertyName().equals( "minimum" ) && startAtMin ) {
 974                     start = getMinimum();
 975                 }
 976 
 977                 if ( e.getPropertyName().equals( "minimum" ) ||
 978                      e.getPropertyName().equals( "maximum" ) ) {
 979 
 980                     Enumeration keys = getLabelTable().keys();
 981                     Hashtable<Object, Object> hashtable = new Hashtable<Object, Object>();
 982 
 983                     // Save the labels that were added by the developer
 984                     while ( keys.hasMoreElements() ) {
 985                         Object key = keys.nextElement();
 986                         Object value = labelTable.get(key);
 987                         if ( !(value instanceof LabelUIResource) ) {
 988                             hashtable.put( key, value );
 989                         }
 990                     }
 991 
 992                     clear();
 993                     createLabels();
 994 
 995                     // Add the saved labels
 996                     keys = hashtable.keys();
 997                     while ( keys.hasMoreElements() ) {
 998                         Object key = keys.nextElement();
 999                         put( key, hashtable.get( key ) );
1000                     }
1001 
1002                     ((JSlider)e.getSource()).setLabelTable( this );
1003                 }
1004             }
1005 
1006             void createLabels() {
1007                 for ( int labelIndex = start; labelIndex <= getMaximum(); labelIndex += increment ) {
1008                     put( Integer.valueOf( labelIndex ), new LabelUIResource( ""+labelIndex, JLabel.CENTER ) );
1009                 }
1010             }
1011         }
1012 
1013         SmartHashtable table = new SmartHashtable( increment, start );
1014 
1015         Dictionary labelTable = getLabelTable();
1016 
1017         if (labelTable != null && (labelTable instanceof PropertyChangeListener)) {
1018             removePropertyChangeListener((PropertyChangeListener) labelTable);
1019         }
1020 
1021         addPropertyChangeListener( table );
1022 
1023         return table;
1024     }
1025 
1026 
1027     /**
1028      * Returns true if the value-range shown for the slider is reversed,
1029      *
1030      * @return true if the slider values are reversed from their normal order
1031      * @see #setInverted
1032      */
1033     public boolean getInverted() {
1034         return isInverted;
1035     }
1036 
1037 
1038     /**
1039      * Specify true to reverse the value-range shown for the slider and false to
1040      * put the value range in the normal order.  The order depends on the
1041      * slider's <code>ComponentOrientation</code> property.  Normal (non-inverted)
1042      * horizontal sliders with a <code>ComponentOrientation</code> value of
1043      * <code>LEFT_TO_RIGHT</code> have their maximum on the right.
1044      * Normal horizontal sliders with a <code>ComponentOrientation</code> value of
1045      * <code>RIGHT_TO_LEFT</code> have their maximum on the left.  Normal vertical
1046      * sliders have their maximum on the top.  These labels are reversed when the
1047      * slider is inverted.
1048      * <p>
1049      * By default, the value of this property is {@code false}.
1050      *
1051      * @param b  true to reverse the slider values from their normal order
1052      * @beaninfo
1053      *        bound: true
1054      *    attribute: visualUpdate true
1055      *  description: If true reverses the slider values from their normal order
1056      *
1057      */
1058     public void setInverted( boolean b ) {
1059         boolean oldValue = isInverted;
1060         isInverted = b;
1061         firePropertyChange("inverted", oldValue, isInverted);
1062         if (b != oldValue) {
1063             repaint();
1064         }
1065     }
1066 
1067 
1068     /**
1069      * This method returns the major tick spacing.  The number that is returned
1070      * represents the distance, measured in values, between each major tick mark.
1071      * If you have a slider with a range from 0 to 50 and the major tick spacing
1072      * is set to 10, you will get major ticks next to the following values:
1073      * 0, 10, 20, 30, 40, 50.
1074      *
1075      * @return the number of values between major ticks
1076      * @see #setMajorTickSpacing
1077      */
1078     public int getMajorTickSpacing() {
1079         return majorTickSpacing;
1080     }
1081 
1082 
1083     /**
1084      * This method sets the major tick spacing.  The number that is passed in
1085      * represents the distance, measured in values, between each major tick mark.
1086      * If you have a slider with a range from 0 to 50 and the major tick spacing
1087      * is set to 10, you will get major ticks next to the following values:
1088      * 0, 10, 20, 30, 40, 50.
1089      * <p>
1090      * In order for major ticks to be painted, {@code setPaintTicks} must be
1091      * set to {@code true}.
1092      * <p>
1093      * This method will also set up a label table for you.
1094      * If there is not already a label table, and the major tick spacing is
1095      * {@code > 0}, and {@code getPaintLabels} returns
1096      * {@code true}, a standard label table will be generated (by calling
1097      * {@code createStandardLabels}) with labels at the major tick marks.
1098      * For the example above, you would get text labels: "0",
1099      * "10", "20", "30", "40", "50".
1100      * The label table is then set on the slider by calling
1101      * {@code setLabelTable}.
1102      *
1103      * @param  n  new value for the {@code majorTickSpacing} property
1104      * @see #getMajorTickSpacing
1105      * @see #setPaintTicks
1106      * @see #setLabelTable
1107      * @see #createStandardLabels(int)
1108      * @beaninfo
1109      *        bound: true
1110      *    attribute: visualUpdate true
1111      *  description: Sets the number of values between major tick marks.
1112      *
1113      */
1114     public void setMajorTickSpacing(int n) {
1115         int oldValue = majorTickSpacing;
1116         majorTickSpacing = n;
1117         if ( labelTable == null && getMajorTickSpacing() > 0 && getPaintLabels() ) {
1118             setLabelTable( createStandardLabels( getMajorTickSpacing() ) );
1119         }
1120         firePropertyChange("majorTickSpacing", oldValue, majorTickSpacing);
1121         if (majorTickSpacing != oldValue && getPaintTicks()) {
1122             repaint();
1123         }
1124     }
1125 
1126 
1127 
1128     /**
1129      * This method returns the minor tick spacing.  The number that is returned
1130      * represents the distance, measured in values, between each minor tick mark.
1131      * If you have a slider with a range from 0 to 50 and the minor tick spacing
1132      * is set to 10, you will get minor ticks next to the following values:
1133      * 0, 10, 20, 30, 40, 50.
1134      *
1135      * @return the number of values between minor ticks
1136      * @see #getMinorTickSpacing
1137      */
1138     public int getMinorTickSpacing() {
1139         return minorTickSpacing;
1140     }
1141 
1142 
1143     /**
1144      * This method sets the minor tick spacing.  The number that is passed in
1145      * represents the distance, measured in values, between each minor tick mark.
1146      * If you have a slider with a range from 0 to 50 and the minor tick spacing
1147      * is set to 10, you will get minor ticks next to the following values:
1148      * 0, 10, 20, 30, 40, 50.
1149      * <p>
1150      * In order for minor ticks to be painted, {@code setPaintTicks} must be
1151      * set to {@code true}.
1152      *
1153      * @param  n  new value for the {@code minorTickSpacing} property
1154      * @see #getMinorTickSpacing
1155      * @see #setPaintTicks
1156      * @beaninfo
1157      *        bound: true
1158      *    attribute: visualUpdate true
1159      *  description: Sets the number of values between minor tick marks.
1160      */
1161     public void setMinorTickSpacing(int n) {
1162         int oldValue = minorTickSpacing;
1163         minorTickSpacing = n;
1164         firePropertyChange("minorTickSpacing", oldValue, minorTickSpacing);
1165         if (minorTickSpacing != oldValue && getPaintTicks()) {
1166             repaint();
1167         }
1168     }
1169 
1170 
1171     /**
1172      * Returns true if the knob (and the data value it represents)
1173      * resolve to the closest tick mark next to where the user
1174      * positioned the knob.
1175      *
1176      * @return true if the value snaps to the nearest tick mark, else false
1177      * @see #setSnapToTicks
1178      */
1179     public boolean getSnapToTicks() {
1180         return snapToTicks;
1181     }
1182 
1183 
1184     /**
1185      * Returns true if the knob (and the data value it represents)
1186      * resolve to the closest slider value next to where the user
1187      * positioned the knob.
1188      *
1189      * @return true if the value snaps to the nearest slider value, else false
1190      * @see #setSnapToValue
1191      */
1192     boolean getSnapToValue() {
1193         return snapToValue;
1194     }
1195 
1196 
1197     /**
1198      * Specifying true makes the knob (and the data value it represents)
1199      * resolve to the closest tick mark next to where the user
1200      * positioned the knob.
1201      * By default, this property is {@code false}.
1202      *
1203      * @param b  true to snap the knob to the nearest tick mark
1204      * @see #getSnapToTicks
1205      * @beaninfo
1206      *       bound: true
1207      * description: If true snap the knob to the nearest tick mark.
1208      */
1209     public void setSnapToTicks(boolean b) {
1210         boolean oldValue = snapToTicks;
1211         snapToTicks = b;
1212         firePropertyChange("snapToTicks", oldValue, snapToTicks);
1213     }
1214 
1215 
1216     /**
1217      * Specifying true makes the knob (and the data value it represents)
1218      * resolve to the closest slider value next to where the user
1219      * positioned the knob. If the {@code snapToTicks} property has also been
1220      * set to {@code true}, the snap-to-ticks behavior will prevail.
1221      * By default, the snapToValue property is {@code true}.
1222      *
1223      * @param b  true to snap the knob to the nearest slider value
1224      * @see #getSnapToValue
1225      * @see #setSnapToTicks
1226      * @beaninfo
1227      *       bound: true
1228      * description: If true snap the knob to the nearest slider value.
1229      */
1230     void setSnapToValue(boolean b) {
1231         boolean oldValue = snapToValue;
1232         snapToValue = b;
1233         firePropertyChange("snapToValue", oldValue, snapToValue);
1234     }
1235 
1236 
1237     /**
1238      * Tells if tick marks are to be painted.
1239      * @return true if tick marks are painted, else false
1240      * @see #setPaintTicks
1241      */
1242     public boolean getPaintTicks() {
1243         return paintTicks;
1244     }
1245 
1246 
1247     /**
1248      * Determines whether tick marks are painted on the slider.
1249      * By default, this property is {@code false}.
1250      *
1251      * @param  b  whether or not tick marks should be painted
1252      * @see #getPaintTicks
1253      * @beaninfo
1254      *        bound: true
1255      *    attribute: visualUpdate true
1256      *  description: If true tick marks are painted on the slider.
1257      */
1258     public void setPaintTicks(boolean b) {
1259         boolean oldValue = paintTicks;
1260         paintTicks = b;
1261         firePropertyChange("paintTicks", oldValue, paintTicks);
1262         if (paintTicks != oldValue) {
1263             revalidate();
1264             repaint();
1265         }
1266     }
1267 
1268     /**
1269      * Tells if the track (area the slider slides in) is to be painted.
1270      * @return true if track is painted, else false
1271      * @see #setPaintTrack
1272      */
1273     public boolean getPaintTrack() {
1274         return paintTrack;
1275     }
1276 
1277 
1278     /**
1279      * Determines whether the track is painted on the slider.
1280      * By default, this property is {@code true}.
1281      *
1282      * @param  b  whether or not to paint the slider track
1283      * @see #getPaintTrack
1284      * @beaninfo
1285      *        bound: true
1286      *    attribute: visualUpdate true
1287      *  description: If true, the track is painted on the slider.
1288      */
1289     public void setPaintTrack(boolean b) {
1290         boolean oldValue = paintTrack;
1291         paintTrack = b;
1292         firePropertyChange("paintTrack", oldValue, paintTrack);
1293         if (paintTrack != oldValue) {
1294             repaint();
1295         }
1296     }
1297 
1298 
1299     /**
1300      * Tells if labels are to be painted.
1301      * @return true if labels are painted, else false
1302      * @see #setPaintLabels
1303      */
1304     public boolean getPaintLabels() {
1305         return paintLabels;
1306     }
1307 
1308 
1309     /**
1310      * Determines whether labels are painted on the slider.
1311      * <p>
1312      * This method will also set up a label table for you.
1313      * If there is not already a label table, and the major tick spacing is
1314      * {@code > 0},
1315      * a standard label table will be generated (by calling
1316      * {@code createStandardLabels}) with labels at the major tick marks.
1317      * The label table is then set on the slider by calling
1318      * {@code setLabelTable}.
1319      * <p>
1320      * By default, this property is {@code false}.
1321      *
1322      * @param  b  whether or not to paint labels
1323      * @see #getPaintLabels
1324      * @see #getLabelTable
1325      * @see #createStandardLabels(int)
1326      * @beaninfo
1327      *        bound: true
1328      *    attribute: visualUpdate true
1329      *  description: If true labels are painted on the slider.
1330      */
1331     public void setPaintLabels(boolean b) {
1332         boolean oldValue = paintLabels;
1333         paintLabels = b;
1334         if ( labelTable == null && getMajorTickSpacing() > 0 ) {
1335             setLabelTable( createStandardLabels( getMajorTickSpacing() ) );
1336         }
1337         firePropertyChange("paintLabels", oldValue, paintLabels);
1338         if (paintLabels != oldValue) {
1339             revalidate();
1340             repaint();
1341         }
1342     }
1343 
1344 
1345     /**
1346      * See readObject() and writeObject() in JComponent for more
1347      * information about serialization in Swing.
1348      */
1349     private void writeObject(ObjectOutputStream s) throws IOException {
1350         s.defaultWriteObject();
1351         if (getUIClassID().equals(uiClassID)) {
1352             byte count = JComponent.getWriteObjCounter(this);
1353             JComponent.setWriteObjCounter(this, --count);
1354             if (count == 0 && ui != null) {
1355                 ui.installUI(this);
1356             }
1357         }
1358     }
1359 
1360 
1361     /**
1362      * Returns a string representation of this JSlider. This method
1363      * is intended to be used only for debugging purposes, and the
1364      * content and format of the returned string may vary between
1365      * implementations. The returned string may be empty but may not
1366      * be <code>null</code>.
1367      *
1368      * @return  a string representation of this JSlider.
1369      */
1370     protected String paramString() {
1371         String paintTicksString = (paintTicks ?
1372                                    "true" : "false");
1373         String paintTrackString = (paintTrack ?
1374                                    "true" : "false");
1375         String paintLabelsString = (paintLabels ?
1376                                     "true" : "false");
1377         String isInvertedString = (isInverted ?
1378                                    "true" : "false");
1379         String snapToTicksString = (snapToTicks ?
1380                                     "true" : "false");
1381         String snapToValueString = (snapToValue ?
1382                                     "true" : "false");
1383         String orientationString = (orientation == HORIZONTAL ?
1384                                     "HORIZONTAL" : "VERTICAL");
1385 
1386         return super.paramString() +
1387         ",isInverted=" + isInvertedString +
1388         ",majorTickSpacing=" + majorTickSpacing +
1389         ",minorTickSpacing=" + minorTickSpacing +
1390         ",orientation=" + orientationString +
1391         ",paintLabels=" + paintLabelsString +
1392         ",paintTicks=" + paintTicksString +
1393         ",paintTrack=" + paintTrackString +
1394         ",snapToTicks=" + snapToTicksString +
1395         ",snapToValue=" + snapToValueString;
1396     }
1397 
1398 
1399 /////////////////
1400 // Accessibility support
1401 ////////////////
1402 
1403     /**
1404      * Gets the AccessibleContext associated with this JSlider.
1405      * For sliders, the AccessibleContext takes the form of an
1406      * AccessibleJSlider.
1407      * A new AccessibleJSlider instance is created if necessary.
1408      *
1409      * @return an AccessibleJSlider that serves as the
1410      *         AccessibleContext of this JSlider
1411      */
1412     public AccessibleContext getAccessibleContext() {
1413         if (accessibleContext == null) {
1414             accessibleContext = new AccessibleJSlider();
1415         }
1416         return accessibleContext;
1417     }
1418 
1419     /**
1420      * This class implements accessibility support for the
1421      * <code>JSlider</code> class.  It provides an implementation of the
1422      * Java Accessibility API appropriate to slider user-interface elements.
1423      * <p>
1424      * <strong>Warning:</strong>
1425      * Serialized objects of this class will not be compatible with
1426      * future Swing releases. The current serialization support is
1427      * appropriate for short term storage or RMI between applications running
1428      * the same version of Swing.  As of 1.4, support for long term storage
1429      * of all JavaBeans&trade;
1430      * has been added to the <code>java.beans</code> package.
1431      * Please see {@link java.beans.XMLEncoder}.
1432      */
1433     @SuppressWarnings("serial") // Same-version serialization only
1434     protected class AccessibleJSlider extends AccessibleJComponent
1435     implements AccessibleValue {
1436 
1437         /**
1438          * Get the state set of this object.
1439          *
1440          * @return an instance of AccessibleState containing the current state
1441          * of the object
1442          * @see AccessibleState
1443          */
1444         public AccessibleStateSet getAccessibleStateSet() {
1445             AccessibleStateSet states = super.getAccessibleStateSet();
1446             if (getValueIsAdjusting()) {
1447                 states.add(AccessibleState.BUSY);
1448             }
1449             if (getOrientation() == VERTICAL) {
1450                 states.add(AccessibleState.VERTICAL);
1451             }
1452             else {
1453                 states.add(AccessibleState.HORIZONTAL);
1454             }
1455             return states;
1456         }
1457 
1458         /**
1459          * Get the role of this object.
1460          *
1461          * @return an instance of AccessibleRole describing the role of the object
1462          */
1463         public AccessibleRole getAccessibleRole() {
1464             return AccessibleRole.SLIDER;
1465         }
1466 
1467         /**
1468          * Get the AccessibleValue associated with this object.  In the
1469          * implementation of the Java Accessibility API for this class,
1470          * return this object, which is responsible for implementing the
1471          * AccessibleValue interface on behalf of itself.
1472          *
1473          * @return this object
1474          */
1475         public AccessibleValue getAccessibleValue() {
1476             return this;
1477         }
1478 
1479         /**
1480          * Get the accessible value of this object.
1481          *
1482          * @return The current value of this object.
1483          */
1484         public Number getCurrentAccessibleValue() {
1485             return Integer.valueOf(getValue());
1486         }
1487 
1488         /**
1489          * Set the value of this object as a Number.
1490          *
1491          * @return True if the value was set.
1492          */
1493         public boolean setCurrentAccessibleValue(Number n) {
1494             // TIGER - 4422535
1495             if (n == null) {
1496                 return false;
1497             }
1498             setValue(n.intValue());
1499             return true;
1500         }
1501 
1502         /**
1503          * Get the minimum accessible value of this object.
1504          *
1505          * @return The minimum value of this object.
1506          */
1507         public Number getMinimumAccessibleValue() {
1508             return Integer.valueOf(getMinimum());
1509         }
1510 
1511         /**
1512          * Get the maximum accessible value of this object.
1513          *
1514          * @return The maximum value of this object.
1515          */
1516         public Number getMaximumAccessibleValue() {
1517             // TIGER - 4422362
1518             BoundedRangeModel model = JSlider.this.getModel();
1519             return Integer.valueOf(model.getMaximum() - model.getExtent());
1520         }
1521     } // AccessibleJSlider
1522 }