Source for javax.swing.JSlider

   1: /* JSlider.java --
   2:    Copyright (C) 2002, 2004, 2005, 2006,  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing;
  40: 
  41: import java.awt.Dimension;
  42: import java.awt.MenuContainer;
  43: import java.awt.image.ImageObserver;
  44: import java.beans.PropertyChangeEvent;
  45: import java.io.Serializable;
  46: import java.util.Dictionary;
  47: import java.util.Enumeration;
  48: import java.util.Hashtable;
  49: 
  50: import javax.accessibility.Accessible;
  51: import javax.accessibility.AccessibleContext;
  52: import javax.accessibility.AccessibleRole;
  53: import javax.accessibility.AccessibleState;
  54: import javax.accessibility.AccessibleStateSet;
  55: import javax.accessibility.AccessibleValue;
  56: import javax.swing.event.ChangeEvent;
  57: import javax.swing.event.ChangeListener;
  58: import javax.swing.plaf.SliderUI;
  59: 
  60: /**
  61:  * A visual component that allows selection of a value within a
  62:  * range by adjusting a thumb in a track. The values for the minimum,
  63:  * maximum, extent and value are stored in a {@link
  64:  * DefaultBoundedRangeModel}.
  65:  * <p>
  66:  * A <code>JSlider</code> component has the following properties:
  67:  * </p>
  68:  * 
  69:  * <table>
  70:  * <tr><th> Property         </th><th> Stored in </th><th> Bound? </th></tr>
  71:  * <tr><td> extent           </td><td> model     </td><td> no     </td></tr>
  72:  * <tr><td> inverted         </td><td> slider    </td><td> yes    </td></tr>
  73:  * <tr><td> labelTable       </td><td> slider    </td><td> yes    </td></tr>
  74:  * <tr><td> majorTickSpacing </td><td> slider    </td><td> yes    </td></tr> 
  75:  * <tr><td> maximum          </td><td> model     </td><td> yes     </td></tr>
  76:  * <tr><td> minimum          </td><td> model     </td><td> yes     </td></tr>
  77:  * <tr><td> minorTickSpacing </td><td> slider    </td><td> yes    </td></tr>
  78:  * <tr><td> model            </td><td> slider    </td><td> yes    </td></tr> 
  79:  * <tr><td> orientation      </td><td> slider    </td><td> yes    </td></tr>
  80:  * <tr><td> paintLabels      </td><td> slider    </td><td> yes    </td></tr>
  81:  * <tr><td> paintTicks       </td><td> slider    </td><td> yes    </td></tr>
  82:  * <tr><td> snapToTicks      </td><td> slider    </td><td> yes     </td></tr>
  83:  * <tr><td> value            </td><td> model     </td><td> no     </td></tr>
  84:  * <tr><td> valueIsAdjusting </td><td> model     </td><td> no     </td></tr>
  85:  * </table>
  86:  * 
  87:  * <p>
  88:  * The various behavioural aspects of these properties follows:
  89:  * </p>
  90:  * 
  91:  * <ul>
  92:  * <li>
  93:  * When a non-bound property stored in the slider changes, the slider fires
  94:  * a {@link ChangeEvent} to its change listeners.
  95:  * </li>
  96:  * <li>
  97:  * When a bound property stored in the slider changes, the slider fires a
  98:  * {@link PropertyChangeEvent} to its property change listeners.
  99:  * </li>
 100:  * <li>
 101:  * If any of the model's properties change, it fires a {@link ChangeEvent} to 
 102:  * its listeners, which include the slider.
 103:  * </li>
 104:  * <li>
 105:  * If the slider receives a {@link ChangeEvent} from its model, it will 
 106:  * propagate the event to its own change listeners, with the event's "source"
 107:  * property set to refer to the slider, rather than the model.
 108:  * </li>
 109:  * </ul>
 110:  */
 111: public class JSlider extends JComponent implements SwingConstants, Accessible,
 112:                                                    ImageObserver,
 113:                                                    MenuContainer, Serializable
 114: {
 115:   private static final long serialVersionUID = -1441275936141218479L;
 116: 
 117:   /**
 118:    * Provides the accessibility features for the <code>JSlider</code>
 119:    * component.
 120:    */
 121:   protected class AccessibleJSlider extends JComponent.AccessibleJComponent
 122:     implements AccessibleValue
 123:   {
 124:     private static final long serialVersionUID = -6301740148041106789L;
 125:   
 126:     /**
 127:      * Creates a new <code>AccessibleJSlider</code> instance.
 128:      */
 129:     protected AccessibleJSlider()
 130:     {
 131:       // Nothing to do here.
 132:     }
 133: 
 134:     /**
 135:      * Returns a set containing the current state of the {@link JSlider} 
 136:      * component.
 137:      *
 138:      * @return The accessible state set.
 139:      */
 140:     public AccessibleStateSet getAccessibleStateSet()
 141:     {
 142:       AccessibleStateSet result = super.getAccessibleStateSet();
 143:       if (orientation == JSlider.HORIZONTAL)
 144:         result.add(AccessibleState.HORIZONTAL);
 145:       else if (orientation == JSlider.VERTICAL)
 146:         result.add(AccessibleState.VERTICAL);
 147:       return result;
 148:     }
 149: 
 150:     /**
 151:      * Returns the accessible role for the <code>JSlider</code> component.
 152:      *
 153:      * @return {@link AccessibleRole#SLIDER}.
 154:      */
 155:     public AccessibleRole getAccessibleRole()
 156:     {
 157:       return AccessibleRole.SLIDER;
 158:     }
 159: 
 160:     /**
 161:      * Returns an object that provides access to the current, minimum and 
 162:      * maximum values for the {@link JSlider}.  Since this class implements 
 163:      * {@link AccessibleValue}, it returns itself.
 164:      *
 165:      * @return The accessible value.
 166:      */
 167:     public AccessibleValue getAccessibleValue()
 168:     {
 169:       return this;
 170:     }
 171: 
 172:     /**
 173:      * Returns the current value of the {@link JSlider} component, as an
 174:      * {@link Integer}.
 175:      *
 176:      * @return The current value of the {@link JSlider} component.
 177:      */
 178:     public Number getCurrentAccessibleValue()
 179:     {
 180:       return new Integer(getValue());
 181:     }
 182: 
 183:     /**
 184:      * Sets the current value of the {@link JSlider} component and sends a
 185:      * {@link PropertyChangeEvent} (with the property name 
 186:      * {@link AccessibleContext#ACCESSIBLE_VALUE_PROPERTY}) to all registered
 187:      * listeners.  If the supplied value is <code>null</code>, this method 
 188:      * does nothing and returns <code>false</code>.
 189:      *
 190:      * @param value  the new slider value (<code>null</code> permitted).
 191:      *
 192:      * @return <code>true</code> if the slider value is updated, and 
 193:      *     <code>false</code> otherwise.
 194:      */
 195:     public boolean setCurrentAccessibleValue(Number value)
 196:     {
 197:       if (value == null)
 198:         return false;
 199:       Number oldValue = getCurrentAccessibleValue();
 200:       setValue(value.intValue());
 201:       firePropertyChange(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, oldValue, 
 202:                          new Integer(getValue()));
 203:       return true;
 204:     }
 205: 
 206:     /**
 207:      * Returns the minimum value of the {@link JSlider} component, as an
 208:      * {@link Integer}.
 209:      *
 210:      * @return The minimum value of the {@link JSlider} component.
 211:      */
 212:     public Number getMinimumAccessibleValue()
 213:     {
 214:       return new Integer(getMinimum());
 215:     }
 216: 
 217:     /**
 218:      * Returns the maximum value of the {@link JSlider} component, as an
 219:      * {@link Integer}.
 220:      *
 221:      * @return The maximum value of the {@link JSlider} component.
 222:      */
 223:     public Number getMaximumAccessibleValue()
 224:     {
 225:       return new Integer(getMaximum());
 226:     }
 227:   }
 228: 
 229:   /** Whether or not this slider paints its ticks. */
 230:   private transient boolean paintTicks;
 231: 
 232:   /** Whether or not this slider paints its track. */
 233:   private transient boolean paintTrack = true;
 234: 
 235:   /** Whether or not this slider paints its labels. */
 236:   private transient boolean paintLabels;
 237: 
 238:   /**
 239:    * A dictionary of (Integer, Component) pairs where each Component is a
 240:    * JLabel and the Integer determines where the label will be painted.
 241:    */
 242:   private transient Dictionary labelTable;
 243: 
 244:   /** The model used to store the slider's range and current value. */
 245:   protected BoundedRangeModel sliderModel;
 246: 
 247:   /** The space/distance between major ticks. */
 248:   protected int majorTickSpacing;
 249: 
 250:   /** The space/distance between minor ticks. */
 251:   protected int minorTickSpacing;
 252: 
 253:   /** Whether the slider snaps its values to ticks. */
 254:   protected boolean snapToTicks;
 255: 
 256:   /** The orientation (horizontal or vertical) of the slider. */
 257:   protected int orientation = HORIZONTAL;
 258: 
 259:   /** Whether the slider is inverted. */
 260:   private transient boolean isInverted;
 261: 
 262:   /** 
 263:    * The listener that monitors the slider's model and forwards events to the
 264:    * slider's listeners (see <code>createChangeListener()</code>). 
 265:    */
 266:   protected ChangeListener changeListener;
 267: 
 268:   /** The change event that is passed to all listeners of this slider. */
 269:   protected transient ChangeEvent changeEvent;
 270: 
 271:   /**
 272:    * Creates a new horizontal <code>JSlider</code> instance with a minimum of 
 273:    * 0, a maximum of 100, and a value of 50.
 274:    */
 275:   public JSlider()
 276:   {
 277:     this(HORIZONTAL, 0, 100, 50);
 278:   }
 279: 
 280:   /**
 281:    * Creates a new <code>JSlider</code> instance with the given orientation 
 282:    * and a minimum of 0, a maximum of 100, and a value of 50.
 283:    *
 284:    * @param orientation The orientation of the slider ({@link #HORIZONTAL} or
 285:    *                    {@link #VERTICAL}).
 286:    * 
 287:    * @throws IllegalArgumentException if <code>orientation</code> is not one of
 288:    *         the specified values.
 289:    */
 290:   public JSlider(int orientation)
 291:   {
 292:     this(orientation, 0, 100, 50);
 293:   }
 294: 
 295:   /**
 296:    * Creates a new horizontal <code>JSlider</code> instance with the given 
 297:    * maximum and minimum and a value that is halfway between the minimum and the
 298:    * maximum.
 299:    *
 300:    * @param minimum The minimum value.
 301:    * @param maximum The maximum value.
 302:    * 
 303:    * @throws IllegalArgumentException if <code>minimum</code> is greater than
 304:    *     <code>maximum</code>.
 305:    */
 306:   public JSlider(int minimum, int maximum)
 307:   {
 308:     this(HORIZONTAL, minimum, maximum, (maximum + minimum) / 2);
 309:   }
 310: 
 311:   /**
 312:    * Creates a new horizontal <code>JSlider</code> instance with the given 
 313:    * minimum, maximum, and value.
 314:    *
 315:    * @param minimum The minimum value.
 316:    * @param maximum The maximum value.
 317:    * @param value The initial value.
 318:    * 
 319:    * @throws IllegalArgumentException if <code>value</code> is not in the 
 320:    *     specified range.
 321:    * @throws IllegalArgumentException if <code>minimum</code> is greater than
 322:    *     <code>maximum</code>.
 323:    */
 324:   public JSlider(int minimum, int maximum, int value)
 325:   {
 326:     this(HORIZONTAL, minimum, maximum, value);
 327:   }
 328: 
 329:   /**
 330:    * Creates a new <code>JSlider</code> instance with the given orientation, 
 331:    * minimum, maximum, and value.
 332:    *
 333:    * @param orientation The orientation of the slider ({@link #HORIZONTAL} or
 334:    *                    {@link #VERTICAL}).
 335:    * @param minimum The minimum value of the JSlider.
 336:    * @param maximum The maximum value of the JSlider.
 337:    * @param value The initial value of the JSlider.
 338:    * 
 339:    * @throws IllegalArgumentException if <code>orientation</code> is not one of
 340:    *     the specified values.
 341:    * @throws IllegalArgumentException if <code>value</code> is not in the 
 342:    *     specified range.
 343:    * @throws IllegalArgumentException if <code>minimum</code> is greater than
 344:    *     <code>maximum</code>.
 345:    */
 346:   public JSlider(int orientation, int minimum, int maximum, int value)
 347:   {
 348:     sliderModel = new DefaultBoundedRangeModel(value, 0, minimum, maximum);
 349:     if (orientation != HORIZONTAL && orientation != VERTICAL)
 350:       throw new IllegalArgumentException(orientation 
 351:                                          + " is not a legal orientation");
 352:     this.orientation = orientation;
 353:     changeListener = createChangeListener();
 354:     sliderModel.addChangeListener(changeListener);
 355:     updateUI();
 356:   }
 357: 
 358:   /**
 359:    * Creates a new horizontal <code>JSlider</code> instance with the given 
 360:    * model.
 361:    *
 362:    * @param model The model (<code>null</code> not permitted).
 363:    * 
 364:    * @throws NullPointerException if <code>model</code> is <code>null</code>.
 365:    */
 366:   public JSlider(BoundedRangeModel model)
 367:   {
 368:     sliderModel = model;
 369:     changeListener = createChangeListener();
 370:     sliderModel.addChangeListener(changeListener);
 371:     updateUI();
 372:   }
 373: 
 374:   /**
 375:    * Returns the slider's value (from the slider's model).
 376:    *
 377:    * @return The value of the slider.
 378:    * 
 379:    * @see #setValue(int)
 380:    */
 381:   public int getValue()
 382:   {
 383:     return sliderModel.getValue();
 384:   }
 385: 
 386:   /**
 387:    * Sets the slider's value and sends a {@link ChangeEvent} to all 
 388:    * registered listeners.  Note that the model will fire a change event to all
 389:    * of its registered listeners first (with the model as the event source) and
 390:    * then the slider will fire another change event to all of its registered
 391:    * listeners (this time with the slider as the event source).
 392:    *
 393:    * @param value  the new value.
 394:    * 
 395:    * @see #getValue()
 396:    */
 397:   public void setValue(int value)
 398:   {
 399:     sliderModel.setValue(value);
 400:   }
 401: 
 402:   /**
 403:    * Returns the slider's UI delegate.
 404:    *
 405:    * @return The slider's UI delegate.
 406:    */
 407:   public SliderUI getUI()
 408:   {
 409:     return (SliderUI) ui;
 410:   }
 411: 
 412:   /**
 413:    * Sets the slider's UI delegate.
 414:    *
 415:    * @param ui  the UI delegate.
 416:    */
 417:   public void setUI(SliderUI ui)
 418:   {
 419:     super.setUI(ui);
 420:   }
 421: 
 422:   /**
 423:    * Sets this slider's UI delegate to the default (obtained from the
 424:    * {@link UIManager}) for the current look and feel.
 425:    */
 426:   public void updateUI()
 427:   {
 428:     setUI((SliderUI) UIManager.getUI(this));
 429:   }
 430: 
 431:   /**
 432:    * Returns the suffix (<code>"SliderUI"</code> in this case) used to 
 433:    * determine the class name for a UI delegate that can provide the look and 
 434:    * feel for a <code>JSlider</code>.
 435:    *
 436:    * @return <code>"SliderUI"</code>.
 437:    */
 438:   public String getUIClassID()
 439:   {
 440:     return "SliderUI";
 441:   }
 442: 
 443:   /**
 444:    * Creates a {@link ChangeListener} that is added to the slider's model and
 445:    * forwards change events generated by the model to the listeners that are
 446:    * registered with the <code>JSlider</code> (by calling the 
 447:    * {@link #fireStateChanged} method).
 448:    *
 449:    * @return A new listener.
 450:    */
 451:   protected ChangeListener createChangeListener()
 452:   {
 453:     return new ChangeListener()
 454:       {
 455:         public void stateChanged(ChangeEvent ce)
 456:         {
 457:           // No need to trigger a repaint since the UI listens to the model
 458:           // as well. All we need to do is pass on the stateChanged event 
 459:           // to our listeners.
 460:           fireStateChanged();
 461:         }
 462:       };
 463:   }
 464: 
 465:   /**
 466:    * Registers a listener with the slider so that it will receive 
 467:    * {@link ChangeEvent} notifications.  Note that change events generated
 468:    * by the slider's model will be forwarded automatically to the slider's
 469:    * listeners.
 470:    *
 471:    * @param listener  the listener to register.
 472:    * 
 473:    * @see #removeChangeListener(ChangeListener)
 474:    */
 475:   public void addChangeListener(ChangeListener listener)
 476:   {
 477:     listenerList.add(ChangeListener.class, listener);
 478:   }
 479: 
 480:   /**
 481:    * Removes a listener from this slider so that it will no longer receive
 482:    * {@link ChangeEvent} notifications from the slider.
 483:    *
 484:    * @param listener The listener to remove.
 485:    * 
 486:    * @see #addChangeListener(ChangeListener)
 487:    */
 488:   public void removeChangeListener(ChangeListener listener)
 489:   {
 490:     listenerList.remove(ChangeListener.class, listener);
 491:   }
 492: 
 493:   /**
 494:    * Sends a {@link ChangeEvent} to all registered listeners, with this slider 
 495:    * as the source.
 496:    */
 497:   protected void fireStateChanged()
 498:   {
 499:     Object[] changeListeners = listenerList.getListenerList();
 500:     if (changeEvent == null)
 501:       changeEvent = new ChangeEvent(this);
 502:     for (int i = changeListeners.length - 2; i >= 0; i -= 2)
 503:       {
 504:         if (changeListeners[i] == ChangeListener.class)
 505:           ((ChangeListener) changeListeners[i + 1]).stateChanged(changeEvent);
 506:       }
 507:   }
 508: 
 509:   /**
 510:    * Returns an array containing all the {@link ChangeListener} instances 
 511:    * registered with this slider.  If no listeners are registered, this method
 512:    * returns an empty array.
 513:    *
 514:    * @return An array array containing all the {@link ChangeListener} instances 
 515:    *     registered with this slider (possibly empty, but never 
 516:    *     <code>null</code>).
 517:    */
 518:   public ChangeListener[] getChangeListeners()
 519:   {
 520:     return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
 521:   }
 522: 
 523:   /**
 524:    * Returns the slider's model, which stores the minimum, maximum and current 
 525:    * values.
 526:    *
 527:    * @return The slider's model.
 528:    * 
 529:    * @see #setModel(BoundedRangeModel)
 530:    */
 531:   public BoundedRangeModel getModel()
 532:   {
 533:     return sliderModel;
 534:   }
 535: 
 536:   /**
 537:    * Sets the slider's model and sends a {@link PropertyChangeEvent} (with the
 538:    * property name "model") to all registered listeners.   The change listener
 539:    * that the slider registered with the original model is removed and added
 540:    * to the new model (this ensures that {@link ChangeEvent} notifications 
 541:    * generated by the model are automatically forwarded to listeners that are
 542:    * registered with the slider).
 543:    *
 544:    * @param model The model to use with the slider.
 545:    * 
 546:    * @see #getModel()
 547:    */
 548:   public void setModel(BoundedRangeModel model)
 549:   {
 550:     // I didn't do the null pointer check on purpose.
 551:     // If you try it with Sun's, it'll go ahead and set it to null
 552:     // and bork the next time it tries to access the model.
 553:     if (model != sliderModel)
 554:       {
 555:         BoundedRangeModel oldModel = sliderModel;
 556:         sliderModel = model;
 557:         oldModel.removeChangeListener(changeListener);
 558:         sliderModel.addChangeListener(changeListener);
 559:         firePropertyChange("model", oldModel, sliderModel);
 560:       }
 561:   }
 562: 
 563:   /**
 564:    * Returns the minimum value of the slider (from the slider's model).
 565:    *
 566:    * @return The minimum value of the slider.
 567:    * 
 568:    * @see #setMinimum(int)
 569:    */
 570:   public int getMinimum()
 571:   {
 572:     return sliderModel.getMinimum();
 573:   }
 574: 
 575:   /**
 576:    * Sets the minimum value of the slider and fires a 
 577:    * {@link PropertyChangeEvent} (with the property name "minimum") to all
 578:    * registered listeners.  Note that:
 579:    * <p>
 580:    * <ul>
 581:    * <li>the minimum value is stored in the slider's model (see 
 582:    *     {@link #getModel()});</li>
 583:    * <li>in addition to the property change event, the slider also fires a 
 584:    *     {@link ChangeEvent}.</li>
 585:    * </ul>
 586:    * 
 587:    * @param minimum The minimum value of the slider.
 588:    * 
 589:    * @see #getMinimum()
 590:    */
 591:   public void setMinimum(int minimum)
 592:   {
 593:     int old = sliderModel.getMinimum();
 594:     sliderModel.setMinimum(minimum);
 595:     if (minimum != old)
 596:       firePropertyChange("minimum", old, minimum);
 597:   }
 598: 
 599:   /**
 600:    * Returns the slider's maximum value (obtained from the slider's model).
 601:    *
 602:    * @return The maximum value of the slider.
 603:    * 
 604:    * @see #setMaximum(int)
 605:    */
 606:   public int getMaximum()
 607:   {
 608:     return sliderModel.getMaximum();
 609:   }
 610: 
 611:   /**
 612:    * Sets the maximum value of the slider and fires a 
 613:    * {@link PropertyChangeEvent} (with the property name "maximum") to all
 614:    * registered listeners.  Note that:
 615:    * <p>
 616:    * <ul>
 617:    * <li>the maximum value is stored in the slider's model (see 
 618:    *     {@link #getModel()});</li>
 619:    * <li>in addition to the property change event, the slider also fires a 
 620:    *     {@link ChangeEvent}.</li>
 621:    * </ul>
 622:    *
 623:    * @param maximum The maximum value of the slider.
 624:    * 
 625:    * @see #getMaximum()
 626:    */
 627:   public void setMaximum(int maximum)
 628:   {
 629:     int old = sliderModel.getMaximum();
 630:     sliderModel.setMaximum(maximum);
 631:     if (maximum != old)
 632:       firePropertyChange("maximum", old, maximum);
 633:   }
 634: 
 635:   /**
 636:    * Returns the <code>valueIsAdjusting</code> flag from the slider's model.
 637:    *
 638:    * @return The <code>valueIsAdjusting</code> flag from the slider's model.
 639:    * 
 640:    * @see #setValueIsAdjusting(boolean)
 641:    */
 642:   public boolean getValueIsAdjusting()
 643:   {
 644:     return sliderModel.getValueIsAdjusting();
 645:   }
 646: 
 647:   /**
 648:    * Sets the <code>valueIsAdjusting</code> flag in the slider's model, and 
 649:    * sends a {@link ChangeEvent} to all registered listeners.
 650:    *
 651:    * @param adjusting  the new flag value.
 652:    * 
 653:    * @see #getValueIsAdjusting()
 654:    */
 655:   public void setValueIsAdjusting(boolean adjusting)
 656:   {
 657:     sliderModel.setValueIsAdjusting(adjusting);
 658:   }
 659: 
 660:   /**
 661:    * Returns the slider's extent value, obtained from the slider's model.
 662:    *
 663:    * @return The extent value.
 664:    * 
 665:    * @see #setExtent(int)
 666:    */
 667:   public int getExtent()
 668:   {
 669:     return sliderModel.getExtent();
 670:   }
 671: 
 672:   /**
 673:    * Sets the slider's extent value and sends a {@link ChangeEvent} to all 
 674:    * registered listeners.  Note that the model will fire a change event to all
 675:    * of its registered listeners first (with the model as the event source) and
 676:    * then the slider will fire another change event to all of its registered
 677:    * listeners (this time with the slider as the event source).
 678:    *
 679:    * @param extent The extent value for this slider.
 680:    * 
 681:    * @see #getExtent()
 682:    */
 683:   public void setExtent(int extent)
 684:   {
 685:     sliderModel.setExtent(extent);
 686:   }
 687: 
 688:   /**
 689:    * Returns the orientation of the slider, either {@link JSlider#HORIZONTAL}
 690:    * or {@link JSlider#VERTICAL}.
 691:    *
 692:    * @return The orientation of the slider.
 693:    * 
 694:    * @see #setOrientation(int)
 695:    */
 696:   public int getOrientation()
 697:   {
 698:     return orientation;
 699:   }
 700: 
 701:   /**
 702:    * Sets the orientation for the slider and sends a 
 703:    * {@link PropertyChangeEvent} (with the property name "orientation") to all
 704:    * registered listeners.
 705:    *
 706:    * @param orientation  the orientation (one of {@link JSlider#HORIZONTAL} or
 707:    *     {@link JSlider#VERTICAL}).
 708:    *     
 709:    * @throws IllegalArgumentException if <code>orientation</code> is not one of
 710:    *     the permitted values.
 711:    *     
 712:    * @see #getOrientation()
 713:    */
 714:   public void setOrientation(int orientation)
 715:   {
 716:     if (orientation != VERTICAL && orientation != HORIZONTAL)
 717:       throw new IllegalArgumentException(
 718:           "orientation must be one of: VERTICAL, HORIZONTAL");
 719:     if (orientation != this.orientation)
 720:       {
 721:         int oldOrientation = this.orientation;
 722:         this.orientation = orientation;
 723:         firePropertyChange("orientation", oldOrientation, this.orientation);
 724:       }
 725:   }
 726: 
 727:   /**
 728:    * Returns the label table for the slider.
 729:    *
 730:    * @return The label table for the slider (possibly <code>null</code>).
 731:    * 
 732:    * @see #setLabelTable(Dictionary)
 733:    */
 734:   public Dictionary getLabelTable()
 735:   {
 736:     return labelTable;
 737:   }
 738: 
 739:   /**
 740:    * Sets the table of labels for the slider and sends a 
 741:    * {@link PropertyChangeEvent} (with the property name "labelTable") to all 
 742:    * registered listeners.
 743:    *
 744:    * @param table  the table of labels (<code>null</code> permitted).
 745:    * 
 746:    * @see #getLabelTable()
 747:    */
 748:   public void setLabelTable(Dictionary table)
 749:   {
 750:     if (table != labelTable)
 751:       {
 752:         Dictionary oldTable = labelTable;
 753:         labelTable = table;
 754:         firePropertyChange("labelTable", oldTable, labelTable);
 755:       }
 756:   }
 757: 
 758:   /**
 759:    * Resets the UI delegates for the labels in the <code>labelTable</code> to 
 760:    * the default for the current look and feel.
 761:    */
 762:   protected void updateLabelUIs()
 763:   {
 764:     if (labelTable == null)
 765:       return;
 766:     for (Enumeration list = labelTable.elements(); list.hasMoreElements();)
 767:       {
 768:         JLabel label = (JLabel) list.nextElement();
 769:         label.updateUI();
 770:       }
 771:   }
 772: 
 773:   /**
 774:    * Creates a hashtable of <code>(Integer, JLabel)</code> pairs that can be 
 775:    * used as a label table for this slider. The labels will start from the 
 776:    * slider's minimum and increase by the increment. Each label will have a text
 777:    * string indicating its integer value.
 778:    *
 779:    * @param increment The increment between labels (must be > 0).
 780:    *
 781:    * @return A hashtable containing the labels.
 782:    *
 783:    * @throws IllegalArgumentException if <code>increment</code> is not greater
 784:    *         than zero.
 785:    */
 786:   public Hashtable createStandardLabels(int increment)
 787:   {
 788:     return createStandardLabels(increment, sliderModel.getMinimum());
 789:   }
 790: 
 791:   /**
 792:    * Creates a hashtable of <code>(Integer, JLabel)</code> pairs that can be 
 793:    * used as a label table for this slider. The labels will start from the 
 794:    * given start value and increase by the increment. Each  label will have a 
 795:    * text string indicating its integer value.
 796:    *
 797:    * @param increment The increment between labels (must be > 0).
 798:    * @param start The value to start from.
 799:    *
 800:    * @return A hashtable with the labels and their keys.
 801:    *
 802:    * @throws IllegalArgumentException if <code>increment</code> is not greater
 803:    *         than zero, or <code>start</code> is not within the range of the
 804:    *         model.
 805:    */
 806:   public Hashtable createStandardLabels(int increment, int start)
 807:   {
 808:     if (increment <= 0) 
 809:       throw new IllegalArgumentException("Requires 'increment' > 0.");
 810:     if (start < getMinimum() || start > getMaximum())
 811:       throw new IllegalArgumentException("The 'start' value is out of range.");
 812:     Hashtable table = new Hashtable();
 813:     JLabel label;
 814:     Dimension dim;
 815: 
 816:     int max = sliderModel.getMaximum();
 817: 
 818:     for (int i = start; i <= max; i += increment)
 819:       {
 820:         label = new JLabel(String.valueOf(i));
 821:         label.setVerticalAlignment(CENTER);
 822:         label.setHorizontalAlignment(CENTER);
 823:         
 824:         // Make sure these labels have the width and height
 825:         // they want.
 826:         dim = label.getPreferredSize();
 827:         label.setBounds(label.getX(), label.getY(),
 828:                         (int) dim.getWidth(),
 829:                         (int) dim.getHeight()); 
 830:         table.put(new Integer(i), label);
 831:       }
 832:     return table;
 833:   }
 834: 
 835:   /**
 836:    * Returns the flag that controls whether or not the value scale for the
 837:    * slider is inverted (the default value is <code>false</code>).
 838:    *
 839:    * @return The flag that controls whether or not the value scale for the
 840:    *     slider is inverted.
 841:    *     
 842:    * @see #setInverted(boolean)
 843:    */
 844:   public boolean getInverted()
 845:   {
 846:     return isInverted;
 847:   }
 848: 
 849:   /**
 850:    * Sets the flag that controls whether or not the value scale for the
 851:    * slider is inverted and, if the new flag value is different to the old flag
 852:    * value, sends a {@link PropertyChangeEvent} to all registered listeners.
 853:    * Typically, a horizontal slider will display a scale that increases from 
 854:    * left to right, but this is reversed if the 'inverted' flag is set to 
 855:    * <code>true</code>.  Similarly, a vertical slider will display a scale that
 856:    * increases from bottom to top, and this is reversed if the 'inverted' flag
 857:    * is set to <code>true</code>.
 858:    *
 859:    * @param inverted  the new flag value.
 860:    * 
 861:    * @see #getInverted()
 862:    */
 863:   public void setInverted(boolean inverted)
 864:   {
 865:     if (isInverted != inverted)
 866:       {
 867:         boolean oldInverted = isInverted;
 868:         isInverted = inverted;
 869:         firePropertyChange("inverted", oldInverted, isInverted);
 870:       }
 871:   }
 872: 
 873:   /**
 874:    * Returns the distance between major tick marks along the slider's value 
 875:    * scale.
 876:    *
 877:    * @return The amount of units between each major tick mark.
 878:    * 
 879:    * @see #setMajorTickSpacing(int)
 880:    */
 881:   public int getMajorTickSpacing()
 882:   {
 883:     return majorTickSpacing;
 884:   }
 885: 
 886:   /**
 887:    * Sets the distance between major tick marks along the slider's value scale, 
 888:    * and sends a {@link PropertyChangeEvent} (with the property name 
 889:    * "majorTickSpacing") to all registered listeners.
 890:    *
 891:    * @param spacing  the distance between major tick marks.
 892:    * 
 893:    * @see #getMajorTickSpacing()
 894:    */
 895:   public void setMajorTickSpacing(int spacing)
 896:   {
 897:     if (majorTickSpacing != spacing)
 898:       {
 899:         int oldSpacing = majorTickSpacing;
 900:         majorTickSpacing = spacing;
 901:         firePropertyChange("majorTickSpacing", oldSpacing, majorTickSpacing);
 902:       }
 903:   }
 904: 
 905:   /**
 906:    * Returns the distance between minor tick marks along the slider's value 
 907:    * scale.
 908:    *
 909:    * @return The distance between minor tick marks along the slider's value 
 910:    *     scale.
 911:    *     
 912:    * @see #setMinorTickSpacing(int)
 913:    */
 914:   public int getMinorTickSpacing()
 915:   {
 916:     return minorTickSpacing;
 917:   }
 918: 
 919:   /**
 920:    * Sets the distance between minor tick marks along the slider's value scale, 
 921:    * and sends a {@link PropertyChangeEvent} (with the property name 
 922:    * "minorTickSpacing") to all registered listeners.
 923:    *
 924:    * @param spacing  the distance between minor tick marks.
 925:    * 
 926:    * @see #getMinorTickSpacing()
 927:    */
 928:   public void setMinorTickSpacing(int spacing)
 929:   {
 930:     if (minorTickSpacing != spacing)
 931:       {
 932:         int oldSpacing = minorTickSpacing;
 933:         minorTickSpacing = spacing;
 934:         firePropertyChange("minorTickSpacing", oldSpacing, minorTickSpacing);
 935:       }
 936:   }
 937: 
 938:   /**
 939:    * Returns the flag that controls whether the slider thumb will snap to ticks.
 940:    * Sliders that snap to ticks will automatically move the thumb to the 
 941:    * nearest tick mark.
 942:    *
 943:    * @return <code>true</code> if the slider thumb automatically.
 944:    * 
 945:    * @see #setSnapToTicks(boolean)
 946:    */
 947:   public boolean getSnapToTicks()
 948:   {
 949:     return snapToTicks;
 950:   }
 951: 
 952:   /**
 953:    * Sets the flag that controls whether the slider thumb will snap to ticks 
 954:    * and sends a {@link PropertyChangeEvent} (with the property name 
 955:    * 'snapToTicks') to all registered listeners. Sliders that snap to ticks 
 956:    * will automatically move the thumb to the nearest tick mark.
 957:    *
 958:    * @param snap  the new flag value.
 959:    * 
 960:    * @see #getSnapToTicks()
 961:    */
 962:   public void setSnapToTicks(boolean snap)
 963:   {
 964:     if (snap != snapToTicks)
 965:       {
 966:         snapToTicks = snap;
 967:         firePropertyChange("snapToTicks", !snap, snap);
 968:       }
 969:   }
 970: 
 971:   /**
 972:    * Returns the flag that controls whether or not tick marks are painted along
 973:    * the slider's value scale.
 974:    *
 975:    * @return <code>true</code> if tick marks should be painted, and 
 976:    *     <code>false</code> if tick marks should not be painted.
 977:    *     
 978:    * @see #setPaintTicks(boolean)
 979:    */
 980:   public boolean getPaintTicks()
 981:   {
 982:     return paintTicks;
 983:   }
 984: 
 985:   /**
 986:    * Sets the flag that controls whether or not tick marks are painted along
 987:    * the slider's value scale, and sends a {@link PropertyChangeEvent} (with 
 988:    * the property name "paintTicks") to all registered listeners. In
 989:    * addition to setting this property to <code>true</code>, one or both of the
 990:    * minor tick spacing and major tick spacing attributes must be set to a 
 991:    * value greater than 0 in order for ticks to be painted.
 992:    *
 993:    * @param paint Whether ticks will be painted.
 994:    * 
 995:    * @see #getPaintTicks()
 996:    */
 997:   public void setPaintTicks(boolean paint)
 998:   {
 999:     if (paint != paintTicks)
1000:       {
1001:         boolean oldPaintTicks = paintTicks;
1002:         paintTicks = paint;
1003:         firePropertyChange("paintTicks", oldPaintTicks, paintTicks);
1004:       }
1005:   }
1006: 
1007:   /**
1008:    * Returns the flag that controls whether or not the track is painted.
1009:    *
1010:    * @return Whether the track will be painted.
1011:    * 
1012:    * @see #setPaintTrack(boolean)
1013:    */
1014:   public boolean getPaintTrack()
1015:   {
1016:     return paintTrack;
1017:   }
1018: 
1019:   /**
1020:    * Sets the flag that controls whether or not the track is painted, and
1021:    * sends a {@link PropertyChangeEvent} (for the "paintTrack" property) to all
1022:    * registered listeners.
1023:    *
1024:    * @param paint Whether the track will be painted.
1025:    * 
1026:    * @see #getPaintTrack()
1027:    */
1028:   public void setPaintTrack(boolean paint)
1029:   {
1030:     if (paintTrack != paint)
1031:     {
1032:       paintTrack = paint;
1033:       firePropertyChange("paintTrack", !paint, paint);
1034:     }
1035:   }
1036: 
1037:   /**
1038:    * Returns the flag that controls whether or not labels are painted for the
1039:    * tick marks along the slider.
1040:    *
1041:    * @return Whether labels will be painted.
1042:    * 
1043:    * @see #setPaintLabels(boolean)
1044:    */
1045:   public boolean getPaintLabels()
1046:   {
1047:     return paintLabels;
1048:   }
1049: 
1050:   /**
1051:    * Sets the flag that controls whether or not labels are painted for the
1052:    * tick marks along the slider and sends a {@link PropertyChangeEvent} (with 
1053:    * the property name "paintLabels") to all registered listeners.
1054:    *
1055:    * @param paint Whether labels will be painted.
1056:    * 
1057:    * @see #getPaintLabels()
1058:    */
1059:   public void setPaintLabels(boolean paint)
1060:   {
1061:     if (paint != paintLabels)
1062:       {
1063:         paintLabels = paint;
1064:         if (paint && majorTickSpacing > 0 && labelTable == null)
1065:           labelTable = createStandardLabels(majorTickSpacing);
1066:         firePropertyChange("paintLabels", !paint, paint);
1067:       }
1068:   }
1069: 
1070:   /**
1071:    * Returns an implementation-dependent string describing the attributes of
1072:    * this <code>JSlider</code>.
1073:    *
1074:    * @return A string describing the attributes of this <code>JSlider</code>
1075:    *         (never <code>null</code>).
1076:    */
1077:   protected String paramString()
1078:   {
1079:     String superParamStr = super.paramString();
1080:     StringBuffer sb = new StringBuffer();
1081:     sb.append(",isInverted=").append(getInverted());
1082:     sb.append(",majorTickSpacing=").append(getMajorTickSpacing());
1083:     sb.append(",minorTickSpacing=").append(getMinorTickSpacing());
1084:     sb.append(",orientation=");
1085:     if (orientation == HORIZONTAL)
1086:       sb.append("HORIZONTAL");
1087:     else
1088:       sb.append("VERTICAL");
1089:     sb.append(",paintLabels=").append(getPaintLabels());
1090:     sb.append(",paintTicks=").append(getPaintTicks());
1091:     sb.append(",paintTrack=").append(getPaintTrack());
1092:     sb.append(",snapToTicks=").append(getSnapToTicks());
1093:     
1094:     // the following is output by the reference implementation.  We don't
1095:     // strictly need to replicate this. Perhaps it has some meaning, but
1096:     // I couldn't determine it yet...
1097:     sb.append(",snapToValue=true");
1098: 
1099:     return superParamStr + sb.toString();
1100:   }
1101: 
1102:   /**
1103:    * Returns the object that provides accessibility features for this
1104:    * <code>JSlider</code> component.
1105:    *
1106:    * @return The accessible context (an instance of {@link AccessibleJSlider}).
1107:    */
1108:   public AccessibleContext getAccessibleContext()
1109:   {
1110:     if (accessibleContext == null)
1111:       accessibleContext = new AccessibleJSlider();
1112:     
1113:     return accessibleContext;
1114:   }
1115: }