1 /* 2 * Copyright (c) 1997, 2018, 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 26 package javax.swing.plaf.basic; 27 28 import java.awt.event.*; 29 import java.awt.*; 30 import java.beans.*; 31 import java.util.Dictionary; 32 import java.util.Enumeration; 33 34 import javax.swing.*; 35 import javax.swing.event.*; 36 import javax.swing.plaf.*; 37 import sun.swing.DefaultLookup; 38 import sun.swing.SwingUtilities2; 39 import sun.swing.UIAction; 40 41 42 /** 43 * A Basic L&F implementation of SliderUI. 44 * 45 * @author Tom Santos 46 */ 47 public class BasicSliderUI extends SliderUI{ 48 // Old actions forward to an instance of this. 49 private static final Actions SHARED_ACTION = new Actions(); 50 51 /** Positive scroll */ 52 public static final int POSITIVE_SCROLL = +1; 53 /** Negative scroll */ 54 public static final int NEGATIVE_SCROLL = -1; 55 /** Minimum scroll */ 56 public static final int MIN_SCROLL = -2; 57 /** Maximum scroll */ 58 public static final int MAX_SCROLL = +2; 59 60 /** Scroll timer */ 61 protected Timer scrollTimer; 62 /** Slider */ 63 protected JSlider slider; 64 65 /** Focus insets */ 66 protected Insets focusInsets = null; 67 /** Inset cache */ 68 protected Insets insetCache = null; 69 /** Left-to-right cache */ 70 protected boolean leftToRightCache = true; 71 /** Focus rectangle */ 72 protected Rectangle focusRect = null; 73 /** Content rectangle */ 74 protected Rectangle contentRect = null; 75 /** Label rectangle */ 76 protected Rectangle labelRect = null; 77 /** Tick rectangle */ 78 protected Rectangle tickRect = null; 79 /** Track rectangle */ 80 protected Rectangle trackRect = null; 81 /** Thumb rectangle */ 82 protected Rectangle thumbRect = null; 83 84 /** The distance that the track is from the side of the control */ 85 protected int trackBuffer = 0; 86 87 private transient boolean isDragging; 88 89 /** Track listener */ 90 protected TrackListener trackListener; 91 /** Change listener */ 92 protected ChangeListener changeListener; 93 /** Component listener */ 94 protected ComponentListener componentListener; 95 /** Focus listener */ 96 protected FocusListener focusListener; 97 /** Scroll listener */ 98 protected ScrollListener scrollListener; 99 /** Property chane listener */ 100 protected PropertyChangeListener propertyChangeListener; 101 private Handler handler; 102 private int lastValue; 103 104 // Colors 105 private Color shadowColor; 106 private Color highlightColor; 107 private Color focusColor; 108 109 /** 110 * Whther or not sameLabelBaselines is up to date. 111 */ 112 private boolean checkedLabelBaselines; 113 /** 114 * Whether or not all the entries in the labeltable have the same 115 * baseline. 116 */ 117 private boolean sameLabelBaselines; 118 119 /** 120 * Returns the shadow color. 121 * @return the shadow color 122 */ getShadowColor()123 protected Color getShadowColor() { 124 return shadowColor; 125 } 126 127 /** 128 * Returns the highlight color. 129 * @return the highlight color 130 */ getHighlightColor()131 protected Color getHighlightColor() { 132 return highlightColor; 133 } 134 135 /** 136 * Returns the focus color. 137 * @return the focus color 138 */ getFocusColor()139 protected Color getFocusColor() { 140 return focusColor; 141 } 142 143 /** 144 * Returns true if the user is dragging the slider. 145 * 146 * @return true if the user is dragging the slider 147 * @since 1.5 148 */ isDragging()149 protected boolean isDragging() { 150 return isDragging; 151 } 152 153 ///////////////////////////////////////////////////////////////////////////// 154 // ComponentUI Interface Implementation methods 155 ///////////////////////////////////////////////////////////////////////////// 156 /** 157 * Creates a UI. 158 * @param b a component 159 * @return a UI 160 */ createUI(JComponent b)161 public static ComponentUI createUI(JComponent b) { 162 return new BasicSliderUI((JSlider)b); 163 } 164 165 /** 166 * Constructs a {@code BasicSliderUI}. 167 * @param b a slider 168 */ BasicSliderUI(JSlider b)169 public BasicSliderUI(JSlider b) { 170 } 171 172 /** 173 * Installs a UI. 174 * @param c a component 175 */ installUI(JComponent c)176 public void installUI(JComponent c) { 177 slider = (JSlider) c; 178 179 checkedLabelBaselines = false; 180 181 slider.setEnabled(slider.isEnabled()); 182 LookAndFeel.installProperty(slider, "opaque", Boolean.TRUE); 183 184 isDragging = false; 185 trackListener = createTrackListener( slider ); 186 changeListener = createChangeListener( slider ); 187 componentListener = createComponentListener( slider ); 188 focusListener = createFocusListener( slider ); 189 scrollListener = createScrollListener( slider ); 190 propertyChangeListener = createPropertyChangeListener( slider ); 191 192 installDefaults( slider ); 193 installListeners( slider ); 194 installKeyboardActions( slider ); 195 196 scrollTimer = new Timer( 100, scrollListener ); 197 scrollTimer.setInitialDelay( 300 ); 198 199 insetCache = slider.getInsets(); 200 leftToRightCache = BasicGraphicsUtils.isLeftToRight(slider); 201 focusRect = new Rectangle(); 202 contentRect = new Rectangle(); 203 labelRect = new Rectangle(); 204 tickRect = new Rectangle(); 205 trackRect = new Rectangle(); 206 thumbRect = new Rectangle(); 207 lastValue = slider.getValue(); 208 209 calculateGeometry(); // This figures out where the labels, ticks, track, and thumb are. 210 } 211 212 /** 213 * Uninstalls a UI. 214 * @param c a component 215 */ uninstallUI(JComponent c)216 public void uninstallUI(JComponent c) { 217 if ( c != slider ) 218 throw new IllegalComponentStateException( 219 this + " was asked to deinstall() " 220 + c + " when it only knows about " 221 + slider + "."); 222 223 scrollTimer.stop(); 224 scrollTimer = null; 225 226 uninstallDefaults(slider); 227 uninstallListeners( slider ); 228 uninstallKeyboardActions(slider); 229 230 insetCache = null; 231 leftToRightCache = true; 232 focusRect = null; 233 contentRect = null; 234 labelRect = null; 235 tickRect = null; 236 trackRect = null; 237 thumbRect = null; 238 trackListener = null; 239 changeListener = null; 240 componentListener = null; 241 focusListener = null; 242 scrollListener = null; 243 propertyChangeListener = null; 244 slider = null; 245 } 246 247 /** 248 * Installs the defaults. 249 * @param slider a slider 250 */ installDefaults( JSlider slider )251 protected void installDefaults( JSlider slider ) { 252 LookAndFeel.installBorder(slider, "Slider.border"); 253 LookAndFeel.installColorsAndFont(slider, "Slider.background", 254 "Slider.foreground", "Slider.font"); 255 highlightColor = UIManager.getColor("Slider.highlight"); 256 257 shadowColor = UIManager.getColor("Slider.shadow"); 258 focusColor = UIManager.getColor("Slider.focus"); 259 260 focusInsets = (Insets)UIManager.get( "Slider.focusInsets" ); 261 // use default if missing so that BasicSliderUI can be used in other 262 // LAFs like Nimbus 263 if (focusInsets == null) focusInsets = new InsetsUIResource(2,2,2,2); 264 } 265 266 /** 267 * Uninstalls the defaults. 268 * @param slider a slider 269 */ uninstallDefaults(JSlider slider)270 protected void uninstallDefaults(JSlider slider) { 271 LookAndFeel.uninstallBorder(slider); 272 273 focusInsets = null; 274 } 275 276 /** 277 * Creates a track listener. 278 * @return a track listener 279 * @param slider a slider 280 */ createTrackListener(JSlider slider)281 protected TrackListener createTrackListener(JSlider slider) { 282 return new TrackListener(); 283 } 284 285 /** 286 * Creates a change listener. 287 * @return a change listener 288 * @param slider a slider 289 */ createChangeListener(JSlider slider)290 protected ChangeListener createChangeListener(JSlider slider) { 291 return getHandler(); 292 } 293 294 /** 295 * Creates a composite listener. 296 * @return a composite listener 297 * @param slider a slider 298 */ createComponentListener(JSlider slider)299 protected ComponentListener createComponentListener(JSlider slider) { 300 return getHandler(); 301 } 302 303 /** 304 * Creates a focus listener. 305 * @return a focus listener 306 * @param slider a slider 307 */ createFocusListener(JSlider slider)308 protected FocusListener createFocusListener(JSlider slider) { 309 return getHandler(); 310 } 311 312 /** 313 * Creates a scroll listener. 314 * @return a scroll listener 315 * @param slider a slider 316 */ createScrollListener( JSlider slider )317 protected ScrollListener createScrollListener( JSlider slider ) { 318 return new ScrollListener(); 319 } 320 321 /** 322 * Creates a property change listener. 323 * @return a property change listener 324 * @param slider a slider 325 */ createPropertyChangeListener( JSlider slider)326 protected PropertyChangeListener createPropertyChangeListener( 327 JSlider slider) { 328 return getHandler(); 329 } 330 getHandler()331 private Handler getHandler() { 332 if (handler == null) { 333 handler = new Handler(); 334 } 335 return handler; 336 } 337 338 /** 339 * Installs listeners. 340 * @param slider a slider 341 */ installListeners( JSlider slider )342 protected void installListeners( JSlider slider ) { 343 slider.addMouseListener(trackListener); 344 slider.addMouseMotionListener(trackListener); 345 slider.addFocusListener(focusListener); 346 slider.addComponentListener(componentListener); 347 slider.addPropertyChangeListener( propertyChangeListener ); 348 slider.getModel().addChangeListener(changeListener); 349 } 350 351 /** 352 * Uninstalls listeners. 353 * @param slider a slider 354 */ uninstallListeners( JSlider slider )355 protected void uninstallListeners( JSlider slider ) { 356 slider.removeMouseListener(trackListener); 357 slider.removeMouseMotionListener(trackListener); 358 slider.removeFocusListener(focusListener); 359 slider.removeComponentListener(componentListener); 360 slider.removePropertyChangeListener( propertyChangeListener ); 361 slider.getModel().removeChangeListener(changeListener); 362 handler = null; 363 } 364 365 /** 366 * Installs keyboard actions. 367 * @param slider a slider 368 */ installKeyboardActions( JSlider slider )369 protected void installKeyboardActions( JSlider slider ) { 370 InputMap km = getInputMap(JComponent.WHEN_FOCUSED, slider); 371 SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, km); 372 LazyActionMap.installLazyActionMap(slider, BasicSliderUI.class, 373 "Slider.actionMap"); 374 } 375 getInputMap(int condition, JSlider slider)376 InputMap getInputMap(int condition, JSlider slider) { 377 if (condition == JComponent.WHEN_FOCUSED) { 378 InputMap keyMap = (InputMap)DefaultLookup.get(slider, this, 379 "Slider.focusInputMap"); 380 InputMap rtlKeyMap; 381 382 if (slider.getComponentOrientation().isLeftToRight() || 383 ((rtlKeyMap = (InputMap)DefaultLookup.get(slider, this, 384 "Slider.focusInputMap.RightToLeft")) == null)) { 385 return keyMap; 386 } else { 387 rtlKeyMap.setParent(keyMap); 388 return rtlKeyMap; 389 } 390 } 391 return null; 392 } 393 394 /** 395 * Populates ComboBox's actions. 396 */ loadActionMap(LazyActionMap map)397 static void loadActionMap(LazyActionMap map) { 398 map.put(new Actions(Actions.POSITIVE_UNIT_INCREMENT)); 399 map.put(new Actions(Actions.POSITIVE_BLOCK_INCREMENT)); 400 map.put(new Actions(Actions.NEGATIVE_UNIT_INCREMENT)); 401 map.put(new Actions(Actions.NEGATIVE_BLOCK_INCREMENT)); 402 map.put(new Actions(Actions.MIN_SCROLL_INCREMENT)); 403 map.put(new Actions(Actions.MAX_SCROLL_INCREMENT)); 404 } 405 406 /** 407 * Uninstalls keyboard actions. 408 * @param slider a slider 409 */ uninstallKeyboardActions( JSlider slider )410 protected void uninstallKeyboardActions( JSlider slider ) { 411 SwingUtilities.replaceUIActionMap(slider, null); 412 SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, 413 null); 414 } 415 416 417 /** 418 * Returns the baseline. 419 * 420 * @throws NullPointerException {@inheritDoc} 421 * @throws IllegalArgumentException {@inheritDoc} 422 * @see javax.swing.JComponent#getBaseline(int, int) 423 * @since 1.6 424 */ getBaseline(JComponent c, int width, int height)425 public int getBaseline(JComponent c, int width, int height) { 426 super.getBaseline(c, width, height); 427 if (slider.getPaintLabels() && labelsHaveSameBaselines()) { 428 FontMetrics metrics = slider.getFontMetrics(slider.getFont()); 429 Insets insets = slider.getInsets(); 430 Dimension thumbSize = getThumbSize(); 431 if (slider.getOrientation() == JSlider.HORIZONTAL) { 432 int tickLength = getTickLength(); 433 int contentHeight = height - insets.top - insets.bottom - 434 focusInsets.top - focusInsets.bottom; 435 int thumbHeight = thumbSize.height; 436 int centerSpacing = thumbHeight; 437 if (slider.getPaintTicks()) { 438 centerSpacing += tickLength; 439 } 440 // Assume uniform labels. 441 centerSpacing += getHeightOfTallestLabel(); 442 int trackY = insets.top + focusInsets.top + 443 (contentHeight - centerSpacing - 1) / 2; 444 int trackHeight = thumbHeight; 445 int tickY = trackY + trackHeight; 446 int tickHeight = tickLength; 447 if (!slider.getPaintTicks()) { 448 tickHeight = 0; 449 } 450 int labelY = tickY + tickHeight; 451 return labelY + metrics.getAscent(); 452 } 453 else { // vertical 454 boolean inverted = slider.getInverted(); 455 Integer value = inverted ? getLowestValue() : 456 getHighestValue(); 457 if (value != null) { 458 int thumbHeight = thumbSize.height; 459 int trackBuffer = Math.max(metrics.getHeight() / 2, 460 thumbHeight / 2); 461 int contentY = focusInsets.top + insets.top; 462 int trackY = contentY + trackBuffer; 463 int trackHeight = height - focusInsets.top - 464 focusInsets.bottom - insets.top - insets.bottom - 465 trackBuffer - trackBuffer; 466 int yPosition = yPositionForValue(value, trackY, 467 trackHeight); 468 return yPosition - metrics.getHeight() / 2 + 469 metrics.getAscent(); 470 } 471 } 472 } 473 return 0; 474 } 475 476 /** 477 * Returns an enum indicating how the baseline of the component 478 * changes as the size changes. 479 * 480 * @throws NullPointerException {@inheritDoc} 481 * @see javax.swing.JComponent#getBaseline(int, int) 482 * @since 1.6 483 */ getBaselineResizeBehavior( JComponent c)484 public Component.BaselineResizeBehavior getBaselineResizeBehavior( 485 JComponent c) { 486 super.getBaselineResizeBehavior(c); 487 // NOTE: BasicSpinner really provides for CENTER_OFFSET, but 488 // the default min/pref size is smaller than it should be 489 // so that getBaseline() doesn't implement the contract 490 // for CENTER_OFFSET as defined in Component. 491 return Component.BaselineResizeBehavior.OTHER; 492 } 493 494 /** 495 * Returns true if all the labels from the label table have the same 496 * baseline. 497 * 498 * @return true if all the labels from the label table have the 499 * same baseline 500 * @since 1.6 501 */ labelsHaveSameBaselines()502 protected boolean labelsHaveSameBaselines() { 503 if (!checkedLabelBaselines) { 504 checkedLabelBaselines = true; 505 @SuppressWarnings("rawtypes") 506 Dictionary dictionary = slider.getLabelTable(); 507 if (dictionary != null) { 508 sameLabelBaselines = true; 509 Enumeration<?> elements = dictionary.elements(); 510 int baseline = -1; 511 while (elements.hasMoreElements()) { 512 JComponent label = (JComponent) elements.nextElement(); 513 Dimension pref = label.getPreferredSize(); 514 int labelBaseline = label.getBaseline(pref.width, 515 pref.height); 516 if (labelBaseline >= 0) { 517 if (baseline == -1) { 518 baseline = labelBaseline; 519 } 520 else if (baseline != labelBaseline) { 521 sameLabelBaselines = false; 522 break; 523 } 524 } 525 else { 526 sameLabelBaselines = false; 527 break; 528 } 529 } 530 } 531 else { 532 sameLabelBaselines = false; 533 } 534 } 535 return sameLabelBaselines; 536 } 537 538 /** 539 * Returns the preferred horizontal size. 540 * @return the preferred horizontal size 541 */ getPreferredHorizontalSize()542 public Dimension getPreferredHorizontalSize() { 543 Dimension horizDim = (Dimension)DefaultLookup.get(slider, 544 this, "Slider.horizontalSize"); 545 if (horizDim == null) { 546 horizDim = new Dimension(200, 21); 547 } 548 return horizDim; 549 } 550 551 /** 552 * Returns the preferred vertical size. 553 * @return the preferred vertical size 554 */ getPreferredVerticalSize()555 public Dimension getPreferredVerticalSize() { 556 Dimension vertDim = (Dimension)DefaultLookup.get(slider, 557 this, "Slider.verticalSize"); 558 if (vertDim == null) { 559 vertDim = new Dimension(21, 200); 560 } 561 return vertDim; 562 } 563 564 /** 565 * Returns the minimum horizontal size. 566 * @return the minimum horizontal size 567 */ getMinimumHorizontalSize()568 public Dimension getMinimumHorizontalSize() { 569 Dimension minHorizDim = (Dimension)DefaultLookup.get(slider, 570 this, "Slider.minimumHorizontalSize"); 571 if (minHorizDim == null) { 572 minHorizDim = new Dimension(36, 21); 573 } 574 return minHorizDim; 575 } 576 577 /** 578 * Returns the minimum vertical size. 579 * @return the minimum vertical size 580 */ getMinimumVerticalSize()581 public Dimension getMinimumVerticalSize() { 582 Dimension minVertDim = (Dimension)DefaultLookup.get(slider, 583 this, "Slider.minimumVerticalSize"); 584 if (minVertDim == null) { 585 minVertDim = new Dimension(21, 36); 586 } 587 return minVertDim; 588 } 589 590 /** 591 * Returns the preferred size. 592 * @param c a component 593 * @return the preferred size 594 */ getPreferredSize(JComponent c)595 public Dimension getPreferredSize(JComponent c) { 596 recalculateIfInsetsChanged(); 597 Dimension d; 598 if ( slider.getOrientation() == JSlider.VERTICAL ) { 599 d = new Dimension(getPreferredVerticalSize()); 600 d.width = insetCache.left + insetCache.right; 601 d.width += focusInsets.left + focusInsets.right; 602 d.width += trackRect.width + tickRect.width + labelRect.width; 603 } 604 else { 605 d = new Dimension(getPreferredHorizontalSize()); 606 d.height = insetCache.top + insetCache.bottom; 607 d.height += focusInsets.top + focusInsets.bottom; 608 d.height += trackRect.height + tickRect.height + labelRect.height; 609 } 610 611 return d; 612 } 613 614 /** 615 * Returns the minimum size. 616 * @param c a component 617 * @return the minimum size 618 */ getMinimumSize(JComponent c)619 public Dimension getMinimumSize(JComponent c) { 620 recalculateIfInsetsChanged(); 621 Dimension d; 622 623 if ( slider.getOrientation() == JSlider.VERTICAL ) { 624 d = new Dimension(getMinimumVerticalSize()); 625 d.width = insetCache.left + insetCache.right; 626 d.width += focusInsets.left + focusInsets.right; 627 d.width += trackRect.width + tickRect.width + labelRect.width; 628 } 629 else { 630 d = new Dimension(getMinimumHorizontalSize()); 631 d.height = insetCache.top + insetCache.bottom; 632 d.height += focusInsets.top + focusInsets.bottom; 633 d.height += trackRect.height + tickRect.height + labelRect.height; 634 } 635 636 return d; 637 } 638 639 /** 640 * Returns the maximum size. 641 * @param c a component 642 * @return the maximum size 643 */ getMaximumSize(JComponent c)644 public Dimension getMaximumSize(JComponent c) { 645 Dimension d = getPreferredSize(c); 646 if ( slider.getOrientation() == JSlider.VERTICAL ) { 647 d.height = Short.MAX_VALUE; 648 } 649 else { 650 d.width = Short.MAX_VALUE; 651 } 652 653 return d; 654 } 655 656 /** 657 * Calculates the geometry. 658 */ calculateGeometry()659 protected void calculateGeometry() { 660 calculateFocusRect(); 661 calculateContentRect(); 662 calculateThumbSize(); 663 calculateTrackBuffer(); 664 calculateTrackRect(); 665 calculateTickRect(); 666 calculateLabelRect(); 667 calculateThumbLocation(); 668 } 669 670 /** 671 * Calculates the focus rectangle. 672 */ calculateFocusRect()673 protected void calculateFocusRect() { 674 focusRect.x = insetCache.left; 675 focusRect.y = insetCache.top; 676 focusRect.width = slider.getWidth() - (insetCache.left + insetCache.right); 677 focusRect.height = slider.getHeight() - (insetCache.top + insetCache.bottom); 678 } 679 680 /** 681 * Calculates the thumb size rectangle. 682 */ calculateThumbSize()683 protected void calculateThumbSize() { 684 Dimension size = getThumbSize(); 685 thumbRect.setSize( size.width, size.height ); 686 } 687 688 /** 689 * Calculates the content rectangle. 690 */ calculateContentRect()691 protected void calculateContentRect() { 692 contentRect.x = focusRect.x + focusInsets.left; 693 contentRect.y = focusRect.y + focusInsets.top; 694 contentRect.width = focusRect.width - (focusInsets.left + focusInsets.right); 695 contentRect.height = focusRect.height - (focusInsets.top + focusInsets.bottom); 696 } 697 getTickSpacing()698 private int getTickSpacing() { 699 int majorTickSpacing = slider.getMajorTickSpacing(); 700 int minorTickSpacing = slider.getMinorTickSpacing(); 701 702 int result; 703 704 if (minorTickSpacing > 0) { 705 result = minorTickSpacing; 706 } else if (majorTickSpacing > 0) { 707 result = majorTickSpacing; 708 } else { 709 result = 0; 710 } 711 712 return result; 713 } 714 715 /** 716 * Calculates the thumb location. 717 */ calculateThumbLocation()718 protected void calculateThumbLocation() { 719 if ( slider.getSnapToTicks() ) { 720 int sliderValue = slider.getValue(); 721 int snappedValue = sliderValue; 722 int tickSpacing = getTickSpacing(); 723 724 if ( tickSpacing != 0 ) { 725 // If it's not on a tick, change the value 726 if ( (sliderValue - slider.getMinimum()) % tickSpacing != 0 ) { 727 float temp = (float)(sliderValue - slider.getMinimum()) / (float)tickSpacing; 728 int whichTick = Math.round( temp ); 729 730 // This is the fix for the bug #6401380 731 if (temp - (int)temp == .5 && sliderValue < lastValue) { 732 whichTick --; 733 } 734 snappedValue = slider.getMinimum() + (whichTick * tickSpacing); 735 } 736 737 if( snappedValue != sliderValue ) { 738 slider.setValue( snappedValue ); 739 } 740 } 741 } 742 743 if ( slider.getOrientation() == JSlider.HORIZONTAL ) { 744 int valuePosition = xPositionForValue(slider.getValue()); 745 746 thumbRect.x = valuePosition - (thumbRect.width / 2); 747 thumbRect.y = trackRect.y; 748 } 749 else { 750 int valuePosition = yPositionForValue(slider.getValue()); 751 752 thumbRect.x = trackRect.x; 753 thumbRect.y = valuePosition - (thumbRect.height / 2); 754 } 755 } 756 757 /** 758 * Calculates the track buffer. 759 */ calculateTrackBuffer()760 protected void calculateTrackBuffer() { 761 if ( slider.getPaintLabels() && slider.getLabelTable() != null ) { 762 Component highLabel = getHighestValueLabel(); 763 Component lowLabel = getLowestValueLabel(); 764 765 if ( slider.getOrientation() == JSlider.HORIZONTAL ) { 766 trackBuffer = Math.max( highLabel.getBounds().width, lowLabel.getBounds().width ) / 2; 767 trackBuffer = Math.max( trackBuffer, thumbRect.width / 2 ); 768 } 769 else { 770 trackBuffer = Math.max( highLabel.getBounds().height, lowLabel.getBounds().height ) / 2; 771 trackBuffer = Math.max( trackBuffer, thumbRect.height / 2 ); 772 } 773 } 774 else { 775 if ( slider.getOrientation() == JSlider.HORIZONTAL ) { 776 trackBuffer = thumbRect.width / 2; 777 } 778 else { 779 trackBuffer = thumbRect.height / 2; 780 } 781 } 782 } 783 784 /** 785 * Calculates the track rectangle. 786 */ calculateTrackRect()787 protected void calculateTrackRect() { 788 int centerSpacing; // used to center sliders added using BorderLayout.CENTER (bug 4275631) 789 if ( slider.getOrientation() == JSlider.HORIZONTAL ) { 790 centerSpacing = thumbRect.height; 791 if ( slider.getPaintTicks() ) centerSpacing += getTickLength(); 792 if ( slider.getPaintLabels() ) centerSpacing += getHeightOfTallestLabel(); 793 trackRect.x = contentRect.x + trackBuffer; 794 trackRect.y = contentRect.y + (contentRect.height - centerSpacing - 1)/2; 795 trackRect.width = contentRect.width - (trackBuffer * 2); 796 trackRect.height = thumbRect.height; 797 } 798 else { 799 centerSpacing = thumbRect.width; 800 if (BasicGraphicsUtils.isLeftToRight(slider)) { 801 if ( slider.getPaintTicks() ) centerSpacing += getTickLength(); 802 if ( slider.getPaintLabels() ) centerSpacing += getWidthOfWidestLabel(); 803 } else { 804 if ( slider.getPaintTicks() ) centerSpacing -= getTickLength(); 805 if ( slider.getPaintLabels() ) centerSpacing -= getWidthOfWidestLabel(); 806 } 807 trackRect.x = contentRect.x + (contentRect.width - centerSpacing - 1)/2; 808 trackRect.y = contentRect.y + trackBuffer; 809 trackRect.width = thumbRect.width; 810 trackRect.height = contentRect.height - (trackBuffer * 2); 811 } 812 813 } 814 815 /** 816 * Gets the height of the tick area for horizontal sliders and the width of 817 * the tick area for vertical sliders. BasicSliderUI uses the returned value 818 * to determine the tick area rectangle. If you want to give your ticks some 819 * room, make this larger than you need and paint your ticks away from the 820 * sides in paintTicks(). 821 * 822 * @return an integer representing the height of the tick area for 823 * horizontal sliders, and the width of the tick area for the vertical 824 * sliders 825 */ getTickLength()826 protected int getTickLength() { 827 return 8; 828 } 829 830 /** 831 * Calculates the tick rectangle. 832 */ calculateTickRect()833 protected void calculateTickRect() { 834 if ( slider.getOrientation() == JSlider.HORIZONTAL ) { 835 tickRect.x = trackRect.x; 836 tickRect.y = trackRect.y + trackRect.height; 837 tickRect.width = trackRect.width; 838 tickRect.height = (slider.getPaintTicks()) ? getTickLength() : 0; 839 } 840 else { 841 tickRect.width = (slider.getPaintTicks()) ? getTickLength() : 0; 842 if(BasicGraphicsUtils.isLeftToRight(slider)) { 843 tickRect.x = trackRect.x + trackRect.width; 844 } 845 else { 846 tickRect.x = trackRect.x - tickRect.width; 847 } 848 tickRect.y = trackRect.y; 849 tickRect.height = trackRect.height; 850 } 851 } 852 853 /** 854 * Calculates the label rectangle. 855 */ calculateLabelRect()856 protected void calculateLabelRect() { 857 if ( slider.getPaintLabels() ) { 858 if ( slider.getOrientation() == JSlider.HORIZONTAL ) { 859 labelRect.x = tickRect.x - trackBuffer; 860 labelRect.y = tickRect.y + tickRect.height; 861 labelRect.width = tickRect.width + (trackBuffer * 2); 862 labelRect.height = getHeightOfTallestLabel(); 863 } 864 else { 865 if(BasicGraphicsUtils.isLeftToRight(slider)) { 866 labelRect.x = tickRect.x + tickRect.width; 867 labelRect.width = getWidthOfWidestLabel(); 868 } 869 else { 870 labelRect.width = getWidthOfWidestLabel(); 871 labelRect.x = tickRect.x - labelRect.width; 872 } 873 labelRect.y = tickRect.y - trackBuffer; 874 labelRect.height = tickRect.height + (trackBuffer * 2); 875 } 876 } 877 else { 878 if ( slider.getOrientation() == JSlider.HORIZONTAL ) { 879 labelRect.x = tickRect.x; 880 labelRect.y = tickRect.y + tickRect.height; 881 labelRect.width = tickRect.width; 882 labelRect.height = 0; 883 } 884 else { 885 if(BasicGraphicsUtils.isLeftToRight(slider)) { 886 labelRect.x = tickRect.x + tickRect.width; 887 } 888 else { 889 labelRect.x = tickRect.x; 890 } 891 labelRect.y = tickRect.y; 892 labelRect.width = 0; 893 labelRect.height = tickRect.height; 894 } 895 } 896 } 897 898 /** 899 * Returns the thumb size. 900 * @return the thumb size 901 */ getThumbSize()902 protected Dimension getThumbSize() { 903 Dimension size = new Dimension(); 904 905 if ( slider.getOrientation() == JSlider.VERTICAL ) { 906 size.width = 20; 907 size.height = 11; 908 } 909 else { 910 size.width = 11; 911 size.height = 20; 912 } 913 914 return size; 915 } 916 917 /** 918 * A property change handler. 919 */ 920 public class PropertyChangeHandler implements PropertyChangeListener { 921 // NOTE: This class exists only for backward compatibility. All 922 // its functionality has been moved into Handler. If you need to add 923 // new functionality add it to the Handler, but make sure this 924 // class calls into the Handler. 925 /** {@inheritDoc} */ propertyChange( PropertyChangeEvent e )926 public void propertyChange( PropertyChangeEvent e ) { 927 getHandler().propertyChange(e); 928 } 929 } 930 931 /** 932 * Returns the width of the widest label. 933 * @return the width of the widest label 934 */ getWidthOfWidestLabel()935 protected int getWidthOfWidestLabel() { 936 @SuppressWarnings("rawtypes") 937 Dictionary dictionary = slider.getLabelTable(); 938 int widest = 0; 939 if ( dictionary != null ) { 940 Enumeration<?> keys = dictionary.keys(); 941 while ( keys.hasMoreElements() ) { 942 JComponent label = (JComponent) dictionary.get(keys.nextElement()); 943 widest = Math.max( label.getPreferredSize().width, widest ); 944 } 945 } 946 return widest; 947 } 948 949 /** 950 * Returns the height of the tallest label. 951 * @return the height of the tallest label 952 */ getHeightOfTallestLabel()953 protected int getHeightOfTallestLabel() { 954 @SuppressWarnings("rawtypes") 955 Dictionary dictionary = slider.getLabelTable(); 956 int tallest = 0; 957 if ( dictionary != null ) { 958 Enumeration<?> keys = dictionary.keys(); 959 while ( keys.hasMoreElements() ) { 960 JComponent label = (JComponent) dictionary.get(keys.nextElement()); 961 tallest = Math.max( label.getPreferredSize().height, tallest ); 962 } 963 } 964 return tallest; 965 } 966 967 /** 968 * Returns the width of the highest value label. 969 * @return the width of the highest value label 970 */ getWidthOfHighValueLabel()971 protected int getWidthOfHighValueLabel() { 972 Component label = getHighestValueLabel(); 973 int width = 0; 974 975 if ( label != null ) { 976 width = label.getPreferredSize().width; 977 } 978 979 return width; 980 } 981 982 /** 983 * Returns the width of the lowest value label. 984 * @return the width of the lowest value label 985 */ getWidthOfLowValueLabel()986 protected int getWidthOfLowValueLabel() { 987 Component label = getLowestValueLabel(); 988 int width = 0; 989 990 if ( label != null ) { 991 width = label.getPreferredSize().width; 992 } 993 994 return width; 995 } 996 997 /** 998 * Returns the height of the highest value label. 999 * @return the height of the highest value label 1000 */ getHeightOfHighValueLabel()1001 protected int getHeightOfHighValueLabel() { 1002 Component label = getHighestValueLabel(); 1003 int height = 0; 1004 1005 if ( label != null ) { 1006 height = label.getPreferredSize().height; 1007 } 1008 1009 return height; 1010 } 1011 1012 /** 1013 * Returns the height of the lowest value label. 1014 * @return the height of the lowest value label 1015 */ getHeightOfLowValueLabel()1016 protected int getHeightOfLowValueLabel() { 1017 Component label = getLowestValueLabel(); 1018 int height = 0; 1019 1020 if ( label != null ) { 1021 height = label.getPreferredSize().height; 1022 } 1023 1024 return height; 1025 } 1026 1027 /** 1028 * Draws inverted. 1029 * @return the inverted-ness 1030 */ drawInverted()1031 protected boolean drawInverted() { 1032 if (slider.getOrientation()==JSlider.HORIZONTAL) { 1033 if(BasicGraphicsUtils.isLeftToRight(slider)) { 1034 return slider.getInverted(); 1035 } else { 1036 return !slider.getInverted(); 1037 } 1038 } else { 1039 return slider.getInverted(); 1040 } 1041 } 1042 1043 /** 1044 * Returns the biggest value that has an entry in the label table. 1045 * 1046 * @return biggest value that has an entry in the label table, or 1047 * null. 1048 * @since 1.6 1049 */ getHighestValue()1050 protected Integer getHighestValue() { 1051 @SuppressWarnings("rawtypes") 1052 Dictionary dictionary = slider.getLabelTable(); 1053 1054 if (dictionary == null) { 1055 return null; 1056 } 1057 1058 Enumeration<?> keys = dictionary.keys(); 1059 1060 Integer max = null; 1061 1062 while (keys.hasMoreElements()) { 1063 Integer i = (Integer) keys.nextElement(); 1064 1065 if (max == null || i > max) { 1066 max = i; 1067 } 1068 } 1069 1070 return max; 1071 } 1072 1073 /** 1074 * Returns the smallest value that has an entry in the label table. 1075 * 1076 * @return smallest value that has an entry in the label table, or 1077 * null. 1078 * @since 1.6 1079 */ getLowestValue()1080 protected Integer getLowestValue() { 1081 @SuppressWarnings("rawtypes") 1082 Dictionary dictionary = slider.getLabelTable(); 1083 1084 if (dictionary == null) { 1085 return null; 1086 } 1087 1088 Enumeration<?> keys = dictionary.keys(); 1089 1090 Integer min = null; 1091 1092 while (keys.hasMoreElements()) { 1093 Integer i = (Integer) keys.nextElement(); 1094 1095 if (min == null || i < min) { 1096 min = i; 1097 } 1098 } 1099 1100 return min; 1101 } 1102 1103 1104 /** 1105 * Returns the label that corresponds to the highest slider value in the 1106 * label table. 1107 * 1108 * @return the label that corresponds to the highest slider value in the 1109 * label table 1110 * @see JSlider#setLabelTable 1111 */ getLowestValueLabel()1112 protected Component getLowestValueLabel() { 1113 Integer min = getLowestValue(); 1114 if (min != null) { 1115 return (Component)slider.getLabelTable().get(min); 1116 } 1117 return null; 1118 } 1119 1120 /** 1121 * Returns the label that corresponds to the lowest slider value in the 1122 * label table. 1123 * 1124 * @return the label that corresponds to the lowest slider value in the 1125 * label table 1126 * @see JSlider#setLabelTable 1127 */ getHighestValueLabel()1128 protected Component getHighestValueLabel() { 1129 Integer max = getHighestValue(); 1130 if (max != null) { 1131 return (Component)slider.getLabelTable().get(max); 1132 } 1133 return null; 1134 } 1135 paint( Graphics g, JComponent c )1136 public void paint( Graphics g, JComponent c ) { 1137 recalculateIfInsetsChanged(); 1138 recalculateIfOrientationChanged(); 1139 Rectangle clip = g.getClipBounds(); 1140 1141 if ( !clip.intersects(trackRect) && slider.getPaintTrack()) 1142 calculateGeometry(); 1143 1144 if ( slider.getPaintTrack() && clip.intersects( trackRect ) ) { 1145 paintTrack( g ); 1146 } 1147 if ( slider.getPaintTicks() && clip.intersects( tickRect ) ) { 1148 paintTicks( g ); 1149 } 1150 if ( slider.getPaintLabels() && clip.intersects( labelRect ) ) { 1151 paintLabels( g ); 1152 } 1153 if ( slider.hasFocus() && clip.intersects( focusRect ) ) { 1154 paintFocus( g ); 1155 } 1156 if ( clip.intersects( thumbRect ) ) { 1157 paintThumb( g ); 1158 } 1159 } 1160 1161 /** 1162 * Recalculates if the insets have changed. 1163 */ recalculateIfInsetsChanged()1164 protected void recalculateIfInsetsChanged() { 1165 Insets newInsets = slider.getInsets(); 1166 if ( !newInsets.equals( insetCache ) ) { 1167 insetCache = newInsets; 1168 calculateGeometry(); 1169 } 1170 } 1171 1172 /** 1173 * Recalculates if the orientation has changed. 1174 */ recalculateIfOrientationChanged()1175 protected void recalculateIfOrientationChanged() { 1176 boolean ltr = BasicGraphicsUtils.isLeftToRight(slider); 1177 if ( ltr!=leftToRightCache ) { 1178 leftToRightCache = ltr; 1179 calculateGeometry(); 1180 } 1181 } 1182 1183 /** 1184 * Paints focus. 1185 * @param g the graphics 1186 */ paintFocus(Graphics g)1187 public void paintFocus(Graphics g) { 1188 g.setColor( getFocusColor() ); 1189 1190 BasicGraphicsUtils.drawDashedRect( g, focusRect.x, focusRect.y, 1191 focusRect.width, focusRect.height ); 1192 } 1193 1194 /** 1195 * Paints track. 1196 * @param g the graphics 1197 */ paintTrack(Graphics g)1198 public void paintTrack(Graphics g) { 1199 1200 Rectangle trackBounds = trackRect; 1201 1202 if ( slider.getOrientation() == JSlider.HORIZONTAL ) { 1203 int cy = (trackBounds.height / 2) - 2; 1204 int cw = trackBounds.width; 1205 1206 g.translate(trackBounds.x, trackBounds.y + cy); 1207 1208 g.setColor(getShadowColor()); 1209 g.drawLine(0, 0, cw - 1, 0); 1210 g.drawLine(0, 1, 0, 2); 1211 g.setColor(getHighlightColor()); 1212 g.drawLine(0, 3, cw, 3); 1213 g.drawLine(cw, 0, cw, 3); 1214 g.setColor(Color.black); 1215 g.drawLine(1, 1, cw-2, 1); 1216 1217 g.translate(-trackBounds.x, -(trackBounds.y + cy)); 1218 } 1219 else { 1220 int cx = (trackBounds.width / 2) - 2; 1221 int ch = trackBounds.height; 1222 1223 g.translate(trackBounds.x + cx, trackBounds.y); 1224 1225 g.setColor(getShadowColor()); 1226 g.drawLine(0, 0, 0, ch - 1); 1227 g.drawLine(1, 0, 2, 0); 1228 g.setColor(getHighlightColor()); 1229 g.drawLine(3, 0, 3, ch); 1230 g.drawLine(0, ch, 3, ch); 1231 g.setColor(Color.black); 1232 g.drawLine(1, 1, 1, ch-2); 1233 1234 g.translate(-(trackBounds.x + cx), -trackBounds.y); 1235 } 1236 } 1237 1238 /** 1239 * Paints ticks. 1240 * @param g the graphics 1241 */ paintTicks(Graphics g)1242 public void paintTicks(Graphics g) { 1243 Rectangle tickBounds = tickRect; 1244 1245 g.setColor(DefaultLookup.getColor(slider, this, "Slider.tickColor", Color.black)); 1246 1247 if ( slider.getOrientation() == JSlider.HORIZONTAL ) { 1248 g.translate(0, tickBounds.y); 1249 1250 if (slider.getMinorTickSpacing() > 0) { 1251 int value = slider.getMinimum(); 1252 1253 while ( value <= slider.getMaximum() ) { 1254 int xPos = xPositionForValue(value); 1255 paintMinorTickForHorizSlider( g, tickBounds, xPos ); 1256 1257 // Overflow checking 1258 if (Integer.MAX_VALUE - slider.getMinorTickSpacing() < value) { 1259 break; 1260 } 1261 1262 value += slider.getMinorTickSpacing(); 1263 } 1264 } 1265 1266 if (slider.getMajorTickSpacing() > 0) { 1267 int value = slider.getMinimum(); 1268 1269 while ( value <= slider.getMaximum() ) { 1270 int xPos = xPositionForValue(value); 1271 paintMajorTickForHorizSlider( g, tickBounds, xPos ); 1272 1273 // Overflow checking 1274 if (Integer.MAX_VALUE - slider.getMajorTickSpacing() < value) { 1275 break; 1276 } 1277 1278 value += slider.getMajorTickSpacing(); 1279 } 1280 } 1281 1282 g.translate( 0, -tickBounds.y); 1283 } else { 1284 g.translate(tickBounds.x, 0); 1285 1286 if (slider.getMinorTickSpacing() > 0) { 1287 int offset = 0; 1288 if(!BasicGraphicsUtils.isLeftToRight(slider)) { 1289 offset = tickBounds.width - tickBounds.width / 2; 1290 g.translate(offset, 0); 1291 } 1292 1293 int value = slider.getMinimum(); 1294 1295 while (value <= slider.getMaximum()) { 1296 int yPos = yPositionForValue(value); 1297 paintMinorTickForVertSlider( g, tickBounds, yPos ); 1298 1299 // Overflow checking 1300 if (Integer.MAX_VALUE - slider.getMinorTickSpacing() < value) { 1301 break; 1302 } 1303 1304 value += slider.getMinorTickSpacing(); 1305 } 1306 1307 if(!BasicGraphicsUtils.isLeftToRight(slider)) { 1308 g.translate(-offset, 0); 1309 } 1310 } 1311 1312 if (slider.getMajorTickSpacing() > 0) { 1313 if(!BasicGraphicsUtils.isLeftToRight(slider)) { 1314 g.translate(2, 0); 1315 } 1316 1317 int value = slider.getMinimum(); 1318 1319 while (value <= slider.getMaximum()) { 1320 int yPos = yPositionForValue(value); 1321 paintMajorTickForVertSlider( g, tickBounds, yPos ); 1322 1323 // Overflow checking 1324 if (Integer.MAX_VALUE - slider.getMajorTickSpacing() < value) { 1325 break; 1326 } 1327 1328 value += slider.getMajorTickSpacing(); 1329 } 1330 1331 if(!BasicGraphicsUtils.isLeftToRight(slider)) { 1332 g.translate(-2, 0); 1333 } 1334 } 1335 g.translate(-tickBounds.x, 0); 1336 } 1337 } 1338 1339 /** 1340 * Paints minor tick for horizontal slider. 1341 * @param g the graphics 1342 * @param tickBounds the tick bounds 1343 * @param x the x coordinate 1344 */ paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x )1345 protected void paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) { 1346 g.drawLine( x, 0, x, tickBounds.height / 2 - 1 ); 1347 } 1348 1349 /** 1350 * Paints major tick for horizontal slider. 1351 * @param g the graphics 1352 * @param tickBounds the tick bounds 1353 * @param x the x coordinate 1354 */ paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x )1355 protected void paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) { 1356 g.drawLine( x, 0, x, tickBounds.height - 2 ); 1357 } 1358 1359 /** 1360 * Paints minor tick for vertical slider. 1361 * @param g the graphics 1362 * @param tickBounds the tick bounds 1363 * @param y the y coordinate 1364 */ paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, int y )1365 protected void paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) { 1366 g.drawLine( 0, y, tickBounds.width / 2 - 1, y ); 1367 } 1368 1369 /** 1370 * Paints major tick for vertical slider. 1371 * @param g the graphics 1372 * @param tickBounds the tick bounds 1373 * @param y the y coordinate 1374 */ paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, int y )1375 protected void paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) { 1376 g.drawLine( 0, y, tickBounds.width - 2, y ); 1377 } 1378 1379 /** 1380 * Paints the labels. 1381 * @param g the graphics 1382 */ paintLabels( Graphics g )1383 public void paintLabels( Graphics g ) { 1384 Rectangle labelBounds = labelRect; 1385 1386 @SuppressWarnings("rawtypes") 1387 Dictionary dictionary = slider.getLabelTable(); 1388 if ( dictionary != null ) { 1389 Enumeration<?> keys = dictionary.keys(); 1390 int minValue = slider.getMinimum(); 1391 int maxValue = slider.getMaximum(); 1392 boolean enabled = slider.isEnabled(); 1393 while ( keys.hasMoreElements() ) { 1394 Integer key = (Integer)keys.nextElement(); 1395 int value = key.intValue(); 1396 if (value >= minValue && value <= maxValue) { 1397 JComponent label = (JComponent) dictionary.get(key); 1398 label.setEnabled(enabled); 1399 1400 if (label instanceof JLabel) { 1401 Icon icon = label.isEnabled() ? ((JLabel) label).getIcon() : ((JLabel) label).getDisabledIcon(); 1402 1403 if (icon instanceof ImageIcon) { 1404 // Register Slider as an image observer. It allows to catch notifications about 1405 // image changes (e.g. gif animation) 1406 Toolkit.getDefaultToolkit().checkImage(((ImageIcon) icon).getImage(), -1, -1, slider); 1407 } 1408 } 1409 1410 if ( slider.getOrientation() == JSlider.HORIZONTAL ) { 1411 g.translate( 0, labelBounds.y ); 1412 paintHorizontalLabel( g, value, label ); 1413 g.translate( 0, -labelBounds.y ); 1414 } 1415 else { 1416 int offset = 0; 1417 if (!BasicGraphicsUtils.isLeftToRight(slider)) { 1418 offset = labelBounds.width - 1419 label.getPreferredSize().width; 1420 } 1421 g.translate( labelBounds.x + offset, 0 ); 1422 paintVerticalLabel( g, value, label ); 1423 g.translate( -labelBounds.x - offset, 0 ); 1424 } 1425 } 1426 } 1427 } 1428 1429 } 1430 1431 /** 1432 * Called for every label in the label table. Used to draw the labels for 1433 * horizontal sliders. The graphics have been translated to labelRect.y 1434 * already. 1435 * 1436 * @param g the graphics context in which to paint 1437 * @param value the value of the slider 1438 * @param label the component label in the label table that needs to be 1439 * painted 1440 * @see JSlider#setLabelTable 1441 */ paintHorizontalLabel( Graphics g, int value, Component label )1442 protected void paintHorizontalLabel( Graphics g, int value, Component label ) { 1443 int labelCenter = xPositionForValue( value ); 1444 int labelLeft = labelCenter - (label.getPreferredSize().width / 2); 1445 g.translate( labelLeft, 0 ); 1446 label.paint( g ); 1447 g.translate( -labelLeft, 0 ); 1448 } 1449 1450 /** 1451 * Called for every label in the label table. Used to draw the labels for 1452 * vertical sliders. The graphics have been translated to labelRect.x 1453 * already. 1454 * 1455 * @param g the graphics context in which to paint 1456 * @param value the value of the slider 1457 * @param label the component label in the label table that needs to be 1458 * painted 1459 * @see JSlider#setLabelTable 1460 */ paintVerticalLabel( Graphics g, int value, Component label )1461 protected void paintVerticalLabel( Graphics g, int value, Component label ) { 1462 int labelCenter = yPositionForValue( value ); 1463 int labelTop = labelCenter - (label.getPreferredSize().height / 2); 1464 g.translate( 0, labelTop ); 1465 label.paint( g ); 1466 g.translate( 0, -labelTop ); 1467 } 1468 1469 /** 1470 * Paints the thumb. 1471 * @param g the graphics 1472 */ paintThumb(Graphics g)1473 public void paintThumb(Graphics g) { 1474 Rectangle knobBounds = thumbRect; 1475 int w = knobBounds.width; 1476 int h = knobBounds.height; 1477 1478 g.translate(knobBounds.x, knobBounds.y); 1479 1480 if ( slider.isEnabled() ) { 1481 g.setColor(slider.getBackground()); 1482 } 1483 else { 1484 g.setColor(slider.getBackground().darker()); 1485 } 1486 1487 Boolean paintThumbArrowShape = 1488 (Boolean)slider.getClientProperty("Slider.paintThumbArrowShape"); 1489 1490 if ((!slider.getPaintTicks() && paintThumbArrowShape == null) || 1491 paintThumbArrowShape == Boolean.FALSE) { 1492 1493 // "plain" version 1494 g.fillRect(0, 0, w, h); 1495 1496 g.setColor(Color.black); 1497 g.drawLine(0, h-1, w-1, h-1); 1498 g.drawLine(w-1, 0, w-1, h-1); 1499 1500 g.setColor(highlightColor); 1501 g.drawLine(0, 0, 0, h-2); 1502 g.drawLine(1, 0, w-2, 0); 1503 1504 g.setColor(shadowColor); 1505 g.drawLine(1, h-2, w-2, h-2); 1506 g.drawLine(w-2, 1, w-2, h-3); 1507 } 1508 else if ( slider.getOrientation() == JSlider.HORIZONTAL ) { 1509 int cw = w / 2; 1510 g.fillRect(1, 1, w-3, h-1-cw); 1511 Polygon p = new Polygon(); 1512 p.addPoint(1, h-cw); 1513 p.addPoint(cw-1, h-1); 1514 p.addPoint(w-2, h-1-cw); 1515 g.fillPolygon(p); 1516 1517 g.setColor(highlightColor); 1518 g.drawLine(0, 0, w-2, 0); 1519 g.drawLine(0, 1, 0, h-1-cw); 1520 g.drawLine(0, h-cw, cw-1, h-1); 1521 1522 g.setColor(Color.black); 1523 g.drawLine(w-1, 0, w-1, h-2-cw); 1524 g.drawLine(w-1, h-1-cw, w-1-cw, h-1); 1525 1526 g.setColor(shadowColor); 1527 g.drawLine(w-2, 1, w-2, h-2-cw); 1528 g.drawLine(w-2, h-1-cw, w-1-cw, h-2); 1529 } 1530 else { // vertical 1531 int cw = h / 2; 1532 if(BasicGraphicsUtils.isLeftToRight(slider)) { 1533 g.fillRect(1, 1, w-1-cw, h-3); 1534 Polygon p = new Polygon(); 1535 p.addPoint(w-cw-1, 0); 1536 p.addPoint(w-1, cw); 1537 p.addPoint(w-1-cw, h-2); 1538 g.fillPolygon(p); 1539 1540 g.setColor(highlightColor); 1541 g.drawLine(0, 0, 0, h - 2); // left 1542 g.drawLine(1, 0, w-1-cw, 0); // top 1543 g.drawLine(w-cw-1, 0, w-1, cw); // top slant 1544 1545 g.setColor(Color.black); 1546 g.drawLine(0, h-1, w-2-cw, h-1); // bottom 1547 g.drawLine(w-1-cw, h-1, w-1, h-1-cw); // bottom slant 1548 1549 g.setColor(shadowColor); 1550 g.drawLine(1, h-2, w-2-cw, h-2 ); // bottom 1551 g.drawLine(w-1-cw, h-2, w-2, h-cw-1 ); // bottom slant 1552 } 1553 else { 1554 g.fillRect(5, 1, w-1-cw, h-3); 1555 Polygon p = new Polygon(); 1556 p.addPoint(cw, 0); 1557 p.addPoint(0, cw); 1558 p.addPoint(cw, h-2); 1559 g.fillPolygon(p); 1560 1561 g.setColor(highlightColor); 1562 g.drawLine(cw-1, 0, w-2, 0); // top 1563 g.drawLine(0, cw, cw, 0); // top slant 1564 1565 g.setColor(Color.black); 1566 g.drawLine(0, h-1-cw, cw, h-1 ); // bottom slant 1567 g.drawLine(cw, h-1, w-1, h-1); // bottom 1568 1569 g.setColor(shadowColor); 1570 g.drawLine(cw, h-2, w-2, h-2 ); // bottom 1571 g.drawLine(w-1, 1, w-1, h-2 ); // right 1572 } 1573 } 1574 1575 g.translate(-knobBounds.x, -knobBounds.y); 1576 } 1577 1578 // Used exclusively by setThumbLocation() 1579 private static Rectangle unionRect = new Rectangle(); 1580 1581 /** 1582 * Sets the thumb location. 1583 * @param x the x coordinate 1584 * @param y the y coordinate 1585 */ setThumbLocation(int x, int y)1586 public void setThumbLocation(int x, int y) { 1587 unionRect.setBounds( thumbRect ); 1588 1589 thumbRect.setLocation( x, y ); 1590 1591 SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, unionRect ); 1592 slider.repaint( unionRect.x, unionRect.y, unionRect.width, unionRect.height ); 1593 } 1594 1595 /** 1596 * Scrolls by block. 1597 * @param direction the direction 1598 */ scrollByBlock(int direction)1599 public void scrollByBlock(int direction) { 1600 synchronized(slider) { 1601 int blockIncrement = 1602 (slider.getMaximum() - slider.getMinimum()) / 10; 1603 if (blockIncrement == 0) { 1604 blockIncrement = 1; 1605 } 1606 1607 int tickSpacing = getTickSpacing(); 1608 if (slider.getSnapToTicks()) { 1609 1610 if (blockIncrement < tickSpacing) { 1611 blockIncrement = tickSpacing; 1612 } 1613 } 1614 else { 1615 if (tickSpacing > 0) { 1616 blockIncrement = tickSpacing; 1617 } 1618 } 1619 1620 int delta = blockIncrement * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL); 1621 slider.setValue(slider.getValue() + delta); 1622 } 1623 } 1624 1625 /** 1626 * Scrolls by unit. 1627 * @param direction the direction 1628 */ scrollByUnit(int direction)1629 public void scrollByUnit(int direction) { 1630 synchronized(slider) { 1631 int delta = ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL); 1632 1633 if (slider.getSnapToTicks()) { 1634 delta *= getTickSpacing(); 1635 } 1636 1637 slider.setValue(slider.getValue() + delta); 1638 } 1639 } 1640 1641 /** 1642 * This function is called when a mousePressed was detected in the track, 1643 * not in the thumb. The default behavior is to scroll by block. You can 1644 * override this method to stop it from scrolling or to add additional 1645 * behavior. 1646 * 1647 * @param dir the direction and number of blocks to scroll 1648 */ scrollDueToClickInTrack( int dir )1649 protected void scrollDueToClickInTrack( int dir ) { 1650 scrollByBlock( dir ); 1651 } 1652 1653 /** 1654 * Returns the x position for a value. 1655 * @param value the value 1656 * @return the x position for a value 1657 */ xPositionForValue( int value )1658 protected int xPositionForValue( int value ) { 1659 int min = slider.getMinimum(); 1660 int max = slider.getMaximum(); 1661 int trackLength = trackRect.width; 1662 double valueRange = (double)max - (double)min; 1663 double pixelsPerValue = (double)trackLength / valueRange; 1664 int trackLeft = trackRect.x; 1665 int trackRight = trackRect.x + (trackRect.width - 1); 1666 int xPosition; 1667 1668 if ( !drawInverted() ) { 1669 xPosition = trackLeft; 1670 xPosition += Math.round( pixelsPerValue * ((double)value - min) ); 1671 } 1672 else { 1673 xPosition = trackRight; 1674 xPosition -= Math.round( pixelsPerValue * ((double)value - min) ); 1675 } 1676 1677 xPosition = Math.max( trackLeft, xPosition ); 1678 xPosition = Math.min( trackRight, xPosition ); 1679 1680 return xPosition; 1681 } 1682 1683 /** 1684 * Returns the y position for a value. 1685 * @param value the value 1686 * @return the y position for a value 1687 */ yPositionForValue( int value )1688 protected int yPositionForValue( int value ) { 1689 return yPositionForValue(value, trackRect.y, trackRect.height); 1690 } 1691 1692 /** 1693 * Returns the y location for the specified value. No checking is 1694 * done on the arguments. In particular if <code>trackHeight</code> is 1695 * negative undefined results may occur. 1696 * 1697 * @param value the slider value to get the location for 1698 * @param trackY y-origin of the track 1699 * @param trackHeight the height of the track 1700 * @return the y location for the specified value of the slider 1701 * @since 1.6 1702 */ yPositionForValue(int value, int trackY, int trackHeight)1703 protected int yPositionForValue(int value, int trackY, int trackHeight) { 1704 int min = slider.getMinimum(); 1705 int max = slider.getMaximum(); 1706 double valueRange = (double)max - (double)min; 1707 double pixelsPerValue = (double)trackHeight / valueRange; 1708 int trackBottom = trackY + (trackHeight - 1); 1709 int yPosition; 1710 1711 if ( !drawInverted() ) { 1712 yPosition = trackY; 1713 yPosition += Math.round( pixelsPerValue * ((double)max - value ) ); 1714 } 1715 else { 1716 yPosition = trackY; 1717 yPosition += Math.round( pixelsPerValue * ((double)value - min) ); 1718 } 1719 1720 yPosition = Math.max( trackY, yPosition ); 1721 yPosition = Math.min( trackBottom, yPosition ); 1722 1723 return yPosition; 1724 } 1725 1726 /** 1727 * Returns the value at the y position. If {@code yPos} is beyond the 1728 * track at the bottom or the top, this method sets the value to either 1729 * the minimum or maximum value of the slider, depending on if the slider 1730 * is inverted or not. 1731 * 1732 * @param yPos the location of the slider along the y axis 1733 * @return the value at the y position 1734 */ valueForYPosition( int yPos )1735 public int valueForYPosition( int yPos ) { 1736 int value; 1737 final int minValue = slider.getMinimum(); 1738 final int maxValue = slider.getMaximum(); 1739 final int trackLength = trackRect.height; 1740 final int trackTop = trackRect.y; 1741 final int trackBottom = trackRect.y + (trackRect.height - 1); 1742 1743 if ( yPos <= trackTop ) { 1744 value = drawInverted() ? minValue : maxValue; 1745 } 1746 else if ( yPos >= trackBottom ) { 1747 value = drawInverted() ? maxValue : minValue; 1748 } 1749 else { 1750 int distanceFromTrackTop = yPos - trackTop; 1751 double valueRange = (double)maxValue - (double)minValue; 1752 double valuePerPixel = valueRange / (double)trackLength; 1753 int valueFromTrackTop = (int)Math.round( distanceFromTrackTop * valuePerPixel ); 1754 1755 value = drawInverted() ? minValue + valueFromTrackTop : maxValue - valueFromTrackTop; 1756 } 1757 1758 return value; 1759 } 1760 1761 /** 1762 * Returns the value at the x position. If {@code xPos} is beyond the 1763 * track at the left or the right, this method sets the value to either the 1764 * minimum or maximum value of the slider, depending on if the slider is 1765 * inverted or not. 1766 * 1767 * @param xPos the location of the slider along the x axis 1768 * @return the value of the x position 1769 */ valueForXPosition( int xPos )1770 public int valueForXPosition( int xPos ) { 1771 int value; 1772 final int minValue = slider.getMinimum(); 1773 final int maxValue = slider.getMaximum(); 1774 final int trackLength = trackRect.width; 1775 final int trackLeft = trackRect.x; 1776 final int trackRight = trackRect.x + (trackRect.width - 1); 1777 1778 if ( xPos <= trackLeft ) { 1779 value = drawInverted() ? maxValue : minValue; 1780 } 1781 else if ( xPos >= trackRight ) { 1782 value = drawInverted() ? minValue : maxValue; 1783 } 1784 else { 1785 int distanceFromTrackLeft = xPos - trackLeft; 1786 double valueRange = (double)maxValue - (double)minValue; 1787 double valuePerPixel = valueRange / (double)trackLength; 1788 int valueFromTrackLeft = (int)Math.round( distanceFromTrackLeft * valuePerPixel ); 1789 1790 value = drawInverted() ? maxValue - valueFromTrackLeft : 1791 minValue + valueFromTrackLeft; 1792 } 1793 1794 return value; 1795 } 1796 1797 1798 private class Handler implements ChangeListener, 1799 ComponentListener, FocusListener, PropertyChangeListener { 1800 // Change Handler stateChanged(ChangeEvent e)1801 public void stateChanged(ChangeEvent e) { 1802 if (!isDragging) { 1803 calculateThumbLocation(); 1804 slider.repaint(); 1805 } 1806 lastValue = slider.getValue(); 1807 } 1808 1809 // Component Handler componentHidden(ComponentEvent e)1810 public void componentHidden(ComponentEvent e) { } componentMoved(ComponentEvent e)1811 public void componentMoved(ComponentEvent e) { } componentResized(ComponentEvent e)1812 public void componentResized(ComponentEvent e) { 1813 calculateGeometry(); 1814 slider.repaint(); 1815 } componentShown(ComponentEvent e)1816 public void componentShown(ComponentEvent e) { } 1817 1818 // Focus Handler focusGained(FocusEvent e)1819 public void focusGained(FocusEvent e) { slider.repaint(); } focusLost(FocusEvent e)1820 public void focusLost(FocusEvent e) { slider.repaint(); } 1821 1822 // Property Change Handler propertyChange(PropertyChangeEvent e)1823 public void propertyChange(PropertyChangeEvent e) { 1824 String propertyName = e.getPropertyName(); 1825 if (propertyName == "orientation" || 1826 propertyName == "inverted" || 1827 propertyName == "labelTable" || 1828 propertyName == "majorTickSpacing" || 1829 propertyName == "minorTickSpacing" || 1830 propertyName == "paintTicks" || 1831 propertyName == "paintTrack" || 1832 propertyName == "font" || 1833 SwingUtilities2.isScaleChanged(e) || 1834 propertyName == "paintLabels" || 1835 propertyName == "Slider.paintThumbArrowShape") { 1836 checkedLabelBaselines = false; 1837 calculateGeometry(); 1838 slider.repaint(); 1839 } else if (propertyName == "componentOrientation") { 1840 calculateGeometry(); 1841 slider.repaint(); 1842 InputMap km = getInputMap(JComponent.WHEN_FOCUSED, slider); 1843 SwingUtilities.replaceUIInputMap(slider, 1844 JComponent.WHEN_FOCUSED, km); 1845 } else if (propertyName == "model") { 1846 ((BoundedRangeModel)e.getOldValue()).removeChangeListener( 1847 changeListener); 1848 ((BoundedRangeModel)e.getNewValue()).addChangeListener( 1849 changeListener); 1850 calculateThumbLocation(); 1851 slider.repaint(); 1852 } 1853 } 1854 } 1855 1856 ///////////////////////////////////////////////////////////////////////// 1857 /// Model Listener Class 1858 ///////////////////////////////////////////////////////////////////////// 1859 /** 1860 * Data model listener. 1861 * 1862 * This class should be treated as a "protected" inner class. 1863 * Instantiate it only within subclasses of <code>Foo</code>. 1864 */ 1865 public class ChangeHandler implements ChangeListener { 1866 // NOTE: This class exists only for backward compatibility. All 1867 // its functionality has been moved into Handler. If you need to add 1868 // new functionality add it to the Handler, but make sure this 1869 // class calls into the Handler. stateChanged(ChangeEvent e)1870 public void stateChanged(ChangeEvent e) { 1871 getHandler().stateChanged(e); 1872 } 1873 } 1874 1875 ///////////////////////////////////////////////////////////////////////// 1876 /// Track Listener Class 1877 ///////////////////////////////////////////////////////////////////////// 1878 /** 1879 * Track mouse movements. 1880 * 1881 * This class should be treated as a "protected" inner class. 1882 * Instantiate it only within subclasses of <code>Foo</code>. 1883 */ 1884 public class TrackListener extends MouseInputAdapter { 1885 /** The offset */ 1886 protected transient int offset; 1887 /** Current mouse x. */ 1888 protected transient int currentMouseX; 1889 /** Current mouse y. */ 1890 protected transient int currentMouseY; 1891 1892 /** 1893 * {@inheritDoc} 1894 */ mouseReleased(MouseEvent e)1895 public void mouseReleased(MouseEvent e) { 1896 if (!slider.isEnabled()) { 1897 return; 1898 } 1899 1900 offset = 0; 1901 scrollTimer.stop(); 1902 1903 isDragging = false; 1904 slider.setValueIsAdjusting(false); 1905 slider.repaint(); 1906 } 1907 1908 /** 1909 * If the mouse is pressed above the "thumb" component 1910 * then reduce the scrollbars value by one page ("page up"), 1911 * otherwise increase it by one page. If there is no 1912 * thumb then page up if the mouse is in the upper half 1913 * of the track. 1914 */ mousePressed(MouseEvent e)1915 public void mousePressed(MouseEvent e) { 1916 if (!slider.isEnabled()) { 1917 return; 1918 } 1919 1920 // We should recalculate geometry just before 1921 // calculation of the thumb movement direction. 1922 // It is important for the case, when JSlider 1923 // is a cell editor in JTable. See 6348946. 1924 calculateGeometry(); 1925 1926 currentMouseX = e.getX(); 1927 currentMouseY = e.getY(); 1928 1929 if (slider.isRequestFocusEnabled()) { 1930 slider.requestFocus(); 1931 } 1932 1933 // Clicked in the Thumb area? 1934 if (thumbRect.contains(currentMouseX, currentMouseY)) { 1935 if (UIManager.getBoolean("Slider.onlyLeftMouseButtonDrag") 1936 && !SwingUtilities.isLeftMouseButton(e)) { 1937 return; 1938 } 1939 1940 switch (slider.getOrientation()) { 1941 case JSlider.VERTICAL: 1942 offset = currentMouseY - thumbRect.y; 1943 break; 1944 case JSlider.HORIZONTAL: 1945 offset = currentMouseX - thumbRect.x; 1946 break; 1947 } 1948 isDragging = true; 1949 return; 1950 } 1951 1952 if (!SwingUtilities.isLeftMouseButton(e)) { 1953 return; 1954 } 1955 1956 isDragging = false; 1957 slider.setValueIsAdjusting(true); 1958 1959 Dimension sbSize = slider.getSize(); 1960 int direction = POSITIVE_SCROLL; 1961 1962 switch (slider.getOrientation()) { 1963 case JSlider.VERTICAL: 1964 if ( thumbRect.isEmpty() ) { 1965 int scrollbarCenter = sbSize.height / 2; 1966 if ( !drawInverted() ) { 1967 direction = (currentMouseY < scrollbarCenter) ? 1968 POSITIVE_SCROLL : NEGATIVE_SCROLL; 1969 } 1970 else { 1971 direction = (currentMouseY < scrollbarCenter) ? 1972 NEGATIVE_SCROLL : POSITIVE_SCROLL; 1973 } 1974 } 1975 else { 1976 int thumbY = thumbRect.y; 1977 if ( !drawInverted() ) { 1978 direction = (currentMouseY < thumbY) ? 1979 POSITIVE_SCROLL : NEGATIVE_SCROLL; 1980 } 1981 else { 1982 direction = (currentMouseY < thumbY) ? 1983 NEGATIVE_SCROLL : POSITIVE_SCROLL; 1984 } 1985 } 1986 break; 1987 case JSlider.HORIZONTAL: 1988 if ( thumbRect.isEmpty() ) { 1989 int scrollbarCenter = sbSize.width / 2; 1990 if ( !drawInverted() ) { 1991 direction = (currentMouseX < scrollbarCenter) ? 1992 NEGATIVE_SCROLL : POSITIVE_SCROLL; 1993 } 1994 else { 1995 direction = (currentMouseX < scrollbarCenter) ? 1996 POSITIVE_SCROLL : NEGATIVE_SCROLL; 1997 } 1998 } 1999 else { 2000 int thumbX = thumbRect.x; 2001 if ( !drawInverted() ) { 2002 direction = (currentMouseX < thumbX) ? 2003 NEGATIVE_SCROLL : POSITIVE_SCROLL; 2004 } 2005 else { 2006 direction = (currentMouseX < thumbX) ? 2007 POSITIVE_SCROLL : NEGATIVE_SCROLL; 2008 } 2009 } 2010 break; 2011 } 2012 2013 if (shouldScroll(direction)) { 2014 scrollDueToClickInTrack(direction); 2015 } 2016 if (shouldScroll(direction)) { 2017 scrollTimer.stop(); 2018 scrollListener.setDirection(direction); 2019 scrollTimer.start(); 2020 } 2021 } 2022 2023 /** 2024 * Returns if scrolling should occur 2025 * @param direction the direction. 2026 * @return if scrolling should occur 2027 */ shouldScroll(int direction)2028 public boolean shouldScroll(int direction) { 2029 Rectangle r = thumbRect; 2030 if (slider.getOrientation() == JSlider.VERTICAL) { 2031 if (drawInverted() ? direction < 0 : direction > 0) { 2032 if (r.y <= currentMouseY) { 2033 return false; 2034 } 2035 } 2036 else if (r.y + r.height >= currentMouseY) { 2037 return false; 2038 } 2039 } 2040 else { 2041 if (drawInverted() ? direction < 0 : direction > 0) { 2042 if (r.x + r.width >= currentMouseX) { 2043 return false; 2044 } 2045 } 2046 else if (r.x <= currentMouseX) { 2047 return false; 2048 } 2049 } 2050 2051 if (direction > 0 && slider.getValue() + slider.getExtent() >= 2052 slider.getMaximum()) { 2053 return false; 2054 } 2055 else if (direction < 0 && slider.getValue() <= 2056 slider.getMinimum()) { 2057 return false; 2058 } 2059 2060 return true; 2061 } 2062 2063 /** 2064 * Set the models value to the position of the top/left 2065 * of the thumb relative to the origin of the track. 2066 */ mouseDragged(MouseEvent e)2067 public void mouseDragged(MouseEvent e) { 2068 int thumbMiddle; 2069 2070 if (!slider.isEnabled()) { 2071 return; 2072 } 2073 2074 currentMouseX = e.getX(); 2075 currentMouseY = e.getY(); 2076 2077 if (!isDragging) { 2078 return; 2079 } 2080 2081 slider.setValueIsAdjusting(true); 2082 2083 switch (slider.getOrientation()) { 2084 case JSlider.VERTICAL: 2085 int halfThumbHeight = thumbRect.height / 2; 2086 int thumbTop = e.getY() - offset; 2087 int trackTop = trackRect.y; 2088 int trackBottom = trackRect.y + (trackRect.height - 1); 2089 int vMax = yPositionForValue(slider.getMaximum() - 2090 slider.getExtent()); 2091 2092 if (drawInverted()) { 2093 trackBottom = vMax; 2094 } 2095 else { 2096 trackTop = vMax; 2097 } 2098 thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight); 2099 thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight); 2100 2101 setThumbLocation(thumbRect.x, thumbTop); 2102 2103 thumbMiddle = thumbTop + halfThumbHeight; 2104 slider.setValue( valueForYPosition( thumbMiddle ) ); 2105 break; 2106 case JSlider.HORIZONTAL: 2107 int halfThumbWidth = thumbRect.width / 2; 2108 int thumbLeft = e.getX() - offset; 2109 int trackLeft = trackRect.x; 2110 int trackRight = trackRect.x + (trackRect.width - 1); 2111 int hMax = xPositionForValue(slider.getMaximum() - 2112 slider.getExtent()); 2113 2114 if (drawInverted()) { 2115 trackLeft = hMax; 2116 } 2117 else { 2118 trackRight = hMax; 2119 } 2120 thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth); 2121 thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth); 2122 2123 setThumbLocation(thumbLeft, thumbRect.y); 2124 2125 thumbMiddle = thumbLeft + halfThumbWidth; 2126 slider.setValue(valueForXPosition(thumbMiddle)); 2127 break; 2128 } 2129 } 2130 2131 /** {@inheritDoc} */ mouseMoved(MouseEvent e)2132 public void mouseMoved(MouseEvent e) { } 2133 } 2134 2135 /** 2136 * Scroll-event listener. 2137 * 2138 * This class should be treated as a "protected" inner class. 2139 * Instantiate it only within subclasses of <code>Foo</code>. 2140 */ 2141 public class ScrollListener implements ActionListener { 2142 // changed this class to public to avoid bogus IllegalAccessException 2143 // bug in InternetExplorer browser. It was protected. Work around 2144 // for 4109432 2145 int direction = POSITIVE_SCROLL; 2146 boolean useBlockIncrement; 2147 2148 /** 2149 * Constructs a {@code ScrollListener} 2150 */ ScrollListener()2151 public ScrollListener() { 2152 direction = POSITIVE_SCROLL; 2153 useBlockIncrement = true; 2154 } 2155 2156 /** 2157 * Constructs a {@code ScrollListener} 2158 * @param dir the direction 2159 * @param block whether or not to scroll by block 2160 */ ScrollListener(int dir, boolean block)2161 public ScrollListener(int dir, boolean block) { 2162 direction = dir; 2163 useBlockIncrement = block; 2164 } 2165 2166 /** 2167 * Sets the direction. 2168 * @param direction the new direction 2169 */ setDirection(int direction)2170 public void setDirection(int direction) { 2171 this.direction = direction; 2172 } 2173 2174 /** 2175 * Sets scrolling by block 2176 * @param block the new scroll by block value 2177 */ setScrollByBlock(boolean block)2178 public void setScrollByBlock(boolean block) { 2179 this.useBlockIncrement = block; 2180 } 2181 2182 /** {@inheritDoc} */ actionPerformed(ActionEvent e)2183 public void actionPerformed(ActionEvent e) { 2184 if (useBlockIncrement) { 2185 scrollByBlock(direction); 2186 } 2187 else { 2188 scrollByUnit(direction); 2189 } 2190 if (!trackListener.shouldScroll(direction)) { 2191 ((Timer)e.getSource()).stop(); 2192 } 2193 } 2194 } 2195 2196 /** 2197 * Listener for resizing events. 2198 * <p> 2199 * This class should be treated as a "protected" inner class. 2200 * Instantiate it only within subclasses of <code>Foo</code>. 2201 */ 2202 public class ComponentHandler extends ComponentAdapter { 2203 // NOTE: This class exists only for backward compatibility. All 2204 // its functionality has been moved into Handler. If you need to add 2205 // new functionality add it to the Handler, but make sure this 2206 // class calls into the Handler. componentResized(ComponentEvent e)2207 public void componentResized(ComponentEvent e) { 2208 getHandler().componentResized(e); 2209 } 2210 } 2211 2212 /** 2213 * Focus-change listener. 2214 * <p> 2215 * This class should be treated as a "protected" inner class. 2216 * Instantiate it only within subclasses of <code>Foo</code>. 2217 */ 2218 public class FocusHandler implements FocusListener { 2219 // NOTE: This class exists only for backward compatibility. All 2220 // its functionality has been moved into Handler. If you need to add 2221 // new functionality add it to the Handler, but make sure this 2222 // class calls into the Handler. focusGained(FocusEvent e)2223 public void focusGained(FocusEvent e) { 2224 getHandler().focusGained(e); 2225 } 2226 focusLost(FocusEvent e)2227 public void focusLost(FocusEvent e) { 2228 getHandler().focusLost(e); 2229 } 2230 } 2231 2232 /** 2233 * As of Java 2 platform v1.3 this undocumented class is no longer used. 2234 * The recommended approach to creating bindings is to use a 2235 * combination of an <code>ActionMap</code>, to contain the action, 2236 * and an <code>InputMap</code> to contain the mapping from KeyStroke 2237 * to action description. The InputMap is usually described in the 2238 * LookAndFeel tables. 2239 * <p> 2240 * Please refer to the key bindings specification for further details. 2241 * <p> 2242 * This class should be treated as a "protected" inner class. 2243 * Instantiate it only within subclasses of <code>Foo</code>. 2244 */ 2245 @SuppressWarnings("serial") // Superclass is not serializable across versions 2246 public class ActionScroller extends AbstractAction { 2247 // NOTE: This class exists only for backward compatibility. All 2248 // its functionality has been moved into Actions. If you need to add 2249 // new functionality add it to the Actions, but make sure this 2250 // class calls into the Actions. 2251 int dir; 2252 boolean block; 2253 JSlider slider; 2254 2255 /** 2256 * Constructs an {@code ActionScroller}. 2257 * @param slider a slider 2258 * @param dir the direction 2259 * @param block block scrolling or not 2260 */ ActionScroller( JSlider slider, int dir, boolean block)2261 public ActionScroller( JSlider slider, int dir, boolean block) { 2262 this.dir = dir; 2263 this.block = block; 2264 this.slider = slider; 2265 } 2266 2267 /** {@inheritDoc} */ actionPerformed(ActionEvent e)2268 public void actionPerformed(ActionEvent e) { 2269 SHARED_ACTION.scroll(slider, BasicSliderUI.this, dir, block); 2270 } 2271 2272 /** {@inheritDoc} */ isEnabled()2273 public boolean isEnabled() { 2274 boolean b = true; 2275 if (slider != null) { 2276 b = slider.isEnabled(); 2277 } 2278 return b; 2279 } 2280 2281 } 2282 2283 2284 /** 2285 * A static version of the above. 2286 */ 2287 @SuppressWarnings("serial") // Superclass is not serializable across versions 2288 static class SharedActionScroller extends AbstractAction { 2289 // NOTE: This class exists only for backward compatibility. All 2290 // its functionality has been moved into Actions. If you need to add 2291 // new functionality add it to the Actions, but make sure this 2292 // class calls into the Actions. 2293 int dir; 2294 boolean block; 2295 SharedActionScroller(int dir, boolean block)2296 public SharedActionScroller(int dir, boolean block) { 2297 this.dir = dir; 2298 this.block = block; 2299 } 2300 actionPerformed(ActionEvent evt)2301 public void actionPerformed(ActionEvent evt) { 2302 JSlider slider = (JSlider)evt.getSource(); 2303 BasicSliderUI ui = (BasicSliderUI)BasicLookAndFeel.getUIOfType( 2304 slider.getUI(), BasicSliderUI.class); 2305 if (ui == null) { 2306 return; 2307 } 2308 SHARED_ACTION.scroll(slider, ui, dir, block); 2309 } 2310 } 2311 2312 private static class Actions extends UIAction { 2313 public static final String POSITIVE_UNIT_INCREMENT = 2314 "positiveUnitIncrement"; 2315 public static final String POSITIVE_BLOCK_INCREMENT = 2316 "positiveBlockIncrement"; 2317 public static final String NEGATIVE_UNIT_INCREMENT = 2318 "negativeUnitIncrement"; 2319 public static final String NEGATIVE_BLOCK_INCREMENT = 2320 "negativeBlockIncrement"; 2321 public static final String MIN_SCROLL_INCREMENT = "minScroll"; 2322 public static final String MAX_SCROLL_INCREMENT = "maxScroll"; 2323 2324 Actions()2325 Actions() { 2326 super(null); 2327 } 2328 Actions(String name)2329 public Actions(String name) { 2330 super(name); 2331 } 2332 actionPerformed(ActionEvent evt)2333 public void actionPerformed(ActionEvent evt) { 2334 JSlider slider = (JSlider)evt.getSource(); 2335 BasicSliderUI ui = (BasicSliderUI)BasicLookAndFeel.getUIOfType( 2336 slider.getUI(), BasicSliderUI.class); 2337 String name = getName(); 2338 2339 if (ui == null) { 2340 return; 2341 } 2342 if (POSITIVE_UNIT_INCREMENT == name) { 2343 scroll(slider, ui, POSITIVE_SCROLL, false); 2344 } else if (NEGATIVE_UNIT_INCREMENT == name) { 2345 scroll(slider, ui, NEGATIVE_SCROLL, false); 2346 } else if (POSITIVE_BLOCK_INCREMENT == name) { 2347 scroll(slider, ui, POSITIVE_SCROLL, true); 2348 } else if (NEGATIVE_BLOCK_INCREMENT == name) { 2349 scroll(slider, ui, NEGATIVE_SCROLL, true); 2350 } else if (MIN_SCROLL_INCREMENT == name) { 2351 scroll(slider, ui, MIN_SCROLL, false); 2352 } else if (MAX_SCROLL_INCREMENT == name) { 2353 scroll(slider, ui, MAX_SCROLL, false); 2354 } 2355 } 2356 scroll(JSlider slider, BasicSliderUI ui, int direction, boolean isBlock)2357 private void scroll(JSlider slider, BasicSliderUI ui, int direction, 2358 boolean isBlock) { 2359 boolean invert = slider.getInverted(); 2360 2361 if (direction == NEGATIVE_SCROLL || direction == POSITIVE_SCROLL) { 2362 if (invert) { 2363 direction = (direction == POSITIVE_SCROLL) ? 2364 NEGATIVE_SCROLL : POSITIVE_SCROLL; 2365 } 2366 2367 if (isBlock) { 2368 ui.scrollByBlock(direction); 2369 } else { 2370 ui.scrollByUnit(direction); 2371 } 2372 } else { // MIN or MAX 2373 if (invert) { 2374 direction = (direction == MIN_SCROLL) ? 2375 MAX_SCROLL : MIN_SCROLL; 2376 } 2377 2378 slider.setValue((direction == MIN_SCROLL) ? 2379 slider.getMinimum() : slider.getMaximum()); 2380 } 2381 } 2382 } 2383 } 2384