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