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
43
44
45 /**
46 * Implementation of ScrollBarUI for the Basic Look and Feel
47 *
48 * @author Rich Schiavi
49 * @author David Kloba
50 * @author Hans Muller
51 */
52 public class BasicScrollBarUI
53 extends ScrollBarUI implements LayoutManager, SwingConstants
54 {
55 private static final int POSITIVE_SCROLL = 1;
56 private static final int NEGATIVE_SCROLL = -1;
57
58 private static final int MIN_SCROLL = 2;
59 private static final int MAX_SCROLL = 3;
60
61 // NOTE: DO NOT use this field directly, SynthScrollBarUI assumes you'll
62 // call getMinimumThumbSize to access it.
63 protected Dimension minimumThumbSize;
64 protected Dimension maximumThumbSize;
65
66 protected Color thumbHighlightColor;
67 protected Color thumbLightShadowColor;
68 protected Color thumbDarkShadowColor;
69 protected Color thumbColor;
70 protected Color trackColor;
71 protected Color trackHighlightColor;
72
73 protected JScrollBar scrollbar;
74 protected JButton incrButton;
75 protected JButton decrButton;
76 protected boolean isDragging;
77 protected TrackListener trackListener;
78 protected ArrowButtonListener buttonListener;
79 protected ModelListener modelListener;
80
81 protected Rectangle thumbRect;
82 protected Rectangle trackRect;
83
84 protected int trackHighlight;
85
86 protected static final int NO_HIGHLIGHT = 0;
87 protected static final int DECREASE_HIGHLIGHT = 1;
88 protected static final int INCREASE_HIGHLIGHT = 2;
89
90 protected ScrollListener scrollListener;
91 protected PropertyChangeListener propertyChangeListener;
92 protected Timer scrollTimer;
93
94 private final static int scrollSpeedThrottle = 60; // delay in milli seconds
95
96 /**
97 * True indicates a middle click will absolutely position the
98 * scrollbar.
99 */
100 private boolean supportsAbsolutePositioning;
101
102 /**
103 * Hint as to what width (when vertical) or height (when horizontal)
104 * should be.
105 *
106 * @since 1.7
107 */
108 protected int scrollBarWidth;
109
110 private Handler handler;
111
131 protected int incrGap;
132
133 /**
134 * Distance between the decrement button and the track. This may be a negative
135 * number. If negative, then an overlap between the button and track will occur,
136 * which is useful for shaped buttons.
137 *
138 * @since 1.7
139 */
140 protected int decrGap;
141
142 static void loadActionMap(LazyActionMap map) {
143 map.put(new Actions(Actions.POSITIVE_UNIT_INCREMENT));
144 map.put(new Actions(Actions.POSITIVE_BLOCK_INCREMENT));
145 map.put(new Actions(Actions.NEGATIVE_UNIT_INCREMENT));
146 map.put(new Actions(Actions.NEGATIVE_BLOCK_INCREMENT));
147 map.put(new Actions(Actions.MIN_SCROLL));
148 map.put(new Actions(Actions.MAX_SCROLL));
149 }
150
151
152 public static ComponentUI createUI(JComponent c) {
153 return new BasicScrollBarUI();
154 }
155
156
157 protected void configureScrollBarColors()
158 {
159 LookAndFeel.installColors(scrollbar, "ScrollBar.background",
160 "ScrollBar.foreground");
161 thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
162 thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow");
163 thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow");
164 thumbColor = UIManager.getColor("ScrollBar.thumb");
165 trackColor = UIManager.getColor("ScrollBar.track");
166 trackHighlightColor = UIManager.getColor("ScrollBar.trackHighlight");
167 }
168
169
170 public void installUI(JComponent c) {
171 scrollbar = (JScrollBar)c;
172 thumbRect = new Rectangle(0, 0, 0, 0);
173 trackRect = new Rectangle(0, 0, 0, 0);
174 installDefaults();
175 installComponents();
176 installListeners();
177 installKeyboardActions();
178 }
179
180 public void uninstallUI(JComponent c) {
181 scrollbar = (JScrollBar)c;
182 uninstallListeners();
183 uninstallDefaults();
184 uninstallComponents();
185 uninstallKeyboardActions();
186 thumbRect = null;
187 scrollbar = null;
188 incrButton = null;
189 decrButton = null;
190 }
191
192
193 protected void installDefaults()
194 {
195 scrollBarWidth = UIManager.getInt("ScrollBar.width");
196 if (scrollBarWidth <= 0) {
197 scrollBarWidth = 16;
198 }
199 minimumThumbSize = (Dimension)UIManager.get("ScrollBar.minimumThumbSize");
200 maximumThumbSize = (Dimension)UIManager.get("ScrollBar.maximumThumbSize");
201
202 Boolean absB = (Boolean)UIManager.get("ScrollBar.allowsAbsolutePositioning");
203 supportsAbsolutePositioning = (absB != null) ? absB.booleanValue() :
204 false;
205
206 trackHighlight = NO_HIGHLIGHT;
207 if (scrollbar.getLayout() == null ||
208 (scrollbar.getLayout() instanceof UIResource)) {
209 scrollbar.setLayout(this);
210 }
211 configureScrollBarColors();
212 LookAndFeel.installBorder(scrollbar, "ScrollBar.border");
223 // components are based on Apples LAF
224 String scaleKey = (String)scrollbar.getClientProperty(
225 "JComponent.sizeVariant");
226 if (scaleKey != null){
227 if ("large".equals(scaleKey)){
228 scrollBarWidth *= 1.15;
229 incrGap *= 1.15;
230 decrGap *= 1.15;
231 } else if ("small".equals(scaleKey)){
232 scrollBarWidth *= 0.857;
233 incrGap *= 0.857;
234 decrGap *= 0.714;
235 } else if ("mini".equals(scaleKey)){
236 scrollBarWidth *= 0.714;
237 incrGap *= 0.714;
238 decrGap *= 0.714;
239 }
240 }
241 }
242
243
244 protected void installComponents(){
245 switch (scrollbar.getOrientation()) {
246 case JScrollBar.VERTICAL:
247 incrButton = createIncreaseButton(SOUTH);
248 decrButton = createDecreaseButton(NORTH);
249 break;
250
251 case JScrollBar.HORIZONTAL:
252 if (scrollbar.getComponentOrientation().isLeftToRight()) {
253 incrButton = createIncreaseButton(EAST);
254 decrButton = createDecreaseButton(WEST);
255 } else {
256 incrButton = createIncreaseButton(WEST);
257 decrButton = createDecreaseButton(EAST);
258 }
259 break;
260 }
261 scrollbar.add(incrButton);
262 scrollbar.add(decrButton);
263 // Force the children's enabled state to be updated.
264 scrollbar.setEnabled(scrollbar.isEnabled());
265 }
266
267 protected void uninstallComponents(){
268 scrollbar.remove(incrButton);
269 scrollbar.remove(decrButton);
270 }
271
272
273 protected void installListeners(){
274 trackListener = createTrackListener();
275 buttonListener = createArrowButtonListener();
276 modelListener = createModelListener();
277 propertyChangeListener = createPropertyChangeListener();
278
279 scrollbar.addMouseListener(trackListener);
280 scrollbar.addMouseMotionListener(trackListener);
281 scrollbar.getModel().addChangeListener(modelListener);
282 scrollbar.addPropertyChangeListener(propertyChangeListener);
283 scrollbar.addFocusListener(getHandler());
284
285 if (incrButton != null) {
286 incrButton.addMouseListener(buttonListener);
287 }
288 if (decrButton != null) {
289 decrButton.addMouseListener(buttonListener);
290 }
291
292 scrollListener = createScrollListener();
293 scrollTimer = new Timer(scrollSpeedThrottle, scrollListener);
294 scrollTimer.setInitialDelay(300); // default InitialDelay?
295 }
296
297
298 protected void installKeyboardActions(){
299 LazyActionMap.installLazyActionMap(scrollbar, BasicScrollBarUI.class,
300 "ScrollBar.actionMap");
301
302 InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
303 SwingUtilities.replaceUIInputMap(scrollbar, JComponent.WHEN_FOCUSED,
304 inputMap);
305 inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
306 SwingUtilities.replaceUIInputMap(scrollbar,
307 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
308 }
309
310 protected void uninstallKeyboardActions(){
311 SwingUtilities.replaceUIInputMap(scrollbar, JComponent.WHEN_FOCUSED,
312 null);
313 SwingUtilities.replaceUIActionMap(scrollbar, null);
314 }
315
316 private InputMap getInputMap(int condition) {
317 if (condition == JComponent.WHEN_FOCUSED) {
318 InputMap keyMap = (InputMap)DefaultLookup.get(
319 scrollbar, this, "ScrollBar.focusInputMap");
320 InputMap rtlKeyMap;
321
322 if (scrollbar.getComponentOrientation().isLeftToRight() ||
323 ((rtlKeyMap = (InputMap)DefaultLookup.get(scrollbar, this, "ScrollBar.focusInputMap.RightToLeft")) == null)) {
324 return keyMap;
325 } else {
326 rtlKeyMap.setParent(keyMap);
327 return rtlKeyMap;
328 }
329 }
330 else if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
331 InputMap keyMap = (InputMap)DefaultLookup.get(
332 scrollbar, this, "ScrollBar.ancestorInputMap");
333 InputMap rtlKeyMap;
334
335 if (scrollbar.getComponentOrientation().isLeftToRight() ||
336 ((rtlKeyMap = (InputMap)DefaultLookup.get(scrollbar, this, "ScrollBar.ancestorInputMap.RightToLeft")) == null)) {
337 return keyMap;
338 } else {
339 rtlKeyMap.setParent(keyMap);
340 return rtlKeyMap;
341 }
342 }
343 return null;
344 }
345
346
347 protected void uninstallListeners() {
348 scrollTimer.stop();
349 scrollTimer = null;
350
351 if (decrButton != null){
352 decrButton.removeMouseListener(buttonListener);
353 }
354 if (incrButton != null){
355 incrButton.removeMouseListener(buttonListener);
356 }
357
358 scrollbar.getModel().removeChangeListener(modelListener);
359 scrollbar.removeMouseListener(trackListener);
360 scrollbar.removeMouseMotionListener(trackListener);
361 scrollbar.removePropertyChangeListener(propertyChangeListener);
362 scrollbar.removeFocusListener(getHandler());
363 handler = null;
364 }
365
366
367 protected void uninstallDefaults(){
368 LookAndFeel.uninstallBorder(scrollbar);
369 if (scrollbar.getLayout() == this) {
370 scrollbar.setLayout(null);
371 }
372 }
373
374
375 private Handler getHandler() {
376 if (handler == null) {
377 handler = new Handler();
378 }
379 return handler;
380 }
381
382 protected TrackListener createTrackListener(){
383 return new TrackListener();
384 }
385
386 protected ArrowButtonListener createArrowButtonListener(){
387 return new ArrowButtonListener();
388 }
389
390 protected ModelListener createModelListener(){
391 return new ModelListener();
392 }
393
394 protected ScrollListener createScrollListener(){
395 return new ScrollListener();
396 }
397
398 protected PropertyChangeListener createPropertyChangeListener() {
399 return getHandler();
400 }
401
402 private void updateThumbState(int x, int y) {
403 Rectangle rect = getThumbBounds();
404
405 setThumbRollover(rect.contains(x, y));
406 }
407
408 /**
409 * Sets whether or not the mouse is currently over the thumb.
410 *
411 * @param active True indicates the thumb is currently active.
412 * @since 1.5
413 */
414 protected void setThumbRollover(boolean active) {
415 if (thumbActive != active) {
416 thumbActive = active;
417 scrollbar.repaint(getThumbBounds());
453 * @see #getMaximumSize
454 * @see #getMinimumSize
455 */
456 public Dimension getPreferredSize(JComponent c) {
457 return (scrollbar.getOrientation() == JScrollBar.VERTICAL)
458 ? new Dimension(scrollBarWidth, 48)
459 : new Dimension(48, scrollBarWidth);
460 }
461
462
463 /**
464 * @param c The JScrollBar that's delegating this method to us.
465 * @return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
466 * @see #getMinimumSize
467 * @see #getPreferredSize
468 */
469 public Dimension getMaximumSize(JComponent c) {
470 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
471 }
472
473 protected JButton createDecreaseButton(int orientation) {
474 return new BasicArrowButton(orientation,
475 UIManager.getColor("ScrollBar.thumb"),
476 UIManager.getColor("ScrollBar.thumbShadow"),
477 UIManager.getColor("ScrollBar.thumbDarkShadow"),
478 UIManager.getColor("ScrollBar.thumbHighlight"));
479 }
480
481 protected JButton createIncreaseButton(int orientation) {
482 return new BasicArrowButton(orientation,
483 UIManager.getColor("ScrollBar.thumb"),
484 UIManager.getColor("ScrollBar.thumbShadow"),
485 UIManager.getColor("ScrollBar.thumbDarkShadow"),
486 UIManager.getColor("ScrollBar.thumbHighlight"));
487 }
488
489
490 protected void paintDecreaseHighlight(Graphics g)
491 {
492 Insets insets = scrollbar.getInsets();
493 Rectangle thumbR = getThumbBounds();
494 g.setColor(trackHighlightColor);
495
496 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
497 //paint the distance between the start of the track and top of the thumb
498 int x = insets.left;
499 int y = trackRect.y;
500 int w = scrollbar.getWidth() - (insets.left + insets.right);
501 int h = thumbR.y - y;
502 g.fillRect(x, y, w, h);
503 } else {
504 //if left-to-right, fill the area between the start of the track and
505 //the left edge of the thumb. If right-to-left, fill the area between
506 //the end of the thumb and end of the track.
507 int x, w;
508 if (scrollbar.getComponentOrientation().isLeftToRight()) {
509 x = trackRect.x;
510 w = thumbR.x - x;
511 } else {
512 x = thumbR.x + thumbR.width;
513 w = trackRect.x + trackRect.width - x;
514 }
515 int y = insets.top;
516 int h = scrollbar.getHeight() - (insets.top + insets.bottom);
517 g.fillRect(x, y, w, h);
518 }
519 }
520
521
522 protected void paintIncreaseHighlight(Graphics g)
523 {
524 Insets insets = scrollbar.getInsets();
525 Rectangle thumbR = getThumbBounds();
526 g.setColor(trackHighlightColor);
527
528 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
529 //fill the area between the bottom of the thumb and the end of the track.
530 int x = insets.left;
531 int y = thumbR.y + thumbR.height;
532 int w = scrollbar.getWidth() - (insets.left + insets.right);
533 int h = trackRect.y + trackRect.height - y;
534 g.fillRect(x, y, w, h);
535 }
536 else {
537 //if left-to-right, fill the area between the right of the thumb and the
538 //end of the track. If right-to-left, then fill the area to the left of
539 //the thumb and the start of the track.
540 int x, w;
541 if (scrollbar.getComponentOrientation().isLeftToRight()) {
542 x = thumbR.x + thumbR.width;
543 w = trackRect.x + trackRect.width - x;
544 } else {
545 x = trackRect.x;
546 w = thumbR.x - x;
547 }
548 int y = insets.top;
549 int h = scrollbar.getHeight() - (insets.top + insets.bottom);
550 g.fillRect(x, y, w, h);
551 }
552 }
553
554
555 protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
556 {
557 g.setColor(trackColor);
558 g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height);
559
560 if(trackHighlight == DECREASE_HIGHLIGHT) {
561 paintDecreaseHighlight(g);
562 }
563 else if(trackHighlight == INCREASE_HIGHLIGHT) {
564 paintIncreaseHighlight(g);
565 }
566 }
567
568
569 protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
570 {
571 if(thumbBounds.isEmpty() || !scrollbar.isEnabled()) {
572 return;
573 }
574
575 int w = thumbBounds.width;
576 int h = thumbBounds.height;
577
578 g.translate(thumbBounds.x, thumbBounds.y);
579
580 g.setColor(thumbDarkShadowColor);
581 drawRect(g, 0, 0, w - 1, h - 1);
582 g.setColor(thumbColor);
583 g.fillRect(0, 0, w - 1, h - 1);
584
585 g.setColor(thumbHighlightColor);
586 drawVLine(g, 1, 1, h - 2);
587 drawHLine(g, 2, w - 3, 1);
588
627
628 /*
629 * LayoutManager Implementation
630 */
631
632 public void addLayoutComponent(String name, Component child) {}
633 public void removeLayoutComponent(Component child) {}
634
635 public Dimension preferredLayoutSize(Container scrollbarContainer) {
636 return getPreferredSize((JComponent)scrollbarContainer);
637 }
638
639 public Dimension minimumLayoutSize(Container scrollbarContainer) {
640 return getMinimumSize((JComponent)scrollbarContainer);
641 }
642
643 private int getValue(JScrollBar sb) {
644 return (useCachedValue) ? scrollBarValue : sb.getValue();
645 }
646
647 protected void layoutVScrollbar(JScrollBar sb)
648 {
649 Dimension sbSize = sb.getSize();
650 Insets sbInsets = sb.getInsets();
651
652 /*
653 * Width and left edge of the buttons and thumb.
654 */
655 int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
656 int itemX = sbInsets.left;
657
658 /* Nominal locations of the buttons, assuming their preferred
659 * size will fit.
660 */
661 boolean squareButtons = DefaultLookup.getBoolean(
662 scrollbar, this, "ScrollBar.squareButtons", false);
663 int decrButtonH = squareButtons ? itemW :
664 decrButton.getPreferredSize().height;
665 int decrButtonY = sbInsets.top;
666
725 if (UIManager.getBoolean("ScrollBar.alwaysShowThumb")) {
726 // This is used primarily for GTK L&F, which expands the
727 // thumb to fit the track when it would otherwise be hidden.
728 setThumbBounds(itemX, itrackY, itemW, itrackH);
729 } else {
730 // Other L&F's simply hide the thumb in this case.
731 setThumbBounds(0, 0, 0, 0);
732 }
733 }
734 else {
735 if ((thumbY + thumbH) > incrButtonY - incrGap) {
736 thumbY = incrButtonY - incrGap - thumbH;
737 }
738 if (thumbY < (decrButtonY + decrButtonH + decrGap)) {
739 thumbY = decrButtonY + decrButtonH + decrGap + 1;
740 }
741 setThumbBounds(itemX, thumbY, itemW, thumbH);
742 }
743 }
744
745
746 protected void layoutHScrollbar(JScrollBar sb)
747 {
748 Dimension sbSize = sb.getSize();
749 Insets sbInsets = sb.getInsets();
750
751 /* Height and top edge of the buttons and thumb.
752 */
753 int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);
754 int itemY = sbInsets.top;
755
756 boolean ltr = sb.getComponentOrientation().isLeftToRight();
757
758 /* Nominal locations of the buttons, assuming their preferred
759 * size will fit.
760 */
761 boolean squareButtons = DefaultLookup.getBoolean(
762 scrollbar, this, "ScrollBar.squareButtons", false);
763 int leftButtonW = squareButtons ? itemH :
764 decrButton.getPreferredSize().width;
765 int rightButtonW = squareButtons ? itemH :
949 */
950 static void scrollByBlock(JScrollBar scrollbar, int direction) {
951 // This method is called from BasicScrollPaneUI to implement wheel
952 // scrolling, and also from scrollByBlock().
953 int oldValue = scrollbar.getValue();
954 int blockIncrement = scrollbar.getBlockIncrement(direction);
955 int delta = blockIncrement * ((direction > 0) ? +1 : -1);
956 int newValue = oldValue + delta;
957
958 // Check for overflow.
959 if (delta > 0 && newValue < oldValue) {
960 newValue = scrollbar.getMaximum();
961 }
962 else if (delta < 0 && newValue > oldValue) {
963 newValue = scrollbar.getMinimum();
964 }
965
966 scrollbar.setValue(newValue);
967 }
968
969 protected void scrollByBlock(int direction)
970 {
971 scrollByBlock(scrollbar, direction);
972 trackHighlight = direction > 0 ? INCREASE_HIGHLIGHT : DECREASE_HIGHLIGHT;
973 Rectangle dirtyRect = getTrackBounds();
974 scrollbar.repaint(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
975 }
976
977 /*
978 * Method for scrolling by a unit increment.
979 * Added for mouse wheel scrolling support, RFE 4202656.
980 *
981 * If limitByBlock is set to true, the scrollbar will scroll at least 1
982 * unit increment, but will not scroll farther than the block increment.
983 * See BasicScrollPaneUI.Handler.mouseWheelMoved().
984 */
985 static void scrollByUnits(JScrollBar scrollbar, int direction,
986 int units, boolean limitToBlock) {
987 // This method is called from BasicScrollPaneUI to implement wheel
988 // scrolling, as well as from scrollByUnit().
1016 newValue = scrollbar.getMaximum();
1017 }
1018 else if (delta < 0 && newValue > oldValue) {
1019 newValue = scrollbar.getMinimum();
1020 }
1021 if (oldValue == newValue) {
1022 break;
1023 }
1024
1025 if (limitToBlock && i > 0) {
1026 assert limit != -1;
1027 if ((direction < 0 && newValue < limit) ||
1028 (direction > 0 && newValue > limit)) {
1029 break;
1030 }
1031 }
1032 scrollbar.setValue(newValue);
1033 }
1034 }
1035
1036 protected void scrollByUnit(int direction) {
1037 scrollByUnits(scrollbar, direction, 1, false);
1038 }
1039
1040 /**
1041 * Indicates whether the user can absolutely position the thumb with
1042 * a mouse gesture (usually the middle mouse button).
1043 *
1044 * @return true if a mouse gesture can absolutely position the thumb
1045 * @since 1.5
1046 */
1047 public boolean getSupportsAbsolutePositioning() {
1048 return supportsAbsolutePositioning;
1049 }
1050
1051 /**
1052 * A listener to listen for model changes.
1053 *
1054 */
1055 protected class ModelListener implements ChangeListener {
1056 public void stateChanged(ChangeEvent e) {
1057 if (!useCachedValue) {
1058 scrollBarValue = scrollbar.getValue();
1059 }
1060 layoutContainer(scrollbar);
1061 useCachedValue = false;
1062 }
1063 }
1064
1065
1066 /**
1067 * Track mouse drags.
1068 */
1069 protected class TrackListener
1070 extends MouseAdapter implements MouseMotionListener
1071 {
1072 protected transient int offset;
1073 protected transient int currentMouseX, currentMouseY;
1074 private transient int direction = +1;
1075
1076 public void mouseReleased(MouseEvent e)
1077 {
1078 if (isDragging) {
1079 updateThumbState(e.getX(), e.getY());
1080 }
1081 if (SwingUtilities.isRightMouseButton(e) ||
1082 (!getSupportsAbsolutePositioning() &&
1083 SwingUtilities.isMiddleMouseButton(e)))
1084 return;
1085 if(!scrollbar.isEnabled())
1086 return;
1087
1088 Rectangle r = getTrackBounds();
1089 scrollbar.repaint(r.x, r.y, r.width, r.height);
1090
1091 trackHighlight = NO_HIGHLIGHT;
1092 isDragging = false;
1093 offset = 0;
1094 scrollTimer.stop();
1095 useCachedValue = true;
1322 switch (scrollbar.getOrientation()) {
1323 case JScrollBar.VERTICAL:
1324 if (direction > 0) {
1325 if (tb.y + tb.height < trackListener.currentMouseY) {
1326 scrollTimer.start();
1327 }
1328 } else if (tb.y > trackListener.currentMouseY) {
1329 scrollTimer.start();
1330 }
1331 break;
1332 case JScrollBar.HORIZONTAL:
1333 if ((direction > 0 && isMouseAfterThumb())
1334 || (direction < 0 && isMouseBeforeThumb())) {
1335
1336 scrollTimer.start();
1337 }
1338 break;
1339 }
1340 }
1341
1342 public void mouseMoved(MouseEvent e) {
1343 if (!isDragging) {
1344 updateThumbState(e.getX(), e.getY());
1345 }
1346 }
1347
1348 /**
1349 * Invoked when the mouse exits the scrollbar.
1350 *
1351 * @param e MouseEvent further describing the event
1352 * @since 1.5
1353 */
1354 public void mouseExited(MouseEvent e) {
1355 if (!isDragging) {
1356 setThumbRollover(false);
1357 }
1358 }
1359 }
1360
1361
1389 }
1390 }
1391
1392 public void mouseReleased(MouseEvent e) {
1393 scrollTimer.stop();
1394 handledEvent = false;
1395 scrollbar.setValueIsAdjusting(false);
1396 }
1397 }
1398
1399
1400 /**
1401 * Listener for scrolling events initiated in the
1402 * <code>ScrollPane</code>.
1403 */
1404 protected class ScrollListener implements ActionListener
1405 {
1406 int direction = +1;
1407 boolean useBlockIncrement;
1408
1409 public ScrollListener() {
1410 direction = +1;
1411 useBlockIncrement = false;
1412 }
1413
1414 public ScrollListener(int dir, boolean block) {
1415 direction = dir;
1416 useBlockIncrement = block;
1417 }
1418
1419 public void setDirection(int direction) { this.direction = direction; }
1420 public void setScrollByBlock(boolean block) { this.useBlockIncrement = block; }
1421
1422 public void actionPerformed(ActionEvent e) {
1423 if(useBlockIncrement) {
1424 scrollByBlock(direction);
1425 // Stop scrolling if the thumb catches up with the mouse
1426 if(scrollbar.getOrientation() == JScrollBar.VERTICAL) {
1427 if(direction > 0) {
1428 if(getThumbBounds().y + getThumbBounds().height
1429 >= trackListener.currentMouseY)
1430 ((Timer)e.getSource()).stop();
1431 } else if(getThumbBounds().y <= trackListener.currentMouseY) {
1432 ((Timer)e.getSource()).stop();
1433 }
1434 } else {
1435 if ((direction > 0 && !isMouseAfterThumb())
1436 || (direction < 0 && !isMouseBeforeThumb())) {
1437
1438 ((Timer)e.getSource()).stop();
1439 }
1440 }
1441 } else {
1480 ((BasicArrowButton)incrButton).setDirection(
1481 orient == HORIZONTAL? EAST : SOUTH);
1482 }
1483 if (decrButton instanceof BasicArrowButton) {
1484 ((BasicArrowButton)decrButton).setDirection(
1485 orient == HORIZONTAL? WEST : NORTH);
1486 }
1487 }
1488 else {
1489 if (incrButton instanceof BasicArrowButton) {
1490 ((BasicArrowButton)incrButton).setDirection(
1491 orient == HORIZONTAL? WEST : SOUTH);
1492 }
1493 if (decrButton instanceof BasicArrowButton) {
1494 ((BasicArrowButton)decrButton).setDirection(
1495 orient == HORIZONTAL ? EAST : NORTH);
1496 }
1497 }
1498 }
1499
1500 public class PropertyChangeHandler implements PropertyChangeListener
1501 {
1502 // NOTE: This class exists only for backward compatibility. All
1503 // its functionality has been moved into Handler. If you need to add
1504 // new functionality add it to the Handler, but make sure this
1505 // class calls into the Handler.
1506
1507 public void propertyChange(PropertyChangeEvent e) {
1508 getHandler().propertyChange(e);
1509 }
1510 }
1511
1512
1513 /**
1514 * Used for scrolling the scrollbar.
1515 */
1516 private static class Actions extends UIAction {
1517 private static final String POSITIVE_UNIT_INCREMENT =
1518 "positiveUnitIncrement";
1519 private static final String POSITIVE_BLOCK_INCREMENT =
1520 "positiveBlockIncrement";
1521 private static final String NEGATIVE_UNIT_INCREMENT =
1522 "negativeUnitIncrement";
1523 private static final String NEGATIVE_BLOCK_INCREMENT =
1524 "negativeBlockIncrement";
1525 private static final String MIN_SCROLL = "minScroll";
1526 private static final String MAX_SCROLL = "maxScroll";
|
1 /*
2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
43
44
45 /**
46 * Implementation of ScrollBarUI for the Basic Look and Feel
47 *
48 * @author Rich Schiavi
49 * @author David Kloba
50 * @author Hans Muller
51 */
52 public class BasicScrollBarUI
53 extends ScrollBarUI implements LayoutManager, SwingConstants
54 {
55 private static final int POSITIVE_SCROLL = 1;
56 private static final int NEGATIVE_SCROLL = -1;
57
58 private static final int MIN_SCROLL = 2;
59 private static final int MAX_SCROLL = 3;
60
61 // NOTE: DO NOT use this field directly, SynthScrollBarUI assumes you'll
62 // call getMinimumThumbSize to access it.
63 /** Minimum thumb size */
64 protected Dimension minimumThumbSize;
65 /** Maximum thumb size */
66 protected Dimension maximumThumbSize;
67
68 /** Thumb highlight color */
69 protected Color thumbHighlightColor;
70 /** Thumb light shadow color */
71 protected Color thumbLightShadowColor;
72 /** Thumb dark shadow color */
73 protected Color thumbDarkShadowColor;
74 /** Thumb color */
75 protected Color thumbColor;
76 /** Track color */
77 protected Color trackColor;
78 /** Track highlight color */
79 protected Color trackHighlightColor;
80
81 /** Scrollbar */
82 protected JScrollBar scrollbar;
83 /** Increment button */
84 protected JButton incrButton;
85 /** Decrement button */
86 protected JButton decrButton;
87 /** Dragging */
88 protected boolean isDragging;
89 /** Track listener */
90 protected TrackListener trackListener;
91 /** Button listener */
92 protected ArrowButtonListener buttonListener;
93 /** Model listener */
94 protected ModelListener modelListener;
95
96 /** Thumb rectangle */
97 protected Rectangle thumbRect;
98 /** Track rectangle */
99 protected Rectangle trackRect;
100
101 /** Track highlight */
102 protected int trackHighlight;
103
104 /** No highlight */
105 protected static final int NO_HIGHLIGHT = 0;
106 /** Decrease highlight */
107 protected static final int DECREASE_HIGHLIGHT = 1;
108 /** Increase highlight */
109 protected static final int INCREASE_HIGHLIGHT = 2;
110
111 /** Scroll listener */
112 protected ScrollListener scrollListener;
113 /** Property change listener */
114 protected PropertyChangeListener propertyChangeListener;
115 /** Scroll timer */
116 protected Timer scrollTimer;
117
118 private final static int scrollSpeedThrottle = 60; // delay in milli seconds
119
120 /**
121 * True indicates a middle click will absolutely position the
122 * scrollbar.
123 */
124 private boolean supportsAbsolutePositioning;
125
126 /**
127 * Hint as to what width (when vertical) or height (when horizontal)
128 * should be.
129 *
130 * @since 1.7
131 */
132 protected int scrollBarWidth;
133
134 private Handler handler;
135
155 protected int incrGap;
156
157 /**
158 * Distance between the decrement button and the track. This may be a negative
159 * number. If negative, then an overlap between the button and track will occur,
160 * which is useful for shaped buttons.
161 *
162 * @since 1.7
163 */
164 protected int decrGap;
165
166 static void loadActionMap(LazyActionMap map) {
167 map.put(new Actions(Actions.POSITIVE_UNIT_INCREMENT));
168 map.put(new Actions(Actions.POSITIVE_BLOCK_INCREMENT));
169 map.put(new Actions(Actions.NEGATIVE_UNIT_INCREMENT));
170 map.put(new Actions(Actions.NEGATIVE_BLOCK_INCREMENT));
171 map.put(new Actions(Actions.MIN_SCROLL));
172 map.put(new Actions(Actions.MAX_SCROLL));
173 }
174
175 /**
176 * Creates the UI.
177 * @param c the component
178 * @return the UI
179 */
180 public static ComponentUI createUI(JComponent c) {
181 return new BasicScrollBarUI();
182 }
183
184 /**
185 * Configures the scroll bar colors.
186 */
187 protected void configureScrollBarColors()
188 {
189 LookAndFeel.installColors(scrollbar, "ScrollBar.background",
190 "ScrollBar.foreground");
191 thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
192 thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow");
193 thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow");
194 thumbColor = UIManager.getColor("ScrollBar.thumb");
195 trackColor = UIManager.getColor("ScrollBar.track");
196 trackHighlightColor = UIManager.getColor("ScrollBar.trackHighlight");
197 }
198
199 /**
200 * Installs the UI.
201 * @param c the component
202 */
203 public void installUI(JComponent c) {
204 scrollbar = (JScrollBar)c;
205 thumbRect = new Rectangle(0, 0, 0, 0);
206 trackRect = new Rectangle(0, 0, 0, 0);
207 installDefaults();
208 installComponents();
209 installListeners();
210 installKeyboardActions();
211 }
212
213 /**
214 * Uninstalls the UI.
215 * @param c the component
216 */
217 public void uninstallUI(JComponent c) {
218 scrollbar = (JScrollBar)c;
219 uninstallListeners();
220 uninstallDefaults();
221 uninstallComponents();
222 uninstallKeyboardActions();
223 thumbRect = null;
224 scrollbar = null;
225 incrButton = null;
226 decrButton = null;
227 }
228
229 /**
230 * Installs the defaults.
231 */
232 protected void installDefaults()
233 {
234 scrollBarWidth = UIManager.getInt("ScrollBar.width");
235 if (scrollBarWidth <= 0) {
236 scrollBarWidth = 16;
237 }
238 minimumThumbSize = (Dimension)UIManager.get("ScrollBar.minimumThumbSize");
239 maximumThumbSize = (Dimension)UIManager.get("ScrollBar.maximumThumbSize");
240
241 Boolean absB = (Boolean)UIManager.get("ScrollBar.allowsAbsolutePositioning");
242 supportsAbsolutePositioning = (absB != null) ? absB.booleanValue() :
243 false;
244
245 trackHighlight = NO_HIGHLIGHT;
246 if (scrollbar.getLayout() == null ||
247 (scrollbar.getLayout() instanceof UIResource)) {
248 scrollbar.setLayout(this);
249 }
250 configureScrollBarColors();
251 LookAndFeel.installBorder(scrollbar, "ScrollBar.border");
262 // components are based on Apples LAF
263 String scaleKey = (String)scrollbar.getClientProperty(
264 "JComponent.sizeVariant");
265 if (scaleKey != null){
266 if ("large".equals(scaleKey)){
267 scrollBarWidth *= 1.15;
268 incrGap *= 1.15;
269 decrGap *= 1.15;
270 } else if ("small".equals(scaleKey)){
271 scrollBarWidth *= 0.857;
272 incrGap *= 0.857;
273 decrGap *= 0.714;
274 } else if ("mini".equals(scaleKey)){
275 scrollBarWidth *= 0.714;
276 incrGap *= 0.714;
277 decrGap *= 0.714;
278 }
279 }
280 }
281
282 /**
283 * Installs the components.
284 */
285 protected void installComponents(){
286 switch (scrollbar.getOrientation()) {
287 case JScrollBar.VERTICAL:
288 incrButton = createIncreaseButton(SOUTH);
289 decrButton = createDecreaseButton(NORTH);
290 break;
291
292 case JScrollBar.HORIZONTAL:
293 if (scrollbar.getComponentOrientation().isLeftToRight()) {
294 incrButton = createIncreaseButton(EAST);
295 decrButton = createDecreaseButton(WEST);
296 } else {
297 incrButton = createIncreaseButton(WEST);
298 decrButton = createDecreaseButton(EAST);
299 }
300 break;
301 }
302 scrollbar.add(incrButton);
303 scrollbar.add(decrButton);
304 // Force the children's enabled state to be updated.
305 scrollbar.setEnabled(scrollbar.isEnabled());
306 }
307
308 /**
309 * Uninstalls the components.
310 */
311 protected void uninstallComponents(){
312 scrollbar.remove(incrButton);
313 scrollbar.remove(decrButton);
314 }
315
316 /**
317 * Installs the listeners.
318 */
319 protected void installListeners(){
320 trackListener = createTrackListener();
321 buttonListener = createArrowButtonListener();
322 modelListener = createModelListener();
323 propertyChangeListener = createPropertyChangeListener();
324
325 scrollbar.addMouseListener(trackListener);
326 scrollbar.addMouseMotionListener(trackListener);
327 scrollbar.getModel().addChangeListener(modelListener);
328 scrollbar.addPropertyChangeListener(propertyChangeListener);
329 scrollbar.addFocusListener(getHandler());
330
331 if (incrButton != null) {
332 incrButton.addMouseListener(buttonListener);
333 }
334 if (decrButton != null) {
335 decrButton.addMouseListener(buttonListener);
336 }
337
338 scrollListener = createScrollListener();
339 scrollTimer = new Timer(scrollSpeedThrottle, scrollListener);
340 scrollTimer.setInitialDelay(300); // default InitialDelay?
341 }
342
343 /**
344 * Installs the keyboard actions.
345 */
346 protected void installKeyboardActions(){
347 LazyActionMap.installLazyActionMap(scrollbar, BasicScrollBarUI.class,
348 "ScrollBar.actionMap");
349
350 InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
351 SwingUtilities.replaceUIInputMap(scrollbar, JComponent.WHEN_FOCUSED,
352 inputMap);
353 inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
354 SwingUtilities.replaceUIInputMap(scrollbar,
355 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
356 }
357
358 /**
359 * Uninstalls the keyboard actions.
360 */
361 protected void uninstallKeyboardActions(){
362 SwingUtilities.replaceUIInputMap(scrollbar, JComponent.WHEN_FOCUSED,
363 null);
364 SwingUtilities.replaceUIActionMap(scrollbar, null);
365 }
366
367 private InputMap getInputMap(int condition) {
368 if (condition == JComponent.WHEN_FOCUSED) {
369 InputMap keyMap = (InputMap)DefaultLookup.get(
370 scrollbar, this, "ScrollBar.focusInputMap");
371 InputMap rtlKeyMap;
372
373 if (scrollbar.getComponentOrientation().isLeftToRight() ||
374 ((rtlKeyMap = (InputMap)DefaultLookup.get(scrollbar, this, "ScrollBar.focusInputMap.RightToLeft")) == null)) {
375 return keyMap;
376 } else {
377 rtlKeyMap.setParent(keyMap);
378 return rtlKeyMap;
379 }
380 }
381 else if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
382 InputMap keyMap = (InputMap)DefaultLookup.get(
383 scrollbar, this, "ScrollBar.ancestorInputMap");
384 InputMap rtlKeyMap;
385
386 if (scrollbar.getComponentOrientation().isLeftToRight() ||
387 ((rtlKeyMap = (InputMap)DefaultLookup.get(scrollbar, this, "ScrollBar.ancestorInputMap.RightToLeft")) == null)) {
388 return keyMap;
389 } else {
390 rtlKeyMap.setParent(keyMap);
391 return rtlKeyMap;
392 }
393 }
394 return null;
395 }
396
397 /**
398 * Uninstall the listeners.
399 */
400 protected void uninstallListeners() {
401 scrollTimer.stop();
402 scrollTimer = null;
403
404 if (decrButton != null){
405 decrButton.removeMouseListener(buttonListener);
406 }
407 if (incrButton != null){
408 incrButton.removeMouseListener(buttonListener);
409 }
410
411 scrollbar.getModel().removeChangeListener(modelListener);
412 scrollbar.removeMouseListener(trackListener);
413 scrollbar.removeMouseMotionListener(trackListener);
414 scrollbar.removePropertyChangeListener(propertyChangeListener);
415 scrollbar.removeFocusListener(getHandler());
416 handler = null;
417 }
418
419 /**
420 * Uninstalls the defaults.
421 */
422 protected void uninstallDefaults(){
423 LookAndFeel.uninstallBorder(scrollbar);
424 if (scrollbar.getLayout() == this) {
425 scrollbar.setLayout(null);
426 }
427 }
428
429
430 private Handler getHandler() {
431 if (handler == null) {
432 handler = new Handler();
433 }
434 return handler;
435 }
436
437 /**
438 * Creates a track listener.
439 * @return a track listener
440 */
441 protected TrackListener createTrackListener(){
442 return new TrackListener();
443 }
444
445 /**
446 * Creates an arrow button listener.
447 * @return an arrow button listener
448 */
449 protected ArrowButtonListener createArrowButtonListener(){
450 return new ArrowButtonListener();
451 }
452
453 /**
454 * Creates a model listener.
455 * @return a model listener
456 */
457 protected ModelListener createModelListener(){
458 return new ModelListener();
459 }
460
461 /**
462 * Creates a scroll listener.
463 * @return a scroll listener
464 */
465 protected ScrollListener createScrollListener(){
466 return new ScrollListener();
467 }
468
469 /**
470 * Creates a property change listener.
471 * @return a property change listener
472 */
473 protected PropertyChangeListener createPropertyChangeListener() {
474 return getHandler();
475 }
476
477 private void updateThumbState(int x, int y) {
478 Rectangle rect = getThumbBounds();
479
480 setThumbRollover(rect.contains(x, y));
481 }
482
483 /**
484 * Sets whether or not the mouse is currently over the thumb.
485 *
486 * @param active True indicates the thumb is currently active.
487 * @since 1.5
488 */
489 protected void setThumbRollover(boolean active) {
490 if (thumbActive != active) {
491 thumbActive = active;
492 scrollbar.repaint(getThumbBounds());
528 * @see #getMaximumSize
529 * @see #getMinimumSize
530 */
531 public Dimension getPreferredSize(JComponent c) {
532 return (scrollbar.getOrientation() == JScrollBar.VERTICAL)
533 ? new Dimension(scrollBarWidth, 48)
534 : new Dimension(48, scrollBarWidth);
535 }
536
537
538 /**
539 * @param c The JScrollBar that's delegating this method to us.
540 * @return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
541 * @see #getMinimumSize
542 * @see #getPreferredSize
543 */
544 public Dimension getMaximumSize(JComponent c) {
545 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
546 }
547
548 /**
549 * Creates a decrease button.
550 * @param orientation the orientation
551 * @return a decrease button
552 */
553 protected JButton createDecreaseButton(int orientation) {
554 return new BasicArrowButton(orientation,
555 UIManager.getColor("ScrollBar.thumb"),
556 UIManager.getColor("ScrollBar.thumbShadow"),
557 UIManager.getColor("ScrollBar.thumbDarkShadow"),
558 UIManager.getColor("ScrollBar.thumbHighlight"));
559 }
560
561 /**
562 * Creates an increase button.
563 * @param orientation the orientation
564 * @return an increase button
565 */
566 protected JButton createIncreaseButton(int orientation) {
567 return new BasicArrowButton(orientation,
568 UIManager.getColor("ScrollBar.thumb"),
569 UIManager.getColor("ScrollBar.thumbShadow"),
570 UIManager.getColor("ScrollBar.thumbDarkShadow"),
571 UIManager.getColor("ScrollBar.thumbHighlight"));
572 }
573
574
575 /**
576 * Paints the decrease highlight.
577 * @param g the graphics
578 */
579 protected void paintDecreaseHighlight(Graphics g)
580 {
581 Insets insets = scrollbar.getInsets();
582 Rectangle thumbR = getThumbBounds();
583 g.setColor(trackHighlightColor);
584
585 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
586 //paint the distance between the start of the track and top of the thumb
587 int x = insets.left;
588 int y = trackRect.y;
589 int w = scrollbar.getWidth() - (insets.left + insets.right);
590 int h = thumbR.y - y;
591 g.fillRect(x, y, w, h);
592 } else {
593 //if left-to-right, fill the area between the start of the track and
594 //the left edge of the thumb. If right-to-left, fill the area between
595 //the end of the thumb and end of the track.
596 int x, w;
597 if (scrollbar.getComponentOrientation().isLeftToRight()) {
598 x = trackRect.x;
599 w = thumbR.x - x;
600 } else {
601 x = thumbR.x + thumbR.width;
602 w = trackRect.x + trackRect.width - x;
603 }
604 int y = insets.top;
605 int h = scrollbar.getHeight() - (insets.top + insets.bottom);
606 g.fillRect(x, y, w, h);
607 }
608 }
609
610
611 /**
612 * Paints the increase highlight.
613 * @param g the graphics
614 */
615 protected void paintIncreaseHighlight(Graphics g)
616 {
617 Insets insets = scrollbar.getInsets();
618 Rectangle thumbR = getThumbBounds();
619 g.setColor(trackHighlightColor);
620
621 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
622 //fill the area between the bottom of the thumb and the end of the track.
623 int x = insets.left;
624 int y = thumbR.y + thumbR.height;
625 int w = scrollbar.getWidth() - (insets.left + insets.right);
626 int h = trackRect.y + trackRect.height - y;
627 g.fillRect(x, y, w, h);
628 }
629 else {
630 //if left-to-right, fill the area between the right of the thumb and the
631 //end of the track. If right-to-left, then fill the area to the left of
632 //the thumb and the start of the track.
633 int x, w;
634 if (scrollbar.getComponentOrientation().isLeftToRight()) {
635 x = thumbR.x + thumbR.width;
636 w = trackRect.x + trackRect.width - x;
637 } else {
638 x = trackRect.x;
639 w = thumbR.x - x;
640 }
641 int y = insets.top;
642 int h = scrollbar.getHeight() - (insets.top + insets.bottom);
643 g.fillRect(x, y, w, h);
644 }
645 }
646
647
648 /**
649 * Paints the track.
650 * @param g the graphics
651 * @param c the component
652 * @param trackBounds the track bounds
653 */
654 protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
655 {
656 g.setColor(trackColor);
657 g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height);
658
659 if(trackHighlight == DECREASE_HIGHLIGHT) {
660 paintDecreaseHighlight(g);
661 }
662 else if(trackHighlight == INCREASE_HIGHLIGHT) {
663 paintIncreaseHighlight(g);
664 }
665 }
666
667 /**
668 * Paints the thumb.
669 * @param g the graphics
670 * @param c the component
671 * @param thumbBounds the thumb bounds
672 */
673 protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
674 {
675 if(thumbBounds.isEmpty() || !scrollbar.isEnabled()) {
676 return;
677 }
678
679 int w = thumbBounds.width;
680 int h = thumbBounds.height;
681
682 g.translate(thumbBounds.x, thumbBounds.y);
683
684 g.setColor(thumbDarkShadowColor);
685 drawRect(g, 0, 0, w - 1, h - 1);
686 g.setColor(thumbColor);
687 g.fillRect(0, 0, w - 1, h - 1);
688
689 g.setColor(thumbHighlightColor);
690 drawVLine(g, 1, 1, h - 2);
691 drawHLine(g, 2, w - 3, 1);
692
731
732 /*
733 * LayoutManager Implementation
734 */
735
736 public void addLayoutComponent(String name, Component child) {}
737 public void removeLayoutComponent(Component child) {}
738
739 public Dimension preferredLayoutSize(Container scrollbarContainer) {
740 return getPreferredSize((JComponent)scrollbarContainer);
741 }
742
743 public Dimension minimumLayoutSize(Container scrollbarContainer) {
744 return getMinimumSize((JComponent)scrollbarContainer);
745 }
746
747 private int getValue(JScrollBar sb) {
748 return (useCachedValue) ? scrollBarValue : sb.getValue();
749 }
750
751 /**
752 * Laysouts a vertical scroll bar.
753 * @param sb the scroll bar
754 */
755 protected void layoutVScrollbar(JScrollBar sb)
756 {
757 Dimension sbSize = sb.getSize();
758 Insets sbInsets = sb.getInsets();
759
760 /*
761 * Width and left edge of the buttons and thumb.
762 */
763 int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
764 int itemX = sbInsets.left;
765
766 /* Nominal locations of the buttons, assuming their preferred
767 * size will fit.
768 */
769 boolean squareButtons = DefaultLookup.getBoolean(
770 scrollbar, this, "ScrollBar.squareButtons", false);
771 int decrButtonH = squareButtons ? itemW :
772 decrButton.getPreferredSize().height;
773 int decrButtonY = sbInsets.top;
774
833 if (UIManager.getBoolean("ScrollBar.alwaysShowThumb")) {
834 // This is used primarily for GTK L&F, which expands the
835 // thumb to fit the track when it would otherwise be hidden.
836 setThumbBounds(itemX, itrackY, itemW, itrackH);
837 } else {
838 // Other L&F's simply hide the thumb in this case.
839 setThumbBounds(0, 0, 0, 0);
840 }
841 }
842 else {
843 if ((thumbY + thumbH) > incrButtonY - incrGap) {
844 thumbY = incrButtonY - incrGap - thumbH;
845 }
846 if (thumbY < (decrButtonY + decrButtonH + decrGap)) {
847 thumbY = decrButtonY + decrButtonH + decrGap + 1;
848 }
849 setThumbBounds(itemX, thumbY, itemW, thumbH);
850 }
851 }
852
853 /**
854 * Laysouts a vertical scroll bar.
855 * @param sb the scroll bar
856 */
857 protected void layoutHScrollbar(JScrollBar sb)
858 {
859 Dimension sbSize = sb.getSize();
860 Insets sbInsets = sb.getInsets();
861
862 /* Height and top edge of the buttons and thumb.
863 */
864 int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);
865 int itemY = sbInsets.top;
866
867 boolean ltr = sb.getComponentOrientation().isLeftToRight();
868
869 /* Nominal locations of the buttons, assuming their preferred
870 * size will fit.
871 */
872 boolean squareButtons = DefaultLookup.getBoolean(
873 scrollbar, this, "ScrollBar.squareButtons", false);
874 int leftButtonW = squareButtons ? itemH :
875 decrButton.getPreferredSize().width;
876 int rightButtonW = squareButtons ? itemH :
1060 */
1061 static void scrollByBlock(JScrollBar scrollbar, int direction) {
1062 // This method is called from BasicScrollPaneUI to implement wheel
1063 // scrolling, and also from scrollByBlock().
1064 int oldValue = scrollbar.getValue();
1065 int blockIncrement = scrollbar.getBlockIncrement(direction);
1066 int delta = blockIncrement * ((direction > 0) ? +1 : -1);
1067 int newValue = oldValue + delta;
1068
1069 // Check for overflow.
1070 if (delta > 0 && newValue < oldValue) {
1071 newValue = scrollbar.getMaximum();
1072 }
1073 else if (delta < 0 && newValue > oldValue) {
1074 newValue = scrollbar.getMinimum();
1075 }
1076
1077 scrollbar.setValue(newValue);
1078 }
1079
1080 /**
1081 * Scrolls by block.
1082 * @param direction the direction to scroll
1083 */
1084 protected void scrollByBlock(int direction)
1085 {
1086 scrollByBlock(scrollbar, direction);
1087 trackHighlight = direction > 0 ? INCREASE_HIGHLIGHT : DECREASE_HIGHLIGHT;
1088 Rectangle dirtyRect = getTrackBounds();
1089 scrollbar.repaint(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
1090 }
1091
1092 /*
1093 * Method for scrolling by a unit increment.
1094 * Added for mouse wheel scrolling support, RFE 4202656.
1095 *
1096 * If limitByBlock is set to true, the scrollbar will scroll at least 1
1097 * unit increment, but will not scroll farther than the block increment.
1098 * See BasicScrollPaneUI.Handler.mouseWheelMoved().
1099 */
1100 static void scrollByUnits(JScrollBar scrollbar, int direction,
1101 int units, boolean limitToBlock) {
1102 // This method is called from BasicScrollPaneUI to implement wheel
1103 // scrolling, as well as from scrollByUnit().
1131 newValue = scrollbar.getMaximum();
1132 }
1133 else if (delta < 0 && newValue > oldValue) {
1134 newValue = scrollbar.getMinimum();
1135 }
1136 if (oldValue == newValue) {
1137 break;
1138 }
1139
1140 if (limitToBlock && i > 0) {
1141 assert limit != -1;
1142 if ((direction < 0 && newValue < limit) ||
1143 (direction > 0 && newValue > limit)) {
1144 break;
1145 }
1146 }
1147 scrollbar.setValue(newValue);
1148 }
1149 }
1150
1151 /**
1152 * Scrolls by unit.
1153 * @param direction the direction to scroll
1154 */
1155 protected void scrollByUnit(int direction) {
1156 scrollByUnits(scrollbar, direction, 1, false);
1157 }
1158
1159 /**
1160 * Indicates whether the user can absolutely position the thumb with
1161 * a mouse gesture (usually the middle mouse button).
1162 *
1163 * @return true if a mouse gesture can absolutely position the thumb
1164 * @since 1.5
1165 */
1166 public boolean getSupportsAbsolutePositioning() {
1167 return supportsAbsolutePositioning;
1168 }
1169
1170 /**
1171 * A listener to listen for model changes.
1172 */
1173 protected class ModelListener implements ChangeListener {
1174 public void stateChanged(ChangeEvent e) {
1175 if (!useCachedValue) {
1176 scrollBarValue = scrollbar.getValue();
1177 }
1178 layoutContainer(scrollbar);
1179 useCachedValue = false;
1180 }
1181 }
1182
1183
1184 /**
1185 * Track mouse drags.
1186 */
1187 protected class TrackListener
1188 extends MouseAdapter implements MouseMotionListener
1189 {
1190 /** The offset */
1191 protected transient int offset;
1192 /** Current mouse x position */
1193 protected transient int currentMouseX;
1194 /** Current mouse y position */
1195 protected transient int currentMouseY;
1196 private transient int direction = +1;
1197
1198 /** {@inheritDoc} */
1199 public void mouseReleased(MouseEvent e)
1200 {
1201 if (isDragging) {
1202 updateThumbState(e.getX(), e.getY());
1203 }
1204 if (SwingUtilities.isRightMouseButton(e) ||
1205 (!getSupportsAbsolutePositioning() &&
1206 SwingUtilities.isMiddleMouseButton(e)))
1207 return;
1208 if(!scrollbar.isEnabled())
1209 return;
1210
1211 Rectangle r = getTrackBounds();
1212 scrollbar.repaint(r.x, r.y, r.width, r.height);
1213
1214 trackHighlight = NO_HIGHLIGHT;
1215 isDragging = false;
1216 offset = 0;
1217 scrollTimer.stop();
1218 useCachedValue = true;
1445 switch (scrollbar.getOrientation()) {
1446 case JScrollBar.VERTICAL:
1447 if (direction > 0) {
1448 if (tb.y + tb.height < trackListener.currentMouseY) {
1449 scrollTimer.start();
1450 }
1451 } else if (tb.y > trackListener.currentMouseY) {
1452 scrollTimer.start();
1453 }
1454 break;
1455 case JScrollBar.HORIZONTAL:
1456 if ((direction > 0 && isMouseAfterThumb())
1457 || (direction < 0 && isMouseBeforeThumb())) {
1458
1459 scrollTimer.start();
1460 }
1461 break;
1462 }
1463 }
1464
1465 /** {@inheritDoc} */
1466 public void mouseMoved(MouseEvent e) {
1467 if (!isDragging) {
1468 updateThumbState(e.getX(), e.getY());
1469 }
1470 }
1471
1472 /**
1473 * Invoked when the mouse exits the scrollbar.
1474 *
1475 * @param e MouseEvent further describing the event
1476 * @since 1.5
1477 */
1478 public void mouseExited(MouseEvent e) {
1479 if (!isDragging) {
1480 setThumbRollover(false);
1481 }
1482 }
1483 }
1484
1485
1513 }
1514 }
1515
1516 public void mouseReleased(MouseEvent e) {
1517 scrollTimer.stop();
1518 handledEvent = false;
1519 scrollbar.setValueIsAdjusting(false);
1520 }
1521 }
1522
1523
1524 /**
1525 * Listener for scrolling events initiated in the
1526 * <code>ScrollPane</code>.
1527 */
1528 protected class ScrollListener implements ActionListener
1529 {
1530 int direction = +1;
1531 boolean useBlockIncrement;
1532
1533 /** Constructs a {@code ScrollListener}. */
1534 public ScrollListener() {
1535 direction = +1;
1536 useBlockIncrement = false;
1537 }
1538
1539 /**
1540 * Constructs a {@code ScrollListener}.
1541 * @param dir direction
1542 * @param block use block increment
1543 */
1544 public ScrollListener(int dir, boolean block) {
1545 direction = dir;
1546 useBlockIncrement = block;
1547 }
1548
1549 /**
1550 * Sets the direction.
1551 * @param direction the new direction
1552 */
1553 public void setDirection(int direction) { this.direction = direction; }
1554 /**
1555 * Sets the scrolling by block
1556 * @param block whether or not to scroll by block
1557 */
1558 public void setScrollByBlock(boolean block) { this.useBlockIncrement = block; }
1559
1560 /** {@inheritDoc} */
1561 public void actionPerformed(ActionEvent e) {
1562 if(useBlockIncrement) {
1563 scrollByBlock(direction);
1564 // Stop scrolling if the thumb catches up with the mouse
1565 if(scrollbar.getOrientation() == JScrollBar.VERTICAL) {
1566 if(direction > 0) {
1567 if(getThumbBounds().y + getThumbBounds().height
1568 >= trackListener.currentMouseY)
1569 ((Timer)e.getSource()).stop();
1570 } else if(getThumbBounds().y <= trackListener.currentMouseY) {
1571 ((Timer)e.getSource()).stop();
1572 }
1573 } else {
1574 if ((direction > 0 && !isMouseAfterThumb())
1575 || (direction < 0 && !isMouseBeforeThumb())) {
1576
1577 ((Timer)e.getSource()).stop();
1578 }
1579 }
1580 } else {
1619 ((BasicArrowButton)incrButton).setDirection(
1620 orient == HORIZONTAL? EAST : SOUTH);
1621 }
1622 if (decrButton instanceof BasicArrowButton) {
1623 ((BasicArrowButton)decrButton).setDirection(
1624 orient == HORIZONTAL? WEST : NORTH);
1625 }
1626 }
1627 else {
1628 if (incrButton instanceof BasicArrowButton) {
1629 ((BasicArrowButton)incrButton).setDirection(
1630 orient == HORIZONTAL? WEST : SOUTH);
1631 }
1632 if (decrButton instanceof BasicArrowButton) {
1633 ((BasicArrowButton)decrButton).setDirection(
1634 orient == HORIZONTAL ? EAST : NORTH);
1635 }
1636 }
1637 }
1638
1639 /** Property change handler */
1640 public class PropertyChangeHandler implements PropertyChangeListener
1641 {
1642 // NOTE: This class exists only for backward compatibility. All
1643 // its functionality has been moved into Handler. If you need to add
1644 // new functionality add it to the Handler, but make sure this
1645 // class calls into the Handler.
1646 /** {@inheritDoc} */
1647 public void propertyChange(PropertyChangeEvent e) {
1648 getHandler().propertyChange(e);
1649 }
1650 }
1651
1652
1653 /**
1654 * Used for scrolling the scrollbar.
1655 */
1656 private static class Actions extends UIAction {
1657 private static final String POSITIVE_UNIT_INCREMENT =
1658 "positiveUnitIncrement";
1659 private static final String POSITIVE_BLOCK_INCREMENT =
1660 "positiveBlockIncrement";
1661 private static final String NEGATIVE_UNIT_INCREMENT =
1662 "negativeUnitIncrement";
1663 private static final String NEGATIVE_BLOCK_INCREMENT =
1664 "negativeBlockIncrement";
1665 private static final String MIN_SCROLL = "minScroll";
1666 private static final String MAX_SCROLL = "maxScroll";
|