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