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