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