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