1 /*
2  * @(#)QuaquaSliderUI.java  2.0.1  2008-07-07
3  *
4  * Copyright (c) 2005-2008 Werner Randelshofer
5  * Staldenmattweg 2, Immensee, CH-6405, Switzerland.
6  * All rights reserved.
7  *
8  * The copyright of this software is owned by Werner Randelshofer.
9  * You may not use, copy or modify this software, except in
10  * accordance with the license agreement you entered into with
11  * Werner Randelshofer. For details see accompanying license terms.
12  */
13 package ch.randelshofer.quaqua;
14 
15 import ch.randelshofer.quaqua.util.Debug;
16 import ch.randelshofer.quaqua.util.InsetsUtil;
17 import java.awt.*;
18 import java.awt.event.*;
19 import java.beans.*;
20 import javax.swing.*;
21 import javax.swing.border.*;
22 import javax.swing.event.*;
23 import javax.swing.plaf.*;
24 import javax.swing.plaf.basic.*;
25 
26 /**
27  * QuaquaSliderUI.
28  *
29  * @author  Werner Randelshofer
30  * @version 2.0.1 2008-07-07 Determine whether to request focus on mouse pressed
31  * from the UIManager.
32  * <br>2.0 2007-12-02 Rewritten.
33  * <br>1.0 December 9, 2005 Created.
34  */
35 public class QuaquaSliderUI extends BasicSliderUI
36         implements VisuallyLayoutable {
37 
38     private Handler handler;
39     private transient boolean isDragging;
40 
createUI(JComponent b)41     public static ComponentUI createUI(JComponent b) {
42         return new QuaquaSliderUI((JSlider) b);
43     }
44 
QuaquaSliderUI(JSlider b)45     public QuaquaSliderUI(JSlider b) {
46         super(b);
47     }
48 
installUI(JComponent c)49     public void installUI(JComponent c) {
50         super.installUI(c);
51         QuaquaUtilities.installProperty(c, "opaque", UIManager.get("Slider.opaque"));
52     //slider.setOpaque(false);
53     }
54 
installDefaults(JSlider slider)55     public void installDefaults(JSlider slider) {
56         super.installDefaults(slider);
57 
58         focusInsets = getVisualMargin(slider);
59         slider.setRequestFocusEnabled(QuaquaManager.getBoolean("Slider.requestFocusEnabled"));
60     }
61 
uninstallListeners(JSlider slider)62     protected void uninstallListeners(JSlider slider) {
63         super.uninstallListeners(slider);
64         handler = null;
65     }
66 
createTrackListener(JSlider slider)67     protected TrackListener createTrackListener(JSlider slider) {
68         return new QuaquaTrackListener();
69     }
70 
createChangeListener(JSlider slider)71     protected ChangeListener createChangeListener(JSlider slider) {
72         return getHandler();
73     }
74 
createComponentListener(JSlider slider)75     protected ComponentListener createComponentListener(JSlider slider) {
76         return getHandler();
77     }
78 
createFocusListener(JSlider slider)79     protected FocusListener createFocusListener(JSlider slider) {
80         return getHandler();
81     }
82 
createScrollListener(JSlider slider)83     protected ScrollListener createScrollListener(JSlider slider) {
84         return new ScrollListener();
85     }
86 
createPropertyChangeListener( JSlider slider)87     protected PropertyChangeListener createPropertyChangeListener(
88             JSlider slider) {
89         return getHandler();
90     }
91 
getHandler()92     private Handler getHandler() {
93         if (handler == null) {
94             handler = new Handler();
95         }
96         return handler;
97     }
98 
getThumbSize()99     protected Dimension getThumbSize() {
100         Icon thumb = getThumbIcon();
101         return new Dimension(thumb.getIconWidth(), thumb.getIconHeight());
102     }
103 
isSmall()104     protected boolean isSmall() {
105             return QuaquaUtilities.isSmallSizeVariant(slider);
106     }
107 
getThumbIcon()108     protected Icon getThumbIcon() {
109         String suffix = isSmall() ? ".small" : "";
110         if (slider.getPaintTicks()) {
111             if (slider.getOrientation() == JSlider.HORIZONTAL) {
112                 if (QuaquaUtilities.isLeftToRight(slider)) {
113                     return UIManager.getIcon("Slider.southThumb" + suffix);
114                 } else {
115                     return UIManager.getIcon("Slider.northThumb" + suffix);
116                 }
117             } else {
118                 if (QuaquaUtilities.isLeftToRight(slider)) {
119                     return UIManager.getIcon("Slider.eastThumb" + suffix);
120                 } else {
121                     return UIManager.getIcon("Slider.westThumb" + suffix);
122                 }
123             }
124         } else {
125             return UIManager.getIcon("Slider.roundThumb" + suffix);
126         }
127     }
128 
paint(Graphics gr, JComponent c)129     public void paint(Graphics gr, JComponent c) {
130         Graphics2D g = (Graphics2D) gr;
131         Object oldHints = QuaquaUtilities.beginGraphics(g);
132         super.paint(g, c);
133         QuaquaUtilities.endGraphics(g, oldHints);
134         Debug.paint(g, c, this);
135     }
136 
paintThumb(Graphics g)137     public void paintThumb(Graphics g) {
138         Rectangle knobBounds = thumbRect;
139         int x = knobBounds.x;
140         int y = knobBounds.y;
141         getThumbIcon().paintIcon(slider, g, x, y);
142     }
143 
paintLabels(Graphics g)144     public void paintLabels(Graphics g) {
145         g.setColor(slider.getForeground());
146         super.paintLabels(g);
147     }
148 
paintFocus(Graphics g)149     public void paintFocus(Graphics g) {
150     // empty
151     }
152 
calculateGeometry()153     protected void calculateGeometry() {
154         focusInsets = getVisualMargin(slider);
155         super.calculateGeometry();
156     }
calculateContentRect()157     protected void calculateContentRect() {
158         contentRect.x = focusRect.x + focusInsets.left;
159         contentRect.y = focusRect.y + focusInsets.top;
160         contentRect.width = focusRect.width - (focusInsets.left + focusInsets.right);
161         contentRect.height = focusRect.height - (focusInsets.top + focusInsets.bottom);
162     }
163 
calculateThumbLocation()164     protected void calculateThumbLocation() {
165         if (slider.getSnapToTicks()) {
166             int sliderValue = slider.getValue();
167             int snappedValue = sliderValue;
168             int majorTickSpacing = slider.getMajorTickSpacing();
169             int minorTickSpacing = slider.getMinorTickSpacing();
170             int tickSpacing = 0;
171 
172             if (minorTickSpacing > 0) {
173                 tickSpacing = minorTickSpacing;
174             } else if (majorTickSpacing > 0) {
175                 tickSpacing = majorTickSpacing;
176             }
177 
178             if (tickSpacing != 0) {
179                 // If it's not on a tick, change the value
180                 if ((sliderValue - slider.getMinimum()) % tickSpacing != 0) {
181                     float temp = (float) (sliderValue - slider.getMinimum()) / (float) tickSpacing;
182                     int whichTick = Math.round(temp);
183                     snappedValue = slider.getMinimum() + (whichTick * tickSpacing);
184                 }
185 
186                 if (snappedValue != sliderValue) {
187                     slider.setValue(snappedValue);
188                 }
189             }
190         }
191 
192         if (slider.getOrientation() == JSlider.HORIZONTAL) {
193             int valuePosition = xPositionForValue(slider.getValue());
194 
195             thumbRect.x = valuePosition - (thumbRect.width / 2);
196             thumbRect.y = trackRect.y + (trackRect.height - thumbRect.height) / 2;
197         /*
198         if (slider.getPaintTicks()) {
199         if (QuaquaUtilities.isLeftToRight(slider)) {
200         thumbRect.y += 3;
201         } else {
202         thumbRect.y -= 3;
203         }
204         }*/
205         } else {
206             int valuePosition = yPositionForValue(slider.getValue());
207 
208             thumbRect.x = trackRect.x + (trackRect.width - thumbRect.width) / 2;
209             thumbRect.y = valuePosition - (thumbRect.height / 2);
210         /*
211         if (slider.getPaintTicks()) {
212         if (QuaquaUtilities.isLeftToRight(slider)) {
213         thumbRect.x += 3;
214         } else {
215         thumbRect.x -= 3;
216         }
217         }*/
218         }
219     }
220 
calculateLabelRect()221     protected void calculateLabelRect() {
222         if (slider.getPaintLabels()) {
223             if (slider.getOrientation() == JSlider.HORIZONTAL) {
224                 labelRect.x = tickRect.x - trackBuffer;
225                 labelRect.width = tickRect.width + (trackBuffer * 2);
226                 labelRect.height = getHeightOfTallestLabel();
227                 if (QuaquaUtilities.isLeftToRight(slider)) {
228                     labelRect.y = tickRect.y + tickRect.height;
229                 } else {
230                     labelRect.y = tickRect.y - labelRect.height;
231                 }
232             } else {
233                 labelRect.y = tickRect.y - trackBuffer;
234                 labelRect.height = tickRect.height + (trackBuffer * 2);
235                 labelRect.width = getWidthOfWidestLabel();
236                 if (QuaquaUtilities.isLeftToRight(slider)) {
237                     labelRect.x = tickRect.x + tickRect.width;
238                 } else {
239                     labelRect.x = tickRect.x - labelRect.width;
240                 }
241             }
242         } else {
243             if (slider.getOrientation() == JSlider.HORIZONTAL) {
244                 labelRect.x = tickRect.x;
245                 labelRect.y = tickRect.y + tickRect.height;
246                 labelRect.width = tickRect.width;
247                 labelRect.height = 0;
248             } else {
249                 if (QuaquaUtilities.isLeftToRight(slider)) {
250                     labelRect.x = tickRect.x + tickRect.width;
251                 } else {
252                     labelRect.x = tickRect.x;
253                 }
254                 labelRect.y = tickRect.y;
255                 labelRect.width = 0;
256                 labelRect.height = tickRect.height;
257             }
258         }
259     }
260 
calculateTickRect()261     protected void calculateTickRect() {
262         if (slider.getOrientation() == JSlider.HORIZONTAL) {
263             tickRect.x = trackRect.x;
264             tickRect.width = trackRect.width;
265             tickRect.height = getTickLength();
266 
267             if (QuaquaUtilities.isLeftToRight(slider)) {
268                 tickRect.y = trackRect.y + trackRect.height;
269             } else {
270                 tickRect.y = trackRect.y - tickRect.height;
271             }
272             if (!slider.getPaintTicks()) {
273                 --tickRect.y;
274                 tickRect.height = 0;
275             }
276         } else {
277             tickRect.y = trackRect.y;
278             tickRect.height = trackRect.height;
279             tickRect.width = getTickLength();
280             if (QuaquaUtilities.isLeftToRight(slider)) {
281                 tickRect.x = trackRect.x + trackRect.width;
282             } else {
283                 tickRect.x = trackRect.x - tickRect.width - 1;
284             }
285 
286             if (!slider.getPaintTicks()) {
287                 --tickRect.x;
288                 tickRect.width = 0;
289             }
290         }
291     }
292 
calculateTrackRect()293     protected void calculateTrackRect() {
294         int centerSpacing = 0; // used to center sliders added using BorderLayout.CENTER (bug 4275631)
295         if (slider.getOrientation() == JSlider.HORIZONTAL) {
296             centerSpacing = thumbRect.height;
297             if (QuaquaUtilities.isLeftToRight(slider)) {
298                 if (slider.getPaintTicks()) {
299                     centerSpacing += getTickLength();
300                 }
301                 if (slider.getPaintLabels()) {
302                     centerSpacing += getHeightOfTallestLabel();
303                 }
304             } else {
305                 if (slider.getPaintTicks()) {
306                     centerSpacing -= getTickLength();
307                 }
308                 if (slider.getPaintLabels()) {
309                     centerSpacing -= getHeightOfTallestLabel();
310                 }
311             }
312             trackRect.x = contentRect.x + trackBuffer;
313             trackRect.y = contentRect.y + (contentRect.height - centerSpacing - 1) / 2 + 1;
314             trackRect.width = contentRect.width - (trackBuffer * 2);
315             trackRect.height = thumbRect.height - 2;
316         } else {
317             centerSpacing = thumbRect.width;
318             if (QuaquaUtilities.isLeftToRight(slider)) {
319                 if (slider.getPaintTicks()) {
320                     centerSpacing += getTickLength();
321                 }
322                 if (slider.getPaintLabels()) {
323                     centerSpacing += getWidthOfWidestLabel();
324                 }
325             } else {
326                 if (slider.getPaintTicks()) {
327                     centerSpacing -= getTickLength();
328                 }
329                 if (slider.getPaintLabels()) {
330                     centerSpacing -= getWidthOfWidestLabel();
331                 }
332             }
333             trackRect.x = contentRect.x + (contentRect.width - centerSpacing - 1) / 2 + 1;
334             trackRect.y = contentRect.y + trackBuffer;
335             trackRect.width = thumbRect.width - 2;
336             trackRect.height = contentRect.height - (trackBuffer * 2);
337         }
338     }
339 
paintTrack(Graphics g)340     public void paintTrack(Graphics g) {
341         int cx, cy, cw, ch;
342         int pad;
343 
344         Rectangle trackBounds = trackRect;
345 
346         if (slider.getOrientation() == JSlider.HORIZONTAL) {
347             int index = slider.isEnabled() ? 0 : 1;
348             Border border = ((Border[]) UIManager.get("Slider.horizontalTracks"))[index];
349             Insets insets = border.getBorderInsets(slider);
350             int offset = 0;
351             if (slider.getPaintTicks()) {
352                 if (isSmall()) {
353                     offset = (QuaquaUtilities.isLeftToRight(slider)) ? -1 : 1;
354                 } else {
355                     offset = (QuaquaUtilities.isLeftToRight(slider)) ? -3 : 3;
356                 }
357             }
358             border.paintBorder(
359                     slider,
360                     g,
361                     trackBounds.x - thumbRect.width / 2 + 3,
362                     trackBounds.y + (trackBounds.height - insets.top - insets.bottom) / 2 + offset,
363                     trackBounds.width + thumbRect.width - 6,
364                     insets.top + insets.bottom);
365         } else {
366             int index = slider.isEnabled() ? 0 : 1;
367             Border border = ((Border[]) UIManager.get("Slider.verticalTracks"))[index];
368             Insets insets = border.getBorderInsets(slider);
369             int offset = 0;
370             if (slider.getPaintTicks()) {
371                 if (isSmall()) {
372                     offset = (QuaquaUtilities.isLeftToRight(slider)) ? -1 : 1;
373                 } else {
374                     offset = (QuaquaUtilities.isLeftToRight(slider)) ? -3 : 3;
375                 }
376             }
377 
378             int x = trackBounds.x + (trackBounds.width - insets.left - insets.right) / 2;
379             border.paintBorder(
380                     slider,
381                     g,
382                     x + offset,
383                     trackBounds.y - thumbRect.height / 2 + 3,
384                     insets.left + insets.right,
385                     trackBounds.height + thumbRect.height - 6);
386         }
387     }
388 
paintTicks(Graphics g)389     public void paintTicks(Graphics g) {
390         Rectangle tickBounds = tickRect;
391         int i;
392         int maj, min, max;
393         int w = tickBounds.width;
394         int h = tickBounds.height;
395         int centerEffect, tickHeight;
396 
397         g.setColor(UIManager.getColor("Slider.tickColor"));
398 
399         maj = slider.getMajorTickSpacing();
400         min = slider.getMinorTickSpacing();
401 
402         if (slider.getOrientation() == JSlider.HORIZONTAL) {
403             g.translate(0, tickBounds.y);
404 
405             int value = slider.getMinimum();
406             int xPos = 0;
407 
408             if (slider.getMinorTickSpacing() > 0) {
409                 int offset = 0;
410                 if (!QuaquaUtilities.isLeftToRight(slider)) {
411                     offset = tickBounds.height - tickBounds.height / 2;
412                     g.translate(0, offset);
413                 }
414                 while (value <= slider.getMaximum()) {
415                     xPos = xPositionForValue(value);
416                     paintMinorTickForHorizSlider(g, tickBounds, xPos);
417                     value += slider.getMinorTickSpacing();
418                 }
419                 if (!QuaquaUtilities.isLeftToRight(slider)) {
420                     g.translate(0, -offset);
421                 }
422             }
423 
424             if (slider.getMajorTickSpacing() > 0) {
425                 value = slider.getMinimum();
426                 if (!QuaquaUtilities.isLeftToRight(slider)) {
427                     g.translate(0, 1);
428                 }
429 
430                 while (value <= slider.getMaximum()) {
431                     xPos = xPositionForValue(value);
432                     paintMajorTickForHorizSlider(g, tickBounds, xPos);
433                     value += slider.getMajorTickSpacing();
434                 }
435                 if (!QuaquaUtilities.isLeftToRight(slider)) {
436                     g.translate(0, -1);
437                 }
438             }
439 
440             g.translate(0, -tickBounds.y);
441         } else {
442             g.translate(tickBounds.x, 0);
443 
444             int value = slider.getMinimum();
445             int yPos = 0;
446 
447             if (slider.getMinorTickSpacing() > 0) {
448                 int offset = 0;
449                 if (!QuaquaUtilities.isLeftToRight(slider)) {
450                     offset = tickBounds.width - tickBounds.width / 2;
451                     g.translate(offset, 0);
452                 }
453 
454                 while (value <= slider.getMaximum()) {
455                     yPos = yPositionForValue(value);
456                     paintMinorTickForVertSlider(g, tickBounds, yPos);
457                     value += slider.getMinorTickSpacing();
458                 }
459 
460                 if (!QuaquaUtilities.isLeftToRight(slider)) {
461                     g.translate(-offset, 0);
462                 }
463             }
464 
465             if (slider.getMajorTickSpacing() > 0) {
466                 value = slider.getMinimum();
467                 if (!QuaquaUtilities.isLeftToRight(slider)) {
468                     g.translate(2, 0);
469                 }
470 
471                 while (value <= slider.getMaximum()) {
472                     yPos = yPositionForValue(value);
473                     paintMajorTickForVertSlider(g, tickBounds, yPos);
474                     value += slider.getMajorTickSpacing();
475                 }
476 
477                 if (!QuaquaUtilities.isLeftToRight(slider)) {
478                     g.translate(-2, 0);
479                 }
480             }
481             g.translate(-tickBounds.x, 0);
482         }
483     }
484 
getInputMap(int condition, JSlider slider)485     InputMap getInputMap(int condition, JSlider slider) {
486         if (condition == JComponent.WHEN_FOCUSED) {
487             InputMap keyMap = (InputMap) UIManager.get(
488                     "Slider.focusInputMap");
489             InputMap rtlKeyMap;
490 
491             if (slider.getComponentOrientation().isLeftToRight() ||
492                     ((rtlKeyMap = (InputMap) UIManager.get(
493                     "Slider.focusInputMap.RightToLeft")) == null)) {
494                 return keyMap;
495             } else {
496                 rtlKeyMap.setParent(keyMap);
497                 return rtlKeyMap;
498             }
499         }
500         return null;
501     }
502 
getVisualMargin(JSlider tc)503     public Insets getVisualMargin(JSlider tc) {
504         Insets margin = (Insets) tc.getClientProperty("Quaqua.Component.visualMargin");
505         if (margin == null) {
506             margin = UIManager.getInsets("Component.visualMargin");
507         }
508         return (margin == null) ? new Insets(0, 0, 0, 0) : (Insets) margin.clone();
509     }
510 
getVisualBounds(JComponent c, int type, int width, int height)511     public Rectangle getVisualBounds(JComponent c, int type, int width, int height) {
512         Rectangle bounds = new Rectangle(0, 0, width, height);
513         if (type == VisuallyLayoutable.CLIP_BOUNDS) {
514             return bounds;
515         }
516 
517         JSlider b = (JSlider) c;
518         if (type == VisuallyLayoutable.COMPONENT_BOUNDS) {
519             Border border = b.getBorder();
520             if (border == null || border instanceof UIResource) {
521                 InsetsUtil.subtractInto(getVisualMargin(b), bounds);
522             }
523         }
524         return bounds;
525     }
526 
527     private class Handler implements ChangeListener,
528             ComponentListener, FocusListener, PropertyChangeListener {
529         // Change Handler
stateChanged(ChangeEvent e)530         public void stateChanged(ChangeEvent e) {
531             if (!isDragging) {
532                 calculateThumbLocation();
533                 slider.repaint();
534             }
535         }
536 
537         // Component Handler
componentHidden(ComponentEvent e)538         public void componentHidden(ComponentEvent e) {
539         }
540 
componentMoved(ComponentEvent e)541         public void componentMoved(ComponentEvent e) {
542         }
543 
componentResized(ComponentEvent e)544         public void componentResized(ComponentEvent e) {
545             calculateGeometry();
546             slider.repaint();
547         }
548 
componentShown(ComponentEvent e)549         public void componentShown(ComponentEvent e) {
550         }
551 
552         // Focus Handler
focusGained(FocusEvent e)553         public void focusGained(FocusEvent e) {
554             slider.repaint();
555         }
556 
focusLost(FocusEvent e)557         public void focusLost(FocusEvent e) {
558             slider.repaint();
559         }
560 
561         // Property Change Handler
propertyChange(PropertyChangeEvent e)562         public void propertyChange(PropertyChangeEvent e) {
563             String name = e.getPropertyName();
564             if (name == "orientation" ||
565                     name == "inverted" ||
566                     name == "labelTable" ||
567                     name == "majorTickSpacing" ||
568                     name == "minorTickSpacing" ||
569                     name == "paintTicks" ||
570                     name == "paintTrack" ||
571                     name == "paintLabels") {
572                 calculateGeometry();
573                 slider.repaint();
574             } else if (name == "componentOrientation") {
575                 calculateGeometry();
576                 slider.repaint();
577                 InputMap km = getInputMap(JComponent.WHEN_FOCUSED, slider);
578                 SwingUtilities.replaceUIInputMap(slider,
579                         JComponent.WHEN_FOCUSED, km);
580             } else if (name == "model") {
581                 ((BoundedRangeModel) e.getOldValue()).removeChangeListener(
582                         changeListener);
583                 ((BoundedRangeModel) e.getNewValue()).addChangeListener(
584                         changeListener);
585                 calculateThumbLocation();
586                 slider.repaint();
587             } else if (name == "Frame.active") {
588                 slider.repaint(thumbRect);
589        } else if (name.equals("JComponent.sizeVariant")) {
590             QuaquaUtilities.applySizeVariant(slider);
591             }
592         }
593     }
594 
595     /**
596      * Track mouse movements.
597      *
598      * This class should be treated as a &quot;protected&quot; inner class.
599      * Instantiate it only within subclasses of <Foo>.
600      */
601     public class QuaquaTrackListener extends TrackListener {
602         //protected transient int offset;
603         //protected transient int currentMouseX, currentMouseY;
mouseReleased(MouseEvent e)604         public void mouseReleased(MouseEvent e) {
605             if (!slider.isEnabled()) {
606                 return;
607             }
608 
609             offset = 0;
610             scrollTimer.stop();
611 
612             // This is the way we have to determine snap-to-ticks.  It's
613             // hard to explain but since ChangeEvents don't give us any
614             // idea what has changed we don't have a way to stop the thumb
615             // bounds from being recalculated.  Recalculating the thumb
616             // bounds moves the thumb over the current value (i.e., snapping
617             // to the ticks).
618             if (slider.getSnapToTicks() /*|| slider.getSnapToValue()*/) {
619                 isDragging = false;
620                 slider.setValueIsAdjusting(false);
621             } else {
622                 slider.setValueIsAdjusting(false);
623                 isDragging = false;
624             }
625             slider.repaint();
626         }
627 
628         /**
629          * If the mouse is pressed above the "thumb" component
630          * then reduce the scrollbars value by one page ("page up"),
631          * otherwise increase it by one page.  If there is no
632          * thumb then page up if the mouse is in the upper half
633          * of the track.
634          */
mousePressed(MouseEvent e)635         public void mousePressed(MouseEvent e) {
636             if (!slider.isEnabled()) {
637                 return;
638             }
639 
640             currentMouseX = e.getX();
641             currentMouseY = e.getY();
642 
643             if (slider.isRequestFocusEnabled()) {
644                 slider.requestFocus();
645             }
646 
647             // Clicked in the Thumb area?
648             if (thumbRect.contains(currentMouseX, currentMouseY)) {
649                 switch (slider.getOrientation()) {
650                     case JSlider.VERTICAL:
651                         offset = currentMouseY - thumbRect.y;
652                         break;
653                     case JSlider.HORIZONTAL:
654                         offset = currentMouseX - thumbRect.x;
655                         break;
656                 }
657                 isDragging = true;
658                 return;
659             }
660             isDragging = false;
661             slider.setValueIsAdjusting(true);
662 
663             Dimension sbSize = slider.getSize();
664             int direction = POSITIVE_SCROLL;
665 
666             switch (slider.getOrientation()) {
667                 case JSlider.VERTICAL:
668                     if (thumbRect.isEmpty()) {
669                         int scrollbarCenter = sbSize.height / 2;
670                         if (!drawInverted()) {
671                             direction = (currentMouseY < scrollbarCenter) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
672                         } else {
673                             direction = (currentMouseY < scrollbarCenter) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
674                         }
675                     } else {
676                         int thumbY = thumbRect.y;
677                         if (!drawInverted()) {
678                             direction = (currentMouseY < thumbY) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
679                         } else {
680                             direction = (currentMouseY < thumbY) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
681                         }
682                     }
683                     break;
684                 case JSlider.HORIZONTAL:
685                     if (thumbRect.isEmpty()) {
686                         int scrollbarCenter = sbSize.width / 2;
687                         if (!drawInverted()) {
688                             direction = (currentMouseX < scrollbarCenter) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
689                         } else {
690                             direction = (currentMouseX < scrollbarCenter) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
691                         }
692                     } else {
693                         int thumbX = thumbRect.x;
694                         if (!drawInverted()) {
695                             direction = (currentMouseX < thumbX) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
696                         } else {
697                             direction = (currentMouseX < thumbX) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
698                         }
699                     }
700                     break;
701             }
702             scrollDueToClickInTrack(direction);
703             Rectangle r = thumbRect;
704             if (!r.contains(currentMouseX, currentMouseY)) {
705                 if (shouldScroll(direction)) {
706                     scrollTimer.stop();
707                     scrollListener.setDirection(direction);
708                     scrollTimer.start();
709                 }
710             }
711         }
712 
shouldScroll(int direction)713         public boolean shouldScroll(int direction) {
714             Rectangle r = thumbRect;
715             if (slider.getOrientation() == JSlider.VERTICAL) {
716                 if (drawInverted() ? direction < 0 : direction > 0) {
717                     if (r.y + r.height <= currentMouseY) {
718                         return false;
719                     }
720                 } else if (r.y >= currentMouseY) {
721                     return false;
722                 }
723             } else {
724                 if (drawInverted() ? direction < 0 : direction > 0) {
725                     if (r.x + r.width >= currentMouseX) {
726                         return false;
727                     }
728                 } else if (r.x <= currentMouseX) {
729                     return false;
730                 }
731             }
732 
733             if (direction > 0 && slider.getValue() + slider.getExtent() >=
734                     slider.getMaximum()) {
735                 return false;
736             } else if (direction < 0 && slider.getValue() <=
737                     slider.getMinimum()) {
738                 return false;
739             }
740 
741             return true;
742         }
743 
744         /**
745          * Set the models value to the position of the top/left
746          * of the thumb relative to the origin of the track.
747          */
mouseDragged(MouseEvent e)748         public void mouseDragged(MouseEvent e) {
749             int thumbMiddle = 0;
750 
751             if (!slider.isEnabled()) {
752                 return;
753             }
754 
755             currentMouseX = e.getX();
756             currentMouseY = e.getY();
757 
758             if (!isDragging) {
759                 return;
760             }
761 
762             slider.setValueIsAdjusting(true);
763 
764             switch (slider.getOrientation()) {
765                 case JSlider.VERTICAL:
766                     int halfThumbHeight = thumbRect.height / 2;
767                     int thumbTop = e.getY() - offset;
768                     int trackTop = trackRect.y;
769                     int trackBottom = trackRect.y + (trackRect.height - 1);
770                     int vMax = yPositionForValue(slider.getMaximum() -
771                             slider.getExtent());
772 
773                     if (drawInverted()) {
774                         trackBottom = vMax;
775                     } else {
776                         trackTop = vMax;
777                     }
778                     thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight);
779                     thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight);
780 
781                     setThumbLocation(thumbRect.x, thumbTop);
782 
783                     thumbMiddle = thumbTop + halfThumbHeight;
784                     slider.setValue(valueForYPosition(thumbMiddle));
785                     break;
786                 case JSlider.HORIZONTAL:
787                     int halfThumbWidth = thumbRect.width / 2;
788                     int thumbLeft = e.getX() - offset;
789                     int trackLeft = trackRect.x;
790                     int trackRight = trackRect.x + (trackRect.width - 1);
791                     int hMax = xPositionForValue(slider.getMaximum() -
792                             slider.getExtent());
793 
794                     if (drawInverted()) {
795                         trackLeft = hMax;
796                     } else {
797                         trackRight = hMax;
798                     }
799                     thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
800                     thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);
801 
802                     setThumbLocation(thumbLeft, thumbRect.y);
803 
804                     thumbMiddle = thumbLeft + halfThumbWidth;
805                     slider.setValue(valueForXPosition(thumbMiddle));
806                     break;
807                 default:
808                     return;
809             }
810         }
811 
mouseMoved(MouseEvent e)812         public void mouseMoved(MouseEvent e) {
813         }
814     }
815 
getBaseline(JComponent c, int width, int height)816     public int getBaseline(JComponent c, int width, int height) {
817         return -1;
818     }
819 }
820