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