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