Frames | No Frames |
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: }