1 /* 2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing; 26 27 import javax.swing.event.*; 28 import javax.swing.plaf.*; 29 import javax.accessibility.*; 30 31 import java.io.Serializable; 32 import java.io.ObjectOutputStream; 33 import java.io.IOException; 34 35 import java.awt.*; 36 import java.util.*; 37 import java.beans.JavaBean; 38 import java.beans.BeanProperty; 39 import java.beans.PropertyChangeEvent; 40 import java.beans.PropertyChangeListener; 41 42 /** 43 * A component that lets the user graphically select a value by sliding 44 * a knob within a bounded interval. The knob is always positioned 45 * at the points that match integer values within the specified interval. 46 * <p> 47 * The slider can show both 48 * major tick marks, and minor tick marks between the major ones. The number of 49 * values between the tick marks is controlled with 50 * <code>setMajorTickSpacing</code> and <code>setMinorTickSpacing</code>. 51 * Painting of tick marks is controlled by {@code setPaintTicks}. 52 * <p> 53 * Sliders can also print text labels at regular intervals (or at 54 * arbitrary locations) along the slider track. Painting of labels is 55 * controlled by {@code setLabelTable} and {@code setPaintLabels}. 56 * <p> 57 * For further information and examples see 58 * <a 59 href="https://docs.oracle.com/javase/tutorial/uiswing/components/slider.html">How to Use Sliders</a>, 60 * a section in <em>The Java Tutorial.</em> 61 * <p> 62 * <strong>Warning:</strong> Swing is not thread safe. For more 63 * information see <a 64 * href="package-summary.html#threading">Swing's Threading 65 * Policy</a>. 66 * <p> 67 * <strong>Warning:</strong> 68 * Serialized objects of this class will not be compatible with 69 * future Swing releases. The current serialization support is 70 * appropriate for short term storage or RMI between applications running 71 * the same version of Swing. As of 1.4, support for long term storage 72 * of all JavaBeans 73 * has been added to the <code>java.beans</code> package. 74 * Please see {@link java.beans.XMLEncoder}. 75 * 76 * @author David Kloba 77 * @since 1.2 78 */ 79 @JavaBean(defaultProperty = "UI", description = "A component that supports selecting a integer value from a range.") 80 @SwingContainer(false) 81 @SuppressWarnings("serial") // Same-version serialization only 82 public class JSlider extends JComponent implements SwingConstants, Accessible { 83 /** 84 * @see #getUIClassID 85 * @see #readObject 86 */ 87 private static final String uiClassID = "SliderUI"; 88 89 private boolean paintTicks = false; 90 private boolean paintTrack = true; 91 private boolean paintLabels = false; 92 private boolean isInverted = false; 93 94 /** 95 * The data model that handles the numeric maximum value, 96 * minimum value, and current-position value for the slider. 97 */ 98 protected BoundedRangeModel sliderModel; 99 100 /** 101 * The number of values between the major tick marks -- the 102 * larger marks that break up the minor tick marks. 103 */ 104 protected int majorTickSpacing; 105 106 /** 107 * The number of values between the minor tick marks -- the 108 * smaller marks that occur between the major tick marks. 109 * @see #setMinorTickSpacing 110 */ 111 protected int minorTickSpacing; 112 113 /** 114 * If true, the knob (and the data value it represents) 115 * resolve to the closest tick mark next to where the user 116 * positioned the knob. The default is false. 117 * @see #setSnapToTicks 118 */ 119 protected boolean snapToTicks = false; 120 121 /** 122 * If true, the knob (and the data value it represents) 123 * resolve to the closest slider value next to where the user 124 * positioned the knob. 125 */ 126 boolean snapToValue = true; 127 128 /** 129 * Whether the slider is horizontal or vertical 130 * The default is horizontal. 131 * 132 * @see #setOrientation 133 */ 134 protected int orientation; 135 136 137 /** 138 * {@code Dictionary} of what labels to draw at which values 139 */ 140 @SuppressWarnings("rawtypes") 141 private Dictionary labelTable; 142 // For better source compatibility, the labelTable field and 143 // associated getter and setter methods are being left as raw 144 // types. 145 146 /** 147 * The changeListener (no suffix) is the listener we add to the 148 * slider's model. This listener is initialized to the 149 * {@code ChangeListener} returned from {@code createChangeListener}, 150 * which by default just forwards events 151 * to {@code ChangeListener}s (if any) added directly to the slider. 152 * 153 * @see #addChangeListener 154 * @see #createChangeListener 155 */ 156 protected ChangeListener changeListener = createChangeListener(); 157 158 159 /** 160 * Only one <code>ChangeEvent</code> is needed per slider instance since the 161 * event's only (read-only) state is the source property. The source 162 * of events generated here is always "this". The event is lazily 163 * created the first time that an event notification is fired. 164 * 165 * @see #fireStateChanged 166 */ 167 protected transient ChangeEvent changeEvent = null; 168 169 170 private void checkOrientation(int orientation) { 171 switch (orientation) { 172 case VERTICAL: 173 case HORIZONTAL: 174 break; 175 default: 176 throw new IllegalArgumentException("orientation must be one of: VERTICAL, HORIZONTAL"); 177 } 178 } 179 180 181 /** 182 * Creates a horizontal slider with the range 0 to 100 and 183 * an initial value of 50. 184 */ 185 public JSlider() { 186 this(HORIZONTAL, 0, 100, 50); 187 } 188 189 190 /** 191 * Creates a slider using the specified orientation with the 192 * range {@code 0} to {@code 100} and an initial value of {@code 50}. 193 * The orientation can be 194 * either <code>SwingConstants.VERTICAL</code> or 195 * <code>SwingConstants.HORIZONTAL</code>. 196 * 197 * @param orientation the orientation of the slider 198 * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL} 199 * @see #setOrientation 200 */ 201 public JSlider(int orientation) { 202 this(orientation, 0, 100, 50); 203 } 204 205 206 /** 207 * Creates a horizontal slider using the specified min and max 208 * with an initial value equal to the average of the min plus max. 209 * <p> 210 * The <code>BoundedRangeModel</code> that holds the slider's data 211 * handles any issues that may arise from improperly setting the 212 * minimum and maximum values on the slider. See the 213 * {@code BoundedRangeModel} documentation for details. 214 * 215 * @param min the minimum value of the slider 216 * @param max the maximum value of the slider 217 * 218 * @see BoundedRangeModel 219 * @see #setMinimum 220 * @see #setMaximum 221 */ 222 public JSlider(int min, int max) { 223 this(HORIZONTAL, min, max, (min + max) / 2); 224 } 225 226 227 /** 228 * Creates a horizontal slider using the specified min, max and value. 229 * <p> 230 * The <code>BoundedRangeModel</code> that holds the slider's data 231 * handles any issues that may arise from improperly setting the 232 * minimum, initial, and maximum values on the slider. See the 233 * {@code BoundedRangeModel} documentation for details. 234 * 235 * @param min the minimum value of the slider 236 * @param max the maximum value of the slider 237 * @param value the initial value of the slider 238 * 239 * @see BoundedRangeModel 240 * @see #setMinimum 241 * @see #setMaximum 242 * @see #setValue 243 */ 244 public JSlider(int min, int max, int value) { 245 this(HORIZONTAL, min, max, value); 246 } 247 248 249 /** 250 * Creates a slider with the specified orientation and the 251 * specified minimum, maximum, and initial values. 252 * The orientation can be 253 * either <code>SwingConstants.VERTICAL</code> or 254 * <code>SwingConstants.HORIZONTAL</code>. 255 * <p> 256 * The <code>BoundedRangeModel</code> that holds the slider's data 257 * handles any issues that may arise from improperly setting the 258 * minimum, initial, and maximum values on the slider. See the 259 * {@code BoundedRangeModel} documentation for details. 260 * 261 * @param orientation the orientation of the slider 262 * @param min the minimum value of the slider 263 * @param max the maximum value of the slider 264 * @param value the initial value of the slider 265 * 266 * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL} 267 * 268 * @see BoundedRangeModel 269 * @see #setOrientation 270 * @see #setMinimum 271 * @see #setMaximum 272 * @see #setValue 273 */ 274 public JSlider(int orientation, int min, int max, int value) 275 { 276 checkOrientation(orientation); 277 this.orientation = orientation; 278 setModel(new DefaultBoundedRangeModel(value, 0, min, max)); 279 updateUI(); 280 } 281 282 283 /** 284 * Creates a horizontal slider using the specified 285 * BoundedRangeModel. 286 * 287 * @param brm a {@code BoundedRangeModel} for the slider 288 */ 289 public JSlider(BoundedRangeModel brm) 290 { 291 this.orientation = JSlider.HORIZONTAL; 292 setModel(brm); 293 updateUI(); 294 } 295 296 297 /** 298 * Gets the UI object which implements the L&F for this component. 299 * 300 * @return the SliderUI object that implements the Slider L&F 301 */ 302 public SliderUI getUI() { 303 return(SliderUI)ui; 304 } 305 306 307 /** 308 * Sets the UI object which implements the L&F for this component. 309 * 310 * @param ui the SliderUI L&F object 311 * @see UIDefaults#getUI 312 */ 313 @BeanProperty(hidden = true, visualUpdate = true, description 314 = "The UI object that implements the slider's LookAndFeel.") 315 public void setUI(SliderUI ui) { 316 super.setUI(ui); 317 } 318 319 320 /** 321 * Resets the UI property to a value from the current look and feel. 322 * 323 * @see JComponent#updateUI 324 */ 325 public void updateUI() { 326 setUI((SliderUI)UIManager.getUI(this)); 327 // The labels preferred size may be derived from the font 328 // of the slider, so we must update the UI of the slider first, then 329 // that of labels. This way when setSize is called the right 330 // font is used. 331 updateLabelUIs(); 332 } 333 334 335 /** 336 * Returns the name of the L&F class that renders this component. 337 * 338 * @return the string "SliderUI" 339 * @see JComponent#getUIClassID 340 * @see UIDefaults#getUI 341 */ 342 @BeanProperty(bound = false) 343 public String getUIClassID() { 344 return uiClassID; 345 } 346 347 348 /** 349 * We pass Change events along to the listeners with the 350 * the slider (instead of the model itself) as the event source. 351 */ 352 private class ModelListener implements ChangeListener, Serializable { 353 public void stateChanged(ChangeEvent e) { 354 fireStateChanged(); 355 } 356 } 357 358 359 /** 360 * Subclasses that want to handle {@code ChangeEvent}s 361 * from the model differently 362 * can override this to return 363 * an instance of a custom <code>ChangeListener</code> implementation. 364 * The default {@code ChangeListener} simply calls the 365 * {@code fireStateChanged} method to forward {@code ChangeEvent}s 366 * to the {@code ChangeListener}s that have been added directly to the 367 * slider. 368 * 369 * @return a instance of new {@code ChangeListener} 370 * @see #changeListener 371 * @see #fireStateChanged 372 * @see javax.swing.event.ChangeListener 373 * @see javax.swing.BoundedRangeModel 374 */ 375 protected ChangeListener createChangeListener() { 376 return new ModelListener(); 377 } 378 379 380 /** 381 * Adds a ChangeListener to the slider. 382 * 383 * @param l the ChangeListener to add 384 * @see #fireStateChanged 385 * @see #removeChangeListener 386 */ 387 public void addChangeListener(ChangeListener l) { 388 listenerList.add(ChangeListener.class, l); 389 } 390 391 392 /** 393 * Removes a ChangeListener from the slider. 394 * 395 * @param l the ChangeListener to remove 396 * @see #fireStateChanged 397 * @see #addChangeListener 398 399 */ 400 public void removeChangeListener(ChangeListener l) { 401 listenerList.remove(ChangeListener.class, l); 402 } 403 404 405 /** 406 * Returns an array of all the <code>ChangeListener</code>s added 407 * to this JSlider with addChangeListener(). 408 * 409 * @return all of the <code>ChangeListener</code>s added or an empty 410 * array if no listeners have been added 411 * @since 1.4 412 */ 413 @BeanProperty(bound = false) 414 public ChangeListener[] getChangeListeners() { 415 return listenerList.getListeners(ChangeListener.class); 416 } 417 418 419 /** 420 * Send a {@code ChangeEvent}, whose source is this {@code JSlider}, to 421 * all {@code ChangeListener}s that have registered interest in 422 * {@code ChangeEvent}s. 423 * This method is called each time a {@code ChangeEvent} is received from 424 * the model. 425 * <p> 426 * The event instance is created if necessary, and stored in 427 * {@code changeEvent}. 428 * 429 * @see #addChangeListener 430 * @see EventListenerList 431 */ 432 protected void fireStateChanged() { 433 Object[] listeners = listenerList.getListenerList(); 434 for (int i = listeners.length - 2; i >= 0; i -= 2) { 435 if (listeners[i]==ChangeListener.class) { 436 if (changeEvent == null) { 437 changeEvent = new ChangeEvent(this); 438 } 439 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent); 440 } 441 } 442 } 443 444 445 /** 446 * Returns the {@code BoundedRangeModel} that handles the slider's three 447 * fundamental properties: minimum, maximum, value. 448 * 449 * @return the data model for this component 450 * @see #setModel 451 * @see BoundedRangeModel 452 */ 453 public BoundedRangeModel getModel() { 454 return sliderModel; 455 } 456 457 458 /** 459 * Sets the {@code BoundedRangeModel} that handles the slider's three 460 * fundamental properties: minimum, maximum, value. 461 *<p> 462 * Attempts to pass a {@code null} model to this method result in 463 * undefined behavior, and, most likely, exceptions. 464 * 465 * @param newModel the new, {@code non-null} <code>BoundedRangeModel</code> to use 466 * 467 * @see #getModel 468 * @see BoundedRangeModel 469 */ 470 @BeanProperty(description 471 = "The sliders BoundedRangeModel.") 472 public void setModel(BoundedRangeModel newModel) 473 { 474 BoundedRangeModel oldModel = getModel(); 475 476 if (oldModel != null) { 477 oldModel.removeChangeListener(changeListener); 478 } 479 480 sliderModel = newModel; 481 482 if (newModel != null) { 483 newModel.addChangeListener(changeListener); 484 } 485 486 if (accessibleContext != null) { 487 accessibleContext.firePropertyChange( 488 AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, 489 (oldModel == null 490 ? null : Integer.valueOf(oldModel.getValue())), 491 (newModel == null 492 ? null : Integer.valueOf(newModel.getValue()))); 493 } 494 495 firePropertyChange("model", oldModel, sliderModel); 496 } 497 498 499 /** 500 * Returns the slider's current value 501 * from the {@code BoundedRangeModel}. 502 * 503 * @return the current value of the slider 504 * @see #setValue 505 * @see BoundedRangeModel#getValue 506 */ 507 public int getValue() { 508 return getModel().getValue(); 509 } 510 511 /** 512 * Sets the slider's current value to {@code n}. This method 513 * forwards the new value to the model. 514 * <p> 515 * The data model (an instance of {@code BoundedRangeModel}) 516 * handles any mathematical 517 * issues arising from assigning faulty values. See the 518 * {@code BoundedRangeModel} documentation for details. 519 * <p> 520 * If the new value is different from the previous value, 521 * all change listeners are notified. 522 * 523 * @param n the new value 524 * @see #getValue 525 * @see #addChangeListener 526 * @see BoundedRangeModel#setValue 527 */ 528 @BeanProperty(bound = false, preferred = true, description 529 = "The sliders current value.") 530 public void setValue(int n) { 531 BoundedRangeModel m = getModel(); 532 int oldValue = m.getValue(); 533 if (oldValue == n) { 534 return; 535 } 536 m.setValue(n); 537 538 if (accessibleContext != null) { 539 accessibleContext.firePropertyChange( 540 AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, 541 Integer.valueOf(oldValue), 542 Integer.valueOf(m.getValue())); 543 } 544 } 545 546 547 /** 548 * Returns the minimum value supported by the slider 549 * from the <code>BoundedRangeModel</code>. 550 * 551 * @return the value of the model's minimum property 552 * @see #setMinimum 553 * @see BoundedRangeModel#getMinimum 554 */ 555 public int getMinimum() { 556 return getModel().getMinimum(); 557 } 558 559 560 /** 561 * Sets the slider's minimum value to {@code minimum}. This method 562 * forwards the new minimum value to the model. 563 * <p> 564 * The data model (an instance of {@code BoundedRangeModel}) 565 * handles any mathematical 566 * issues arising from assigning faulty values. See the 567 * {@code BoundedRangeModel} documentation for details. 568 * <p> 569 * If the new minimum value is different from the previous minimum value, 570 * all change listeners are notified. 571 * 572 * @param minimum the new minimum 573 * @see #getMinimum 574 * @see #addChangeListener 575 * @see BoundedRangeModel#setMinimum 576 */ 577 @BeanProperty(preferred = true, description 578 = "The sliders minimum value.") 579 public void setMinimum(int minimum) { 580 int oldMin = getModel().getMinimum(); 581 getModel().setMinimum(minimum); 582 firePropertyChange( "minimum", Integer.valueOf( oldMin ), Integer.valueOf( minimum ) ); 583 } 584 585 586 /** 587 * Returns the maximum value supported by the slider 588 * from the <code>BoundedRangeModel</code>. 589 * 590 * @return the value of the model's maximum property 591 * @see #setMaximum 592 * @see BoundedRangeModel#getMaximum 593 */ 594 public int getMaximum() { 595 return getModel().getMaximum(); 596 } 597 598 599 /** 600 * Sets the slider's maximum value to {@code maximum}. This method 601 * forwards the new maximum value to the model. 602 * <p> 603 * The data model (an instance of {@code BoundedRangeModel}) 604 * handles any mathematical 605 * issues arising from assigning faulty values. See the 606 * {@code BoundedRangeModel} documentation for details. 607 * <p> 608 * If the new maximum value is different from the previous maximum value, 609 * all change listeners are notified. 610 * 611 * @param maximum the new maximum 612 * @see #getMaximum 613 * @see #addChangeListener 614 * @see BoundedRangeModel#setMaximum 615 */ 616 @BeanProperty(preferred = true, description 617 = "The sliders maximum value.") 618 public void setMaximum(int maximum) { 619 int oldMax = getModel().getMaximum(); 620 getModel().setMaximum(maximum); 621 firePropertyChange( "maximum", Integer.valueOf( oldMax ), Integer.valueOf( maximum ) ); 622 } 623 624 625 /** 626 * Returns the {@code valueIsAdjusting} property from the model. For 627 * details on how this is used, see the {@code setValueIsAdjusting} 628 * documentation. 629 * 630 * @return the value of the model's {@code valueIsAdjusting} property 631 * @see #setValueIsAdjusting 632 */ 633 public boolean getValueIsAdjusting() { 634 return getModel().getValueIsAdjusting(); 635 } 636 637 638 /** 639 * Sets the model's {@code valueIsAdjusting} property. Slider look and 640 * feel implementations should set this property to {@code true} when 641 * a knob drag begins, and to {@code false} when the drag ends. 642 * 643 * @param b the new value for the {@code valueIsAdjusting} property 644 * @see #getValueIsAdjusting 645 * @see BoundedRangeModel#setValueIsAdjusting 646 */ 647 @BeanProperty(bound = false, expert = true, description 648 = "True if the slider knob is being dragged.") 649 public void setValueIsAdjusting(boolean b) { 650 BoundedRangeModel m = getModel(); 651 boolean oldValue = m.getValueIsAdjusting(); 652 m.setValueIsAdjusting(b); 653 654 if ((oldValue != b) && (accessibleContext != null)) { 655 accessibleContext.firePropertyChange( 656 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 657 ((oldValue) ? AccessibleState.BUSY : null), 658 ((b) ? AccessibleState.BUSY : null)); 659 } 660 } 661 662 663 /** 664 * Returns the "extent" from the <code>BoundedRangeModel</code>. 665 * This represents the range of values "covered" by the knob. 666 * 667 * @return an int representing the extent 668 * @see #setExtent 669 * @see BoundedRangeModel#getExtent 670 */ 671 public int getExtent() { 672 return getModel().getExtent(); 673 } 674 675 676 /** 677 * Sets the size of the range "covered" by the knob. Most look 678 * and feel implementations will change the value by this amount 679 * if the user clicks on either side of the knob. This method just 680 * forwards the new extent value to the model. 681 * <p> 682 * The data model (an instance of {@code BoundedRangeModel}) 683 * handles any mathematical 684 * issues arising from assigning faulty values. See the 685 * {@code BoundedRangeModel} documentation for details. 686 * <p> 687 * If the new extent value is different from the previous extent value, 688 * all change listeners are notified. 689 * 690 * @param extent the new extent 691 * @see #getExtent 692 * @see BoundedRangeModel#setExtent 693 */ 694 @BeanProperty(bound = false, expert = true, description 695 = "Size of the range covered by the knob.") 696 public void setExtent(int extent) { 697 getModel().setExtent(extent); 698 } 699 700 701 /** 702 * Return this slider's vertical or horizontal orientation. 703 * @return {@code SwingConstants.VERTICAL} or 704 * {@code SwingConstants.HORIZONTAL} 705 * @see #setOrientation 706 */ 707 public int getOrientation() { 708 return orientation; 709 } 710 711 712 /** 713 * Set the slider's orientation to either {@code SwingConstants.VERTICAL} or 714 * {@code SwingConstants.HORIZONTAL}. 715 * 716 * @param orientation {@code HORIZONTAL} or {@code VERTICAL} 717 * @throws IllegalArgumentException if orientation is not one of {@code VERTICAL}, {@code HORIZONTAL} 718 * @see #getOrientation 719 */ 720 @BeanProperty(preferred = true, visualUpdate = true, enumerationValues = { 721 "JSlider.VERTICAL", 722 "JSlider.HORIZONTAL"}, description 723 = "Set the scrollbars orientation to either VERTICAL or HORIZONTAL.") 724 public void setOrientation(int orientation) 725 { 726 checkOrientation(orientation); 727 int oldValue = this.orientation; 728 this.orientation = orientation; 729 firePropertyChange("orientation", oldValue, orientation); 730 731 if ((oldValue != orientation) && (accessibleContext != null)) { 732 accessibleContext.firePropertyChange( 733 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 734 ((oldValue == VERTICAL) 735 ? AccessibleState.VERTICAL : AccessibleState.HORIZONTAL), 736 ((orientation == VERTICAL) 737 ? AccessibleState.VERTICAL : AccessibleState.HORIZONTAL)); 738 } 739 if (orientation != oldValue) { 740 revalidate(); 741 } 742 } 743 744 745 /** 746 * {@inheritDoc} 747 * 748 * @since 1.6 749 */ 750 public void setFont(Font font) { 751 super.setFont(font); 752 updateLabelSizes(); 753 } 754 755 /** 756 * {@inheritDoc} 757 * @since 1.7 758 */ 759 public boolean imageUpdate(Image img, int infoflags, int x, int y, int w, int h) { 760 if (!isShowing()) { 761 return false; 762 } 763 764 // Check that there is a label with such image 765 Enumeration<?> elements = labelTable.elements(); 766 767 while (elements.hasMoreElements()) { 768 Component component = (Component) elements.nextElement(); 769 770 if (component instanceof JLabel) { 771 JLabel label = (JLabel) component; 772 773 if (SwingUtilities.doesIconReferenceImage(label.getIcon(), img) || 774 SwingUtilities.doesIconReferenceImage(label.getDisabledIcon(), img)) { 775 return super.imageUpdate(img, infoflags, x, y, w, h); 776 } 777 } 778 } 779 780 return false; 781 } 782 783 /** 784 * Returns the dictionary of what labels to draw at which values. 785 * 786 * @return the <code>Dictionary</code> containing labels and 787 * where to draw them 788 */ 789 @SuppressWarnings("rawtypes") 790 public Dictionary getLabelTable() { 791 /* 792 if ( labelTable == null && getMajorTickSpacing() > 0 ) { 793 setLabelTable( createStandardLabels( getMajorTickSpacing() ) ); 794 } 795 */ 796 return labelTable; 797 } 798 799 800 /** 801 * Used to specify what label will be drawn at any given value. 802 * The key-value pairs are of this format: 803 * <code>{ Integer value, java.swing.JComponent label }</code>. 804 * <p> 805 * An easy way to generate a standard table of value labels is by using the 806 * {@code createStandardLabels} method. 807 * <p> 808 * Once the labels have been set, this method calls {@link #updateLabelUIs}. 809 * Note that the labels are only painted if the {@code paintLabels} 810 * property is {@code true}. 811 * 812 * @param labels new {@code Dictionary} of labels, or {@code null} to 813 * remove all labels 814 * @see #createStandardLabels(int) 815 * @see #getLabelTable 816 * @see #setPaintLabels 817 */ 818 @BeanProperty(hidden = true, visualUpdate = true, description 819 = "Specifies what labels will be drawn for any given value.") 820 @SuppressWarnings("rawtypes") 821 public void setLabelTable( Dictionary labels ) { 822 Dictionary oldTable = labelTable; 823 labelTable = labels; 824 updateLabelUIs(); 825 firePropertyChange("labelTable", oldTable, labelTable ); 826 if (labels != oldTable) { 827 revalidate(); 828 repaint(); 829 } 830 } 831 832 833 /** 834 * Updates the UIs for the labels in the label table by calling 835 * {@code updateUI} on each label. The UIs are updated from 836 * the current look and feel. The labels are also set to their 837 * preferred size. 838 * 839 * @see #setLabelTable 840 * @see JComponent#updateUI 841 */ 842 protected void updateLabelUIs() { 843 @SuppressWarnings("rawtypes") 844 Dictionary labelTable = getLabelTable(); 845 846 if (labelTable == null) { 847 return; 848 } 849 Enumeration<?> labels = labelTable.keys(); 850 while ( labels.hasMoreElements() ) { 851 JComponent component = (JComponent) labelTable.get(labels.nextElement()); 852 component.updateUI(); 853 component.setSize(component.getPreferredSize()); 854 } 855 } 856 857 private void updateLabelSizes() { 858 @SuppressWarnings("rawtypes") 859 Dictionary labelTable = getLabelTable(); 860 if (labelTable != null) { 861 Enumeration<?> labels = labelTable.elements(); 862 while (labels.hasMoreElements()) { 863 JComponent component = (JComponent) labels.nextElement(); 864 component.setSize(component.getPreferredSize()); 865 } 866 } 867 } 868 869 870 /** 871 * Creates a {@code Hashtable} of numerical text labels, starting at the 872 * slider minimum, and using the increment specified. 873 * For example, if you call <code>createStandardLabels( 10 )</code> 874 * and the slider minimum is zero, 875 * then labels will be created for the values 0, 10, 20, 30, and so on. 876 * <p> 877 * For the labels to be drawn on the slider, the returned {@code Hashtable} 878 * must be passed into {@code setLabelTable}, and {@code setPaintLabels} 879 * must be set to {@code true}. 880 * <p> 881 * For further details on the makeup of the returned {@code Hashtable}, see 882 * the {@code setLabelTable} documentation. 883 * 884 * @param increment distance between labels in the generated hashtable 885 * @return a new {@code Hashtable} of labels 886 * @see #setLabelTable 887 * @see #setPaintLabels 888 * @throws IllegalArgumentException if {@code increment} is less than or 889 * equal to zero 890 */ 891 public Hashtable<Integer, JComponent> createStandardLabels( int increment ) { 892 return createStandardLabels( increment, getMinimum() ); 893 } 894 895 896 /** 897 * Creates a {@code Hashtable} of numerical text labels, starting at the 898 * starting point specified, and using the increment specified. 899 * For example, if you call 900 * <code>createStandardLabels( 10, 2 )</code>, 901 * then labels will be created for the values 2, 12, 22, 32, and so on. 902 * <p> 903 * For the labels to be drawn on the slider, the returned {@code Hashtable} 904 * must be passed into {@code setLabelTable}, and {@code setPaintLabels} 905 * must be set to {@code true}. 906 * <p> 907 * For further details on the makeup of the returned {@code Hashtable}, see 908 * the {@code setLabelTable} documentation. 909 * 910 * @param increment distance between labels in the generated hashtable 911 * @param start value at which the labels will begin 912 * @return a new {@code Hashtable} of labels 913 * @see #setLabelTable 914 * @see #setPaintLabels 915 * @exception IllegalArgumentException if {@code start} is 916 * out of range, or if {@code increment} is less than or equal 917 * to zero 918 */ 919 public Hashtable<Integer, JComponent> createStandardLabels( int increment, int start ) { 920 if ( start > getMaximum() || start < getMinimum() ) { 921 throw new IllegalArgumentException( "Slider label start point out of range." ); 922 } 923 924 if ( increment <= 0 ) { 925 throw new IllegalArgumentException( "Label incremement must be > 0" ); 926 } 927 928 class SmartHashtable extends Hashtable<Integer, JComponent> implements PropertyChangeListener { 929 int increment = 0; 930 int start = 0; 931 boolean startAtMin = false; 932 933 class LabelUIResource extends JLabel implements UIResource { 934 public LabelUIResource( String text, int alignment ) { 935 super( text, alignment ); 936 setName("Slider.label"); 937 } 938 939 public Font getFont() { 940 Font font = super.getFont(); 941 if (font != null && !(font instanceof UIResource)) { 942 return font; 943 } 944 return JSlider.this.getFont(); 945 } 946 947 public Color getForeground() { 948 Color fg = super.getForeground(); 949 if (fg != null && !(fg instanceof UIResource)) { 950 return fg; 951 } 952 if (!(JSlider.this.getForeground() instanceof UIResource)) { 953 return JSlider.this.getForeground(); 954 } 955 return fg; 956 } 957 } 958 959 public SmartHashtable( int increment, int start ) { 960 super(); 961 this.increment = increment; 962 this.start = start; 963 startAtMin = start == getMinimum(); 964 createLabels(); 965 } 966 967 public void propertyChange( PropertyChangeEvent e ) { 968 if ( e.getPropertyName().equals( "minimum" ) && startAtMin ) { 969 start = getMinimum(); 970 } 971 972 if ( e.getPropertyName().equals( "minimum" ) || 973 e.getPropertyName().equals( "maximum" ) ) { 974 975 Enumeration<?> keys = getLabelTable().keys(); 976 Hashtable<Integer, JComponent> hashtable = new Hashtable<>(); 977 978 // Save the labels that were added by the developer 979 while ( keys.hasMoreElements() ) { 980 Integer key = (Integer) keys.nextElement(); 981 JComponent value = (JComponent) labelTable.get(key); 982 if ( !(value instanceof LabelUIResource) ) { 983 hashtable.put( key, value ); 984 } 985 } 986 987 clear(); 988 createLabels(); 989 990 // Add the saved labels 991 keys = hashtable.keys(); 992 while ( keys.hasMoreElements() ) { 993 Integer key = (Integer) keys.nextElement(); 994 put( key, hashtable.get( key ) ); 995 } 996 997 ((JSlider)e.getSource()).setLabelTable( this ); 998 } 999 } 1000 1001 void createLabels() { 1002 for ( int labelIndex = start; labelIndex <= getMaximum(); labelIndex += increment ) { 1003 put( Integer.valueOf( labelIndex ), new LabelUIResource( ""+labelIndex, JLabel.CENTER ) ); 1004 } 1005 } 1006 } 1007 1008 SmartHashtable table = new SmartHashtable( increment, start ); 1009 1010 @SuppressWarnings("rawtypes") 1011 Dictionary labelTable = getLabelTable(); 1012 1013 if (labelTable != null && (labelTable instanceof PropertyChangeListener)) { 1014 removePropertyChangeListener((PropertyChangeListener) labelTable); 1015 } 1016 1017 addPropertyChangeListener( table ); 1018 1019 return table; 1020 } 1021 1022 1023 /** 1024 * Returns true if the value-range shown for the slider is reversed, 1025 * 1026 * @return true if the slider values are reversed from their normal order 1027 * @see #setInverted 1028 */ 1029 public boolean getInverted() { 1030 return isInverted; 1031 } 1032 1033 1034 /** 1035 * Specify true to reverse the value-range shown for the slider and false to 1036 * put the value range in the normal order. The order depends on the 1037 * slider's <code>ComponentOrientation</code> property. Normal (non-inverted) 1038 * horizontal sliders with a <code>ComponentOrientation</code> value of 1039 * <code>LEFT_TO_RIGHT</code> have their maximum on the right. 1040 * Normal horizontal sliders with a <code>ComponentOrientation</code> value of 1041 * <code>RIGHT_TO_LEFT</code> have their maximum on the left. Normal vertical 1042 * sliders have their maximum on the top. These labels are reversed when the 1043 * slider is inverted. 1044 * <p> 1045 * By default, the value of this property is {@code false}. 1046 * 1047 * @param b true to reverse the slider values from their normal order 1048 */ 1049 @BeanProperty(visualUpdate = true, description 1050 = "If true reverses the slider values from their normal order") 1051 public void setInverted( boolean b ) { 1052 boolean oldValue = isInverted; 1053 isInverted = b; 1054 firePropertyChange("inverted", oldValue, isInverted); 1055 if (b != oldValue) { 1056 repaint(); 1057 } 1058 } 1059 1060 1061 /** 1062 * This method returns the major tick spacing. The number that is returned 1063 * represents the distance, measured in values, between each major tick mark. 1064 * If you have a slider with a range from 0 to 50 and the major tick spacing 1065 * is set to 10, you will get major ticks next to the following values: 1066 * 0, 10, 20, 30, 40, 50. 1067 * 1068 * @return the number of values between major ticks 1069 * @see #setMajorTickSpacing 1070 */ 1071 public int getMajorTickSpacing() { 1072 return majorTickSpacing; 1073 } 1074 1075 1076 /** 1077 * This method sets the major tick spacing. The number that is passed in 1078 * represents the distance, measured in values, between each major tick mark. 1079 * If you have a slider with a range from 0 to 50 and the major tick spacing 1080 * is set to 10, you will get major ticks next to the following values: 1081 * 0, 10, 20, 30, 40, 50. 1082 * <p> 1083 * In order for major ticks to be painted, {@code setPaintTicks} must be 1084 * set to {@code true}. 1085 * <p> 1086 * This method will also set up a label table for you. 1087 * If there is not already a label table, and the major tick spacing is 1088 * {@code > 0}, and {@code getPaintLabels} returns 1089 * {@code true}, a standard label table will be generated (by calling 1090 * {@code createStandardLabels}) with labels at the major tick marks. 1091 * For the example above, you would get text labels: "0", 1092 * "10", "20", "30", "40", "50". 1093 * The label table is then set on the slider by calling 1094 * {@code setLabelTable}. 1095 * 1096 * @param n new value for the {@code majorTickSpacing} property 1097 * @see #getMajorTickSpacing 1098 * @see #setPaintTicks 1099 * @see #setLabelTable 1100 * @see #createStandardLabels(int) 1101 */ 1102 @BeanProperty(visualUpdate = true, description 1103 = "Sets the number of values between major tick marks.") 1104 public void setMajorTickSpacing(int n) { 1105 int oldValue = majorTickSpacing; 1106 majorTickSpacing = n; 1107 if ( labelTable == null && getMajorTickSpacing() > 0 && getPaintLabels() ) { 1108 setLabelTable( createStandardLabels( getMajorTickSpacing() ) ); 1109 } 1110 firePropertyChange("majorTickSpacing", oldValue, majorTickSpacing); 1111 if (majorTickSpacing != oldValue && getPaintTicks()) { 1112 repaint(); 1113 } 1114 } 1115 1116 1117 1118 /** 1119 * This method returns the minor tick spacing. The number that is returned 1120 * represents the distance, measured in values, between each minor tick mark. 1121 * If you have a slider with a range from 0 to 50 and the minor tick spacing 1122 * is set to 10, you will get minor ticks next to the following values: 1123 * 0, 10, 20, 30, 40, 50. 1124 * 1125 * @return the number of values between minor ticks 1126 * @see #getMinorTickSpacing 1127 */ 1128 public int getMinorTickSpacing() { 1129 return minorTickSpacing; 1130 } 1131 1132 1133 /** 1134 * This method sets the minor tick spacing. The number that is passed in 1135 * represents the distance, measured in values, between each minor tick mark. 1136 * If you have a slider with a range from 0 to 50 and the minor tick spacing 1137 * is set to 10, you will get minor ticks next to the following values: 1138 * 0, 10, 20, 30, 40, 50. 1139 * <p> 1140 * In order for minor ticks to be painted, {@code setPaintTicks} must be 1141 * set to {@code true}. 1142 * 1143 * @param n new value for the {@code minorTickSpacing} property 1144 * @see #getMinorTickSpacing 1145 * @see #setPaintTicks 1146 */ 1147 @BeanProperty(visualUpdate = true, description 1148 = "Sets the number of values between minor tick marks.") 1149 public void setMinorTickSpacing(int n) { 1150 int oldValue = minorTickSpacing; 1151 minorTickSpacing = n; 1152 firePropertyChange("minorTickSpacing", oldValue, minorTickSpacing); 1153 if (minorTickSpacing != oldValue && getPaintTicks()) { 1154 repaint(); 1155 } 1156 } 1157 1158 1159 /** 1160 * Returns true if the knob (and the data value it represents) 1161 * resolve to the closest tick mark next to where the user 1162 * positioned the knob. 1163 * 1164 * @return true if the value snaps to the nearest tick mark, else false 1165 * @see #setSnapToTicks 1166 */ 1167 public boolean getSnapToTicks() { 1168 return snapToTicks; 1169 } 1170 1171 1172 /** 1173 * Returns true if the knob (and the data value it represents) 1174 * resolve to the closest slider value next to where the user 1175 * positioned the knob. 1176 * 1177 * @return true if the value snaps to the nearest slider value, else false 1178 * @see #setSnapToValue 1179 */ 1180 boolean getSnapToValue() { 1181 return snapToValue; 1182 } 1183 1184 1185 /** 1186 * Specifying true makes the knob (and the data value it represents) 1187 * resolve to the closest tick mark next to where the user 1188 * positioned the knob. 1189 * By default, this property is {@code false}. 1190 * 1191 * @param b true to snap the knob to the nearest tick mark 1192 * @see #getSnapToTicks 1193 */ 1194 @BeanProperty(description 1195 = "If true snap the knob to the nearest tick mark.") 1196 public void setSnapToTicks(boolean b) { 1197 boolean oldValue = snapToTicks; 1198 snapToTicks = b; 1199 firePropertyChange("snapToTicks", oldValue, snapToTicks); 1200 } 1201 1202 1203 /** 1204 * Specifying true makes the knob (and the data value it represents) 1205 * resolve to the closest slider value next to where the user 1206 * positioned the knob. If the {@code snapToTicks} property has also been 1207 * set to {@code true}, the snap-to-ticks behavior will prevail. 1208 * By default, the snapToValue property is {@code true}. 1209 * 1210 * @param b true to snap the knob to the nearest slider value 1211 * @see #getSnapToValue 1212 * @see #setSnapToTicks 1213 */ 1214 @BeanProperty(description 1215 = "If true snap the knob to the nearest slider value.") 1216 void setSnapToValue(boolean b) { 1217 boolean oldValue = snapToValue; 1218 snapToValue = b; 1219 firePropertyChange("snapToValue", oldValue, snapToValue); 1220 } 1221 1222 1223 /** 1224 * Tells if tick marks are to be painted. 1225 * @return true if tick marks are painted, else false 1226 * @see #setPaintTicks 1227 */ 1228 public boolean getPaintTicks() { 1229 return paintTicks; 1230 } 1231 1232 1233 /** 1234 * Determines whether tick marks are painted on the slider. 1235 * By default, this property is {@code false}. 1236 * 1237 * @param b whether or not tick marks should be painted 1238 * @see #getPaintTicks 1239 */ 1240 @BeanProperty(visualUpdate = true, description 1241 = "If true tick marks are painted on the slider.") 1242 public void setPaintTicks(boolean b) { 1243 boolean oldValue = paintTicks; 1244 paintTicks = b; 1245 firePropertyChange("paintTicks", oldValue, paintTicks); 1246 if (paintTicks != oldValue) { 1247 revalidate(); 1248 repaint(); 1249 } 1250 } 1251 1252 /** 1253 * Tells if the track (area the slider slides in) is to be painted. 1254 * @return true if track is painted, else false 1255 * @see #setPaintTrack 1256 */ 1257 public boolean getPaintTrack() { 1258 return paintTrack; 1259 } 1260 1261 /** 1262 * Determines whether the track is painted on the slider. By default, this 1263 * property is {@code true}. It is up to the look and feel to honor this 1264 * property, some may choose to ignore it. 1265 * 1266 * @param b whether or not to paint the slider track 1267 * @see #getPaintTrack 1268 */ 1269 @BeanProperty(visualUpdate = true, description 1270 = "If true, the track is painted on the slider.") 1271 public void setPaintTrack(boolean b) { 1272 boolean oldValue = paintTrack; 1273 paintTrack = b; 1274 firePropertyChange("paintTrack", oldValue, paintTrack); 1275 if (paintTrack != oldValue) { 1276 repaint(); 1277 } 1278 } 1279 1280 1281 /** 1282 * Tells if labels are to be painted. 1283 * @return true if labels are painted, else false 1284 * @see #setPaintLabels 1285 */ 1286 public boolean getPaintLabels() { 1287 return paintLabels; 1288 } 1289 1290 1291 /** 1292 * Determines whether labels are painted on the slider. 1293 * <p> 1294 * This method will also set up a label table for you. 1295 * If there is not already a label table, and the major tick spacing is 1296 * {@code > 0}, 1297 * a standard label table will be generated (by calling 1298 * {@code createStandardLabels}) with labels at the major tick marks. 1299 * The label table is then set on the slider by calling 1300 * {@code setLabelTable}. 1301 * <p> 1302 * By default, this property is {@code false}. 1303 * 1304 * @param b whether or not to paint labels 1305 * @see #getPaintLabels 1306 * @see #getLabelTable 1307 * @see #createStandardLabels(int) 1308 */ 1309 @BeanProperty(visualUpdate = true, description 1310 = "If true labels are painted on the slider.") 1311 public void setPaintLabels(boolean b) { 1312 boolean oldValue = paintLabels; 1313 paintLabels = b; 1314 if ( labelTable == null && getMajorTickSpacing() > 0 ) { 1315 setLabelTable( createStandardLabels( getMajorTickSpacing() ) ); 1316 } 1317 firePropertyChange("paintLabels", oldValue, paintLabels); 1318 if (paintLabels != oldValue) { 1319 revalidate(); 1320 repaint(); 1321 } 1322 } 1323 1324 1325 /** 1326 * See readObject() and writeObject() in JComponent for more 1327 * information about serialization in Swing. 1328 */ 1329 private void writeObject(ObjectOutputStream s) throws IOException { 1330 s.defaultWriteObject(); 1331 if (getUIClassID().equals(uiClassID)) { 1332 byte count = JComponent.getWriteObjCounter(this); 1333 JComponent.setWriteObjCounter(this, --count); 1334 if (count == 0 && ui != null) { 1335 ui.installUI(this); 1336 } 1337 } 1338 } 1339 1340 1341 /** 1342 * Returns a string representation of this JSlider. This method 1343 * is intended to be used only for debugging purposes, and the 1344 * content and format of the returned string may vary between 1345 * implementations. The returned string may be empty but may not 1346 * be <code>null</code>. 1347 * 1348 * @return a string representation of this JSlider. 1349 */ 1350 protected String paramString() { 1351 String paintTicksString = (paintTicks ? 1352 "true" : "false"); 1353 String paintTrackString = (paintTrack ? 1354 "true" : "false"); 1355 String paintLabelsString = (paintLabels ? 1356 "true" : "false"); 1357 String isInvertedString = (isInverted ? 1358 "true" : "false"); 1359 String snapToTicksString = (snapToTicks ? 1360 "true" : "false"); 1361 String snapToValueString = (snapToValue ? 1362 "true" : "false"); 1363 String orientationString = (orientation == HORIZONTAL ? 1364 "HORIZONTAL" : "VERTICAL"); 1365 1366 return super.paramString() + 1367 ",isInverted=" + isInvertedString + 1368 ",majorTickSpacing=" + majorTickSpacing + 1369 ",minorTickSpacing=" + minorTickSpacing + 1370 ",orientation=" + orientationString + 1371 ",paintLabels=" + paintLabelsString + 1372 ",paintTicks=" + paintTicksString + 1373 ",paintTrack=" + paintTrackString + 1374 ",snapToTicks=" + snapToTicksString + 1375 ",snapToValue=" + snapToValueString; 1376 } 1377 1378 1379 ///////////////// 1380 // Accessibility support 1381 //////////////// 1382 1383 /** 1384 * Gets the AccessibleContext associated with this JSlider. 1385 * For sliders, the AccessibleContext takes the form of an 1386 * AccessibleJSlider. 1387 * A new AccessibleJSlider instance is created if necessary. 1388 * 1389 * @return an AccessibleJSlider that serves as the 1390 * AccessibleContext of this JSlider 1391 */ 1392 @BeanProperty(bound = false) 1393 public AccessibleContext getAccessibleContext() { 1394 if (accessibleContext == null) { 1395 accessibleContext = new AccessibleJSlider(); 1396 } 1397 return accessibleContext; 1398 } 1399 1400 /** 1401 * This class implements accessibility support for the 1402 * <code>JSlider</code> class. It provides an implementation of the 1403 * Java Accessibility API appropriate to slider user-interface elements. 1404 * <p> 1405 * <strong>Warning:</strong> 1406 * Serialized objects of this class will not be compatible with 1407 * future Swing releases. The current serialization support is 1408 * appropriate for short term storage or RMI between applications running 1409 * the same version of Swing. As of 1.4, support for long term storage 1410 * of all JavaBeans 1411 * has been added to the <code>java.beans</code> package. 1412 * Please see {@link java.beans.XMLEncoder}. 1413 */ 1414 @SuppressWarnings("serial") // Same-version serialization only 1415 protected class AccessibleJSlider extends AccessibleJComponent 1416 implements AccessibleValue { 1417 1418 /** 1419 * Get the state set of this object. 1420 * 1421 * @return an instance of AccessibleState containing the current state 1422 * of the object 1423 * @see AccessibleState 1424 */ 1425 public AccessibleStateSet getAccessibleStateSet() { 1426 AccessibleStateSet states = super.getAccessibleStateSet(); 1427 if (getValueIsAdjusting()) { 1428 states.add(AccessibleState.BUSY); 1429 } 1430 if (getOrientation() == VERTICAL) { 1431 states.add(AccessibleState.VERTICAL); 1432 } 1433 else { 1434 states.add(AccessibleState.HORIZONTAL); 1435 } 1436 return states; 1437 } 1438 1439 /** 1440 * Get the role of this object. 1441 * 1442 * @return an instance of AccessibleRole describing the role of the object 1443 */ 1444 public AccessibleRole getAccessibleRole() { 1445 return AccessibleRole.SLIDER; 1446 } 1447 1448 /** 1449 * Get the AccessibleValue associated with this object. In the 1450 * implementation of the Java Accessibility API for this class, 1451 * return this object, which is responsible for implementing the 1452 * AccessibleValue interface on behalf of itself. 1453 * 1454 * @return this object 1455 */ 1456 public AccessibleValue getAccessibleValue() { 1457 return this; 1458 } 1459 1460 /** 1461 * Get the accessible value of this object. 1462 * 1463 * @return The current value of this object. 1464 */ 1465 public Number getCurrentAccessibleValue() { 1466 return Integer.valueOf(getValue()); 1467 } 1468 1469 /** 1470 * Set the value of this object as a Number. 1471 * 1472 * @return True if the value was set. 1473 */ 1474 public boolean setCurrentAccessibleValue(Number n) { 1475 // TIGER - 4422535 1476 if (n == null) { 1477 return false; 1478 } 1479 setValue(n.intValue()); 1480 return true; 1481 } 1482 1483 /** 1484 * Get the minimum accessible value of this object. 1485 * 1486 * @return The minimum value of this object. 1487 */ 1488 public Number getMinimumAccessibleValue() { 1489 return Integer.valueOf(getMinimum()); 1490 } 1491 1492 /** 1493 * Get the maximum accessible value of this object. 1494 * 1495 * @return The maximum value of this object. 1496 */ 1497 public Number getMaximumAccessibleValue() { 1498 // TIGER - 4422362 1499 BoundedRangeModel model = JSlider.this.getModel(); 1500 return Integer.valueOf(model.getMaximum() - model.getExtent()); 1501 } 1502 } // AccessibleJSlider 1503 }