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