History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/plaf/basic/BasicScrollBarUI.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/plaf/basic/BasicScrollBarUI.java+1618−0
@@ -0,0 +1,1618 @@
1/*
2 * Copyright (c) 1997, 2010, 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 */
25package javax.swing.plaf.basic;
26
27
28import sun.swing.DefaultLookup;
29import sun.swing.UIAction;
30
31import java.awt.*;
32import java.awt.event.*;
33
34import java.beans.*;
35
36import javax.swing.*;
37import javax.swing.event.*;
38import javax.swing.plaf.*;
39
40
41/**
42 * Implementation of ScrollBarUI for the Basic Look and Feel
43 *
44 * @author Rich Schiavi
45 * @author David Kloba
46 * @author Hans Muller
47 */
48public class BasicScrollBarUI
49 extends ScrollBarUI implements LayoutManager, SwingConstants
50{
51 private static final int POSITIVE_SCROLL = 1;
52 private static final int NEGATIVE_SCROLL = -1;
53
54 private static final int MIN_SCROLL = 2;
55 private static final int MAX_SCROLL = 3;
56
57 // NOTE: DO NOT use this field directly, SynthScrollBarUI assumes you'll
58 // call getMinimumThumbSize to access it.
59 protected Dimension minimumThumbSize;
60 protected Dimension maximumThumbSize;
61
62 protected Color thumbHighlightColor;
63 protected Color thumbLightShadowColor;
64 protected Color thumbDarkShadowColor;
65 protected Color thumbColor;
66 protected Color trackColor;
67 protected Color trackHighlightColor;
68
69 protected JScrollBar scrollbar;
70 protected JButton incrButton;
71 protected JButton decrButton;
72 protected boolean isDragging;
73 protected TrackListener trackListener;
74 protected ArrowButtonListener buttonListener;
75 protected ModelListener modelListener;
76
77 protected Rectangle thumbRect;
78 protected Rectangle trackRect;
79
80 protected int trackHighlight;
81
82 protected static final int NO_HIGHLIGHT = 0;
83 protected static final int DECREASE_HIGHLIGHT = 1;
84 protected static final int INCREASE_HIGHLIGHT = 2;
85
86 protected ScrollListener scrollListener;
87 protected PropertyChangeListener propertyChangeListener;
88 protected Timer scrollTimer;
89
90 private final static int scrollSpeedThrottle = 60; // delay in milli seconds
91
92 /** True indicates a middle click will absolutely position the
93 * scrollbar. */
94 private boolean supportsAbsolutePositioning;
95
96 /**
97 * Hint as to what width (when vertical) or height (when horizontal)
98 * should be.
99 *
100 * @since 1.7
101 */
102 protected int scrollBarWidth;
103
104 private Handler handler;
105
106 private boolean thumbActive;
107
108 /**
109 * Determine whether scrollbar layout should use cached value or adjusted
110 * value returned by scrollbar's <code>getValue</code>.
111 */
112 private boolean useCachedValue = false;
113 /**
114 * The scrollbar value is cached to save real value if the view is adjusted.
115 */
116 private int scrollBarValue;
117
118 /**
119 * Distance between the increment button and the track. This may be a negative
120 * number. If negative, then an overlap between the button and track will occur,
121 * which is useful for shaped buttons.
122 *
123 * @since 1.7
124 */
125 protected int incrGap;
126
127 /**
128 * Distance between the decrement button and the track. This may be a negative
129 * number. If negative, then an overlap between the button and track will occur,
130 * which is useful for shaped buttons.
131 *
132 * @since 1.7
133 */
134 protected int decrGap;
135
136 static void loadActionMap(LazyActionMap map) {
137 map.put(new Actions(Actions.POSITIVE_UNIT_INCREMENT));
138 map.put(new Actions(Actions.POSITIVE_BLOCK_INCREMENT));
139 map.put(new Actions(Actions.NEGATIVE_UNIT_INCREMENT));
140 map.put(new Actions(Actions.NEGATIVE_BLOCK_INCREMENT));
141 map.put(new Actions(Actions.MIN_SCROLL));
142 map.put(new Actions(Actions.MAX_SCROLL));
143 }
144
145
146 public static ComponentUI createUI(JComponent c) {
147 return new BasicScrollBarUI();
148 }
149
150
151 protected void configureScrollBarColors()
152 {
153 LookAndFeel.installColors(scrollbar, "ScrollBar.background",
154 "ScrollBar.foreground");
155 thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
156 thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow");
157 thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow");
158 thumbColor = UIManager.getColor("ScrollBar.thumb");
159 trackColor = UIManager.getColor("ScrollBar.track");
160 trackHighlightColor = UIManager.getColor("ScrollBar.trackHighlight");
161 }
162
163
164 public void installUI(JComponent c) {
165 scrollbar = (JScrollBar)c;
166 thumbRect = new Rectangle(0, 0, 0, 0);
167 trackRect = new Rectangle(0, 0, 0, 0);
168 installDefaults();
169 installComponents();
170 installListeners();
171 installKeyboardActions();
172 }
173
174 public void uninstallUI(JComponent c) {
175 scrollbar = (JScrollBar)c;
176 uninstallListeners();
177 uninstallDefaults();
178 uninstallComponents();
179 uninstallKeyboardActions();
180 thumbRect = null;
181 scrollbar = null;
182 incrButton = null;
183 decrButton = null;
184 }
185
186
187 protected void installDefaults()
188 {
189 scrollBarWidth = UIManager.getInt("ScrollBar.width");
190 if (scrollBarWidth <= 0) {
191 scrollBarWidth = 16;
192 }
193 minimumThumbSize = (Dimension)UIManager.get("ScrollBar.minimumThumbSize");
194 maximumThumbSize = (Dimension)UIManager.get("ScrollBar.maximumThumbSize");
195
196 Boolean absB = (Boolean)UIManager.get("ScrollBar.allowsAbsolutePositioning");
197 supportsAbsolutePositioning = (absB != null) ? absB.booleanValue() :
198 false;
199
200 trackHighlight = NO_HIGHLIGHT;
201 if (scrollbar.getLayout() == null ||
202 (scrollbar.getLayout() instanceof UIResource)) {
203 scrollbar.setLayout(this);
204 }
205 configureScrollBarColors();
206 LookAndFeel.installBorder(scrollbar, "ScrollBar.border");
207 LookAndFeel.installProperty(scrollbar, "opaque", Boolean.TRUE);
208
209 scrollBarValue = scrollbar.getValue();
210
211 incrGap = UIManager.getInt("ScrollBar.incrementButtonGap");
212 decrGap = UIManager.getInt("ScrollBar.decrementButtonGap");
213
214 // TODO this can be removed when incrGap/decrGap become protected
215 // handle scaling for sizeVarients for special case components. The
216 // key "JComponent.sizeVariant" scales for large/small/mini
217 // components are based on Apples LAF
218 String scaleKey = (String)scrollbar.getClientProperty(
219 "JComponent.sizeVariant");
220 if (scaleKey != null){
221 if ("large".equals(scaleKey)){
222 scrollBarWidth *= 1.15;
223 incrGap *= 1.15;
224 decrGap *= 1.15;
225 } else if ("small".equals(scaleKey)){
226 scrollBarWidth *= 0.857;
227 incrGap *= 0.857;
228 decrGap *= 0.714;
229 } else if ("mini".equals(scaleKey)){
230 scrollBarWidth *= 0.714;
231 incrGap *= 0.714;
232 decrGap *= 0.714;
233 }
234 }
235 }
236
237
238 protected void installComponents(){
239 switch (scrollbar.getOrientation()) {
240 case JScrollBar.VERTICAL:
241 incrButton = createIncreaseButton(SOUTH);
242 decrButton = createDecreaseButton(NORTH);
243 break;
244
245 case JScrollBar.HORIZONTAL:
246 if (scrollbar.getComponentOrientation().isLeftToRight()) {
247 incrButton = createIncreaseButton(EAST);
248 decrButton = createDecreaseButton(WEST);
249 } else {
250 incrButton = createIncreaseButton(WEST);
251 decrButton = createDecreaseButton(EAST);
252 }
253 break;
254 }
255 scrollbar.add(incrButton);
256 scrollbar.add(decrButton);
257 // Force the children's enabled state to be updated.
258 scrollbar.setEnabled(scrollbar.isEnabled());
259 }
260
261 protected void uninstallComponents(){
262 scrollbar.remove(incrButton);
263 scrollbar.remove(decrButton);
264 }
265
266
267 protected void installListeners(){
268 trackListener = createTrackListener();
269 buttonListener = createArrowButtonListener();
270 modelListener = createModelListener();
271 propertyChangeListener = createPropertyChangeListener();
272
273 scrollbar.addMouseListener(trackListener);
274 scrollbar.addMouseMotionListener(trackListener);
275 scrollbar.getModel().addChangeListener(modelListener);
276 scrollbar.addPropertyChangeListener(propertyChangeListener);
277 scrollbar.addFocusListener(getHandler());
278
279 if (incrButton != null) {
280 incrButton.addMouseListener(buttonListener);
281 }
282 if (decrButton != null) {
283 decrButton.addMouseListener(buttonListener);
284 }
285
286 scrollListener = createScrollListener();
287 scrollTimer = new Timer(scrollSpeedThrottle, scrollListener);
288 scrollTimer.setInitialDelay(300); // default InitialDelay?
289 }
290
291
292 protected void installKeyboardActions(){
293 LazyActionMap.installLazyActionMap(scrollbar, BasicScrollBarUI.class,
294 "ScrollBar.actionMap");
295
296 InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
297 SwingUtilities.replaceUIInputMap(scrollbar, JComponent.WHEN_FOCUSED,
298 inputMap);
299 inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
300 SwingUtilities.replaceUIInputMap(scrollbar,
301 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
302 }
303
304 protected void uninstallKeyboardActions(){
305 SwingUtilities.replaceUIInputMap(scrollbar, JComponent.WHEN_FOCUSED,
306 null);
307 SwingUtilities.replaceUIActionMap(scrollbar, null);
308 }
309
310 private InputMap getInputMap(int condition) {
311 if (condition == JComponent.WHEN_FOCUSED) {
312 InputMap keyMap = (InputMap)DefaultLookup.get(
313 scrollbar, this, "ScrollBar.focusInputMap");
314 InputMap rtlKeyMap;
315
316 if (scrollbar.getComponentOrientation().isLeftToRight() ||
317 ((rtlKeyMap = (InputMap)DefaultLookup.get(scrollbar, this, "ScrollBar.focusInputMap.RightToLeft")) == null)) {
318 return keyMap;
319 } else {
320 rtlKeyMap.setParent(keyMap);
321 return rtlKeyMap;
322 }
323 }
324 else if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
325 InputMap keyMap = (InputMap)DefaultLookup.get(
326 scrollbar, this, "ScrollBar.ancestorInputMap");
327 InputMap rtlKeyMap;
328
329 if (scrollbar.getComponentOrientation().isLeftToRight() ||
330 ((rtlKeyMap = (InputMap)DefaultLookup.get(scrollbar, this, "ScrollBar.ancestorInputMap.RightToLeft")) == null)) {
331 return keyMap;
332 } else {
333 rtlKeyMap.setParent(keyMap);
334 return rtlKeyMap;
335 }
336 }
337 return null;
338 }
339
340
341 protected void uninstallListeners() {
342 scrollTimer.stop();
343 scrollTimer = null;
344
345 if (decrButton != null){
346 decrButton.removeMouseListener(buttonListener);
347 }
348 if (incrButton != null){
349 incrButton.removeMouseListener(buttonListener);
350 }
351
352 scrollbar.getModel().removeChangeListener(modelListener);
353 scrollbar.removeMouseListener(trackListener);
354 scrollbar.removeMouseMotionListener(trackListener);
355 scrollbar.removePropertyChangeListener(propertyChangeListener);
356 scrollbar.removeFocusListener(getHandler());
357 handler = null;
358 }
359
360
361 protected void uninstallDefaults(){
362 LookAndFeel.uninstallBorder(scrollbar);
363 if (scrollbar.getLayout() == this) {
364 scrollbar.setLayout(null);
365 }
366 }
367
368
369 private Handler getHandler() {
370 if (handler == null) {
371 handler = new Handler();
372 }
373 return handler;
374 }
375
376 protected TrackListener createTrackListener(){
377 return new TrackListener();
378 }
379
380 protected ArrowButtonListener createArrowButtonListener(){
381 return new ArrowButtonListener();
382 }
383
384 protected ModelListener createModelListener(){
385 return new ModelListener();
386 }
387
388 protected ScrollListener createScrollListener(){
389 return new ScrollListener();
390 }
391
392 protected PropertyChangeListener createPropertyChangeListener() {
393 return getHandler();
394 }
395
396 private void updateThumbState(int x, int y) {
397 Rectangle rect = getThumbBounds();
398
399 setThumbRollover(rect.contains(x, y));
400 }
401
402 /**
403 * Sets whether or not the mouse is currently over the thumb.
404 *
405 * @param active True indicates the thumb is currently active.
406 * @since 1.5
407 */
408 protected void setThumbRollover(boolean active) {
409 if (thumbActive != active) {
410 thumbActive = active;
411 scrollbar.repaint(getThumbBounds());
412 }
413 }
414
415 /**
416 * Returns true if the mouse is currently over the thumb.
417 *
418 * @return true if the thumb is currently active
419 * @since 1.5
420 */
421 public boolean isThumbRollover() {
422 return thumbActive;
423 }
424
425 public void paint(Graphics g, JComponent c) {
426 paintTrack(g, c, getTrackBounds());
427 Rectangle thumbBounds = getThumbBounds();
428 if (thumbBounds.intersects(g.getClipBounds())) {
429 paintThumb(g, c, thumbBounds);
430 }
431 }
432
433
434 /**
435 * A vertical scrollbar's preferred width is the maximum of
436 * preferred widths of the (non <code>null</code>)
437 * increment/decrement buttons,
438 * and the minimum width of the thumb. The preferred height is the
439 * sum of the preferred heights of the same parts. The basis for
440 * the preferred size of a horizontal scrollbar is similar.
441 * <p>
442 * The <code>preferredSize</code> is only computed once, subsequent
443 * calls to this method just return a cached size.
444 *
445 * @param c the <code>JScrollBar</code> that's delegating this method to us
446 * @return the preferred size of a Basic JScrollBar
447 * @see #getMaximumSize
448 * @see #getMinimumSize
449 */
450 public Dimension getPreferredSize(JComponent c) {
451 return (scrollbar.getOrientation() == JScrollBar.VERTICAL)
452 ? new Dimension(scrollBarWidth, 48)
453 : new Dimension(48, scrollBarWidth);
454 }
455
456
457 /**
458 * @param c The JScrollBar that's delegating this method to us.
459 * @return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
460 * @see #getMinimumSize
461 * @see #getPreferredSize
462 */
463 public Dimension getMaximumSize(JComponent c) {
464 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
465 }
466
467 protected JButton createDecreaseButton(int orientation) {
468 return new BasicArrowButton(orientation,
469 UIManager.getColor("ScrollBar.thumb"),
470 UIManager.getColor("ScrollBar.thumbShadow"),
471 UIManager.getColor("ScrollBar.thumbDarkShadow"),
472 UIManager.getColor("ScrollBar.thumbHighlight"));
473 }
474
475 protected JButton createIncreaseButton(int orientation) {
476 return new BasicArrowButton(orientation,
477 UIManager.getColor("ScrollBar.thumb"),
478 UIManager.getColor("ScrollBar.thumbShadow"),
479 UIManager.getColor("ScrollBar.thumbDarkShadow"),
480 UIManager.getColor("ScrollBar.thumbHighlight"));
481 }
482
483
484 protected void paintDecreaseHighlight(Graphics g)
485 {
486 Insets insets = scrollbar.getInsets();
487 Rectangle thumbR = getThumbBounds();
488 g.setColor(trackHighlightColor);
489
490 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
491 //paint the distance between the start of the track and top of the thumb
492 int x = insets.left;
493 int y = trackRect.y;
494 int w = scrollbar.getWidth() - (insets.left + insets.right);
495 int h = thumbR.y - y;
496 g.fillRect(x, y, w, h);
497 } else {
498 //if left-to-right, fill the area between the start of the track and
499 //the left edge of the thumb. If right-to-left, fill the area between
500 //the end of the thumb and end of the track.
501 int x, w;
502 if (scrollbar.getComponentOrientation().isLeftToRight()) {
503 x = trackRect.x;
504 w = thumbR.x - x;
505 } else {
506 x = thumbR.x + thumbR.width;
507 w = trackRect.x + trackRect.width - x;
508 }
509 int y = insets.top;
510 int h = scrollbar.getHeight() - (insets.top + insets.bottom);
511 g.fillRect(x, y, w, h);
512 }
513 }
514
515
516 protected void paintIncreaseHighlight(Graphics g)
517 {
518 Insets insets = scrollbar.getInsets();
519 Rectangle thumbR = getThumbBounds();
520 g.setColor(trackHighlightColor);
521
522 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
523 //fill the area between the bottom of the thumb and the end of the track.
524 int x = insets.left;
525 int y = thumbR.y + thumbR.height;
526 int w = scrollbar.getWidth() - (insets.left + insets.right);
527 int h = trackRect.y + trackRect.height - y;
528 g.fillRect(x, y, w, h);
529 }
530 else {
531 //if left-to-right, fill the area between the right of the thumb and the
532 //end of the track. If right-to-left, then fill the area to the left of
533 //the thumb and the start of the track.
534 int x, w;
535 if (scrollbar.getComponentOrientation().isLeftToRight()) {
536 x = thumbR.x + thumbR.width;
537 w = trackRect.x + trackRect.width - x;
538 } else {
539 x = trackRect.x;
540 w = thumbR.x - x;
541 }
542 int y = insets.top;
543 int h = scrollbar.getHeight() - (insets.top + insets.bottom);
544 g.fillRect(x, y, w, h);
545 }
546 }
547
548
549 protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
550 {
551 g.setColor(trackColor);
552 g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height);
553
554 if(trackHighlight == DECREASE_HIGHLIGHT) {
555 paintDecreaseHighlight(g);
556 }
557 else if(trackHighlight == INCREASE_HIGHLIGHT) {
558 paintIncreaseHighlight(g);
559 }
560 }
561
562
563 protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
564 {
565 if(thumbBounds.isEmpty() || !scrollbar.isEnabled()) {
566 return;
567 }
568
569 int w = thumbBounds.width;
570 int h = thumbBounds.height;
571
572 g.translate(thumbBounds.x, thumbBounds.y);
573
574 g.setColor(thumbDarkShadowColor);
575 g.drawRect(0, 0, w-1, h-1);
576 g.setColor(thumbColor);
577 g.fillRect(0, 0, w-1, h-1);
578
579 g.setColor(thumbHighlightColor);
580 g.drawLine(1, 1, 1, h-2);
581 g.drawLine(2, 1, w-3, 1);
582
583 g.setColor(thumbLightShadowColor);
584 g.drawLine(2, h-2, w-2, h-2);
585 g.drawLine(w-2, 1, w-2, h-3);
586
587 g.translate(-thumbBounds.x, -thumbBounds.y);
588 }
589
590
591 /**
592 * Returns the smallest acceptable size for the thumb. If the scrollbar
593 * becomes so small that this size isn't available, the thumb will be
594 * hidden.
595 * <p>
596 * <b>Warning </b>: the value returned by this method should not be
597 * be modified, it's a shared static constant.
598 *
599 * @return The smallest acceptable size for the thumb.
600 * @see #getMaximumThumbSize
601 */
602 protected Dimension getMinimumThumbSize() {
603 return minimumThumbSize;
604 }
605
606 /**
607 * Returns the largest acceptable size for the thumb. To create a fixed
608 * size thumb one make this method and <code>getMinimumThumbSize</code>
609 * return the same value.
610 * <p>
611 * <b>Warning </b>: the value returned by this method should not be
612 * be modified, it's a shared static constant.
613 *
614 * @return The largest acceptable size for the thumb.
615 * @see #getMinimumThumbSize
616 */
617 protected Dimension getMaximumThumbSize() {
618 return maximumThumbSize;
619 }
620
621
622 /*
623 * LayoutManager Implementation
624 */
625
626 public void addLayoutComponent(String name, Component child) {}
627 public void removeLayoutComponent(Component child) {}
628
629 public Dimension preferredLayoutSize(Container scrollbarContainer) {
630 return getPreferredSize((JComponent)scrollbarContainer);
631 }
632
633 public Dimension minimumLayoutSize(Container scrollbarContainer) {
634 return getMinimumSize((JComponent)scrollbarContainer);
635 }
636
637 private int getValue(JScrollBar sb) {
638 return (useCachedValue) ? scrollBarValue : sb.getValue();
639 }
640
641 protected void layoutVScrollbar(JScrollBar sb)
642 {
643 Dimension sbSize = sb.getSize();
644 Insets sbInsets = sb.getInsets();
645
646 /*
647 * Width and left edge of the buttons and thumb.
648 */
649 int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
650 int itemX = sbInsets.left;
651
652 /* Nominal locations of the buttons, assuming their preferred
653 * size will fit.
654 */
655 boolean squareButtons = DefaultLookup.getBoolean(
656 scrollbar, this, "ScrollBar.squareButtons", false);
657 int decrButtonH = squareButtons ? itemW :
658 decrButton.getPreferredSize().height;
659 int decrButtonY = sbInsets.top;
660
661 int incrButtonH = squareButtons ? itemW :
662 incrButton.getPreferredSize().height;
663 int incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
664
665 /* The thumb must fit within the height left over after we
666 * subtract the preferredSize of the buttons and the insets
667 * and the gaps
668 */
669 int sbInsetsH = sbInsets.top + sbInsets.bottom;
670 int sbButtonsH = decrButtonH + incrButtonH;
671 int gaps = decrGap + incrGap;
672 float trackH = sbSize.height - (sbInsetsH + sbButtonsH) - gaps;
673
674 /* Compute the height and origin of the thumb. The case
675 * where the thumb is at the bottom edge is handled specially
676 * to avoid numerical problems in computing thumbY. Enforce
677 * the thumbs min/max dimensions. If the thumb doesn't
678 * fit in the track (trackH) we'll hide it later.
679 */
680 float min = sb.getMinimum();
681 float extent = sb.getVisibleAmount();
682 float range = sb.getMaximum() - min;
683 float value = getValue(sb);
684
685 int thumbH = (range <= 0)
686 ? getMaximumThumbSize().height : (int)(trackH * (extent / range));
687 thumbH = Math.max(thumbH, getMinimumThumbSize().height);
688 thumbH = Math.min(thumbH, getMaximumThumbSize().height);
689
690 int thumbY = incrButtonY - incrGap - thumbH;
691 if (value < (sb.getMaximum() - sb.getVisibleAmount())) {
692 float thumbRange = trackH - thumbH;
693 thumbY = (int)(0.5f + (thumbRange * ((value - min) / (range - extent))));
694 thumbY += decrButtonY + decrButtonH + decrGap;
695 }
696
697 /* If the buttons don't fit, allocate half of the available
698 * space to each and move the lower one (incrButton) down.
699 */
700 int sbAvailButtonH = (sbSize.height - sbInsetsH);
701 if (sbAvailButtonH < sbButtonsH) {
702 incrButtonH = decrButtonH = sbAvailButtonH / 2;
703 incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
704 }
705 decrButton.setBounds(itemX, decrButtonY, itemW, decrButtonH);
706 incrButton.setBounds(itemX, incrButtonY, itemW, incrButtonH);
707
708 /* Update the trackRect field.
709 */
710 int itrackY = decrButtonY + decrButtonH + decrGap;
711 int itrackH = incrButtonY - incrGap - itrackY;
712 trackRect.setBounds(itemX, itrackY, itemW, itrackH);
713
714 /* If the thumb isn't going to fit, zero it's bounds. Otherwise
715 * make sure it fits between the buttons. Note that setting the
716 * thumbs bounds will cause a repaint.
717 */
718 if(thumbH >= (int)trackH) {
719 if (UIManager.getBoolean("ScrollBar.alwaysShowThumb")) {
720 // This is used primarily for GTK L&F, which expands the
721 // thumb to fit the track when it would otherwise be hidden.
722 setThumbBounds(itemX, itrackY, itemW, itrackH);
723 } else {
724 // Other L&F's simply hide the thumb in this case.
725 setThumbBounds(0, 0, 0, 0);
726 }
727 }
728 else {
729 if ((thumbY + thumbH) > incrButtonY - incrGap) {
730 thumbY = incrButtonY - incrGap - thumbH;
731 }
732 if (thumbY < (decrButtonY + decrButtonH + decrGap)) {
733 thumbY = decrButtonY + decrButtonH + decrGap + 1;
734 }
735 setThumbBounds(itemX, thumbY, itemW, thumbH);
736 }
737 }
738
739
740 protected void layoutHScrollbar(JScrollBar sb)
741 {
742 Dimension sbSize = sb.getSize();
743 Insets sbInsets = sb.getInsets();
744
745 /* Height and top edge of the buttons and thumb.
746 */
747 int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);
748 int itemY = sbInsets.top;
749
750 boolean ltr = sb.getComponentOrientation().isLeftToRight();
751
752 /* Nominal locations of the buttons, assuming their preferred
753 * size will fit.
754 */
755 boolean squareButtons = DefaultLookup.getBoolean(
756 scrollbar, this, "ScrollBar.squareButtons", false);
757 int leftButtonW = squareButtons ? itemH :
758 decrButton.getPreferredSize().width;
759 int rightButtonW = squareButtons ? itemH :
760 incrButton.getPreferredSize().width;
761 if (!ltr) {
762 int temp = leftButtonW;
763 leftButtonW = rightButtonW;
764 rightButtonW = temp;
765 }
766 int leftButtonX = sbInsets.left;
767 int rightButtonX = sbSize.width - (sbInsets.right + rightButtonW);
768 int leftGap = ltr ? decrGap : incrGap;
769 int rightGap = ltr ? incrGap : decrGap;
770
771 /* The thumb must fit within the width left over after we
772 * subtract the preferredSize of the buttons and the insets
773 * and the gaps
774 */
775 int sbInsetsW = sbInsets.left + sbInsets.right;
776 int sbButtonsW = leftButtonW + rightButtonW;
777 float trackW = sbSize.width - (sbInsetsW + sbButtonsW) - (leftGap + rightGap);
778
779 /* Compute the width and origin of the thumb. Enforce
780 * the thumbs min/max dimensions. The case where the thumb
781 * is at the right edge is handled specially to avoid numerical
782 * problems in computing thumbX. If the thumb doesn't
783 * fit in the track (trackH) we'll hide it later.
784 */
785 float min = sb.getMinimum();
786 float max = sb.getMaximum();
787 float extent = sb.getVisibleAmount();
788 float range = max - min;
789 float value = getValue(sb);
790
791 int thumbW = (range <= 0)
792 ? getMaximumThumbSize().width : (int)(trackW * (extent / range));
793 thumbW = Math.max(thumbW, getMinimumThumbSize().width);
794 thumbW = Math.min(thumbW, getMaximumThumbSize().width);
795
796 int thumbX = ltr ? rightButtonX - rightGap - thumbW : leftButtonX + leftButtonW + leftGap;
797 if (value < (max - sb.getVisibleAmount())) {
798 float thumbRange = trackW - thumbW;
799 if( ltr ) {
800 thumbX = (int)(0.5f + (thumbRange * ((value - min) / (range - extent))));
801 } else {
802 thumbX = (int)(0.5f + (thumbRange * ((max - extent - value) / (range - extent))));
803 }
804 thumbX += leftButtonX + leftButtonW + leftGap;
805 }
806
807 /* If the buttons don't fit, allocate half of the available
808 * space to each and move the right one over.
809 */
810 int sbAvailButtonW = (sbSize.width - sbInsetsW);
811 if (sbAvailButtonW < sbButtonsW) {
812 rightButtonW = leftButtonW = sbAvailButtonW / 2;
813 rightButtonX = sbSize.width - (sbInsets.right + rightButtonW + rightGap);
814 }
815
816 (ltr ? decrButton : incrButton).setBounds(leftButtonX, itemY, leftButtonW, itemH);
817 (ltr ? incrButton : decrButton).setBounds(rightButtonX, itemY, rightButtonW, itemH);
818
819 /* Update the trackRect field.
820 */
821 int itrackX = leftButtonX + leftButtonW + leftGap;
822 int itrackW = rightButtonX - rightGap - itrackX;
823 trackRect.setBounds(itrackX, itemY, itrackW, itemH);
824
825 /* Make sure the thumb fits between the buttons. Note
826 * that setting the thumbs bounds causes a repaint.
827 */
828 if (thumbW >= (int)trackW) {
829 if (UIManager.getBoolean("ScrollBar.alwaysShowThumb")) {
830 // This is used primarily for GTK L&F, which expands the
831 // thumb to fit the track when it would otherwise be hidden.
832 setThumbBounds(itrackX, itemY, itrackW, itemH);
833 } else {
834 // Other L&F's simply hide the thumb in this case.
835 setThumbBounds(0, 0, 0, 0);
836 }
837 }
838 else {
839 if (thumbX + thumbW > rightButtonX - rightGap) {
840 thumbX = rightButtonX - rightGap - thumbW;
841 }
842 if (thumbX < leftButtonX + leftButtonW + leftGap) {
843 thumbX = leftButtonX + leftButtonW + leftGap + 1;
844 }
845 setThumbBounds(thumbX, itemY, thumbW, itemH);
846 }
847 }
848
849 public void layoutContainer(Container scrollbarContainer)
850 {
851 /* If the user is dragging the value, we'll assume that the
852 * scrollbars layout is OK modulo the thumb which is being
853 * handled by the dragging code.
854 */
855 if (isDragging) {
856 return;
857 }
858
859 JScrollBar scrollbar = (JScrollBar)scrollbarContainer;
860 switch (scrollbar.getOrientation()) {
861 case JScrollBar.VERTICAL:
862 layoutVScrollbar(scrollbar);
863 break;
864
865 case JScrollBar.HORIZONTAL:
866 layoutHScrollbar(scrollbar);
867 break;
868 }
869 }
870
871
872 /**
873 * Set the bounds of the thumb and force a repaint that includes
874 * the old thumbBounds and the new one.
875 *
876 * @see #getThumbBounds
877 */
878 protected void setThumbBounds(int x, int y, int width, int height)
879 {
880 /* If the thumbs bounds haven't changed, we're done.
881 */
882 if ((thumbRect.x == x) &&
883 (thumbRect.y == y) &&
884 (thumbRect.width == width) &&
885 (thumbRect.height == height)) {
886 return;
887 }
888
889 /* Update thumbRect, and repaint the union of x,y,w,h and
890 * the old thumbRect.
891 */
892 int minX = Math.min(x, thumbRect.x);
893 int minY = Math.min(y, thumbRect.y);
894 int maxX = Math.max(x + width, thumbRect.x + thumbRect.width);
895 int maxY = Math.max(y + height, thumbRect.y + thumbRect.height);
896
897 thumbRect.setBounds(x, y, width, height);
898 scrollbar.repaint(minX, minY, maxX - minX, maxY - minY);
899
900 // Once there is API to determine the mouse location this will need
901 // to be changed.
902 setThumbRollover(false);
903 }
904
905
906 /**
907 * Return the current size/location of the thumb.
908 * <p>
909 * <b>Warning </b>: the value returned by this method should not be
910 * be modified, it's a reference to the actual rectangle, not a copy.
911 *
912 * @return The current size/location of the thumb.
913 * @see #setThumbBounds
914 */
915 protected Rectangle getThumbBounds() {
916 return thumbRect;
917 }
918
919
920 /**
921 * Returns the current bounds of the track, i.e. the space in between
922 * the increment and decrement buttons, less the insets. The value
923 * returned by this method is updated each time the scrollbar is
924 * laid out (validated).
925 * <p>
926 * <b>Warning </b>: the value returned by this method should not be
927 * be modified, it's a reference to the actual rectangle, not a copy.
928 *
929 * @return the current bounds of the scrollbar track
930 * @see #layoutContainer
931 */
932 protected Rectangle getTrackBounds() {
933 return trackRect;
934 }
935
936 /*
937 * Method for scrolling by a block increment.
938 * Added for mouse wheel scrolling support, RFE 4202656.
939 */
940 static void scrollByBlock(JScrollBar scrollbar, int direction) {
941 // This method is called from BasicScrollPaneUI to implement wheel
942 // scrolling, and also from scrollByBlock().
943 int oldValue = scrollbar.getValue();
944 int blockIncrement = scrollbar.getBlockIncrement(direction);
945 int delta = blockIncrement * ((direction > 0) ? +1 : -1);
946 int newValue = oldValue + delta;
947
948 // Check for overflow.
949 if (delta > 0 && newValue < oldValue) {
950 newValue = scrollbar.getMaximum();
951 }
952 else if (delta < 0 && newValue > oldValue) {
953 newValue = scrollbar.getMinimum();
954 }
955
956 scrollbar.setValue(newValue);
957 }
958
959 protected void scrollByBlock(int direction)
960 {
961 scrollByBlock(scrollbar, direction);
962 trackHighlight = direction > 0 ? INCREASE_HIGHLIGHT : DECREASE_HIGHLIGHT;
963 Rectangle dirtyRect = getTrackBounds();
964 scrollbar.repaint(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
965 }
966
967 /*
968 * Method for scrolling by a unit increment.
969 * Added for mouse wheel scrolling support, RFE 4202656.
970 *
971 * If limitByBlock is set to true, the scrollbar will scroll at least 1
972 * unit increment, but will not scroll farther than the block increment.
973 * See BasicScrollPaneUI.Handler.mouseWheelMoved().
974 */
975 static void scrollByUnits(JScrollBar scrollbar, int direction,
976 int units, boolean limitToBlock) {
977 // This method is called from BasicScrollPaneUI to implement wheel
978 // scrolling, as well as from scrollByUnit().
979 int delta;
980 int limit = -1;
981
982 if (limitToBlock) {
983 if (direction < 0) {
984 limit = scrollbar.getValue() -
985 scrollbar.getBlockIncrement(direction);
986 }
987 else {
988 limit = scrollbar.getValue() +
989 scrollbar.getBlockIncrement(direction);
990 }
991 }
992
993 for (int i=0; i<units; i++) {
994 if (direction > 0) {
995 delta = scrollbar.getUnitIncrement(direction);
996 }
997 else {
998 delta = -scrollbar.getUnitIncrement(direction);
999 }
1000
1001 int oldValue = scrollbar.getValue();
1002 int newValue = oldValue + delta;
1003
1004 // Check for overflow.
1005 if (delta > 0 && newValue < oldValue) {
1006 newValue = scrollbar.getMaximum();
1007 }
1008 else if (delta < 0 && newValue > oldValue) {
1009 newValue = scrollbar.getMinimum();
1010 }
1011 if (oldValue == newValue) {
1012 break;
1013 }
1014
1015 if (limitToBlock && i > 0) {
1016 assert limit != -1;
1017 if ((direction < 0 && newValue < limit) ||
1018 (direction > 0 && newValue > limit)) {
1019 break;
1020 }
1021 }
1022 scrollbar.setValue(newValue);
1023 }
1024 }
1025
1026 protected void scrollByUnit(int direction) {
1027 scrollByUnits(scrollbar, direction, 1, false);
1028 }
1029
1030 /**
1031 * Indicates whether the user can absolutely position the thumb with
1032 * a mouse gesture (usually the middle mouse button).
1033 *
1034 * @return true if a mouse gesture can absolutely position the thumb
1035 * @since 1.5
1036 */
1037 public boolean getSupportsAbsolutePositioning() {
1038 return supportsAbsolutePositioning;
1039 }
1040
1041 /**
1042 * A listener to listen for model changes.
1043 *
1044 */
1045 protected class ModelListener implements ChangeListener {
1046 public void stateChanged(ChangeEvent e) {
1047 if (!useCachedValue) {
1048 scrollBarValue = scrollbar.getValue();
1049 }
1050 layoutContainer(scrollbar);
1051 useCachedValue = false;
1052 }
1053 }
1054
1055
1056 /**
1057 * Track mouse drags.
1058 */
1059 protected class TrackListener
1060 extends MouseAdapter implements MouseMotionListener
1061 {
1062 protected transient int offset;
1063 protected transient int currentMouseX, currentMouseY;
1064 private transient int direction = +1;
1065
1066 public void mouseReleased(MouseEvent e)
1067 {
1068 if (isDragging) {
1069 updateThumbState(e.getX(), e.getY());
1070 }
1071 if (SwingUtilities.isRightMouseButton(e) ||
1072 (!getSupportsAbsolutePositioning() &&
1073 SwingUtilities.isMiddleMouseButton(e)))
1074 return;
1075 if(!scrollbar.isEnabled())
1076 return;
1077
1078 Rectangle r = getTrackBounds();
1079 scrollbar.repaint(r.x, r.y, r.width, r.height);
1080
1081 trackHighlight = NO_HIGHLIGHT;
1082 isDragging = false;
1083 offset = 0;
1084 scrollTimer.stop();
1085 useCachedValue = true;
1086 scrollbar.setValueIsAdjusting(false);
1087 }
1088
1089
1090 /**
1091 * If the mouse is pressed above the "thumb" component
1092 * then reduce the scrollbars value by one page ("page up"),
1093 * otherwise increase it by one page. If there is no
1094 * thumb then page up if the mouse is in the upper half
1095 * of the track.
1096 */
1097 public void mousePressed(MouseEvent e)
1098 {
1099 if (SwingUtilities.isRightMouseButton(e) ||
1100 (!getSupportsAbsolutePositioning() &&
1101 SwingUtilities.isMiddleMouseButton(e)))
1102 return;
1103 if(!scrollbar.isEnabled())
1104 return;
1105
1106 if (!scrollbar.hasFocus() && scrollbar.isRequestFocusEnabled()) {
1107 scrollbar.requestFocus();
1108 }
1109
1110 useCachedValue = true;
1111 scrollbar.setValueIsAdjusting(true);
1112
1113 currentMouseX = e.getX();
1114 currentMouseY = e.getY();
1115
1116 // Clicked in the Thumb area?
1117 if(getThumbBounds().contains(currentMouseX, currentMouseY)) {
1118 switch (scrollbar.getOrientation()) {
1119 case JScrollBar.VERTICAL:
1120 offset = currentMouseY - getThumbBounds().y;
1121 break;
1122 case JScrollBar.HORIZONTAL:
1123 offset = currentMouseX - getThumbBounds().x;
1124 break;
1125 }
1126 isDragging = true;
1127 return;
1128 }
1129 else if (getSupportsAbsolutePositioning() &&
1130 SwingUtilities.isMiddleMouseButton(e)) {
1131 switch (scrollbar.getOrientation()) {
1132 case JScrollBar.VERTICAL:
1133 offset = getThumbBounds().height / 2;
1134 break;
1135 case JScrollBar.HORIZONTAL:
1136 offset = getThumbBounds().width / 2;
1137 break;
1138 }
1139 isDragging = true;
1140 setValueFrom(e);
1141 return;
1142 }
1143 isDragging = false;
1144
1145 Dimension sbSize = scrollbar.getSize();
1146 direction = +1;
1147
1148 switch (scrollbar.getOrientation()) {
1149 case JScrollBar.VERTICAL:
1150 if (getThumbBounds().isEmpty()) {
1151 int scrollbarCenter = sbSize.height / 2;
1152 direction = (currentMouseY < scrollbarCenter) ? -1 : +1;
1153 } else {
1154 int thumbY = getThumbBounds().y;
1155 direction = (currentMouseY < thumbY) ? -1 : +1;
1156 }
1157 break;
1158 case JScrollBar.HORIZONTAL:
1159 if (getThumbBounds().isEmpty()) {
1160 int scrollbarCenter = sbSize.width / 2;
1161 direction = (currentMouseX < scrollbarCenter) ? -1 : +1;
1162 } else {
1163 int thumbX = getThumbBounds().x;
1164 direction = (currentMouseX < thumbX) ? -1 : +1;
1165 }
1166 if (!scrollbar.getComponentOrientation().isLeftToRight()) {
1167 direction = -direction;
1168 }
1169 break;
1170 }
1171 scrollByBlock(direction);
1172
1173 scrollTimer.stop();
1174 scrollListener.setDirection(direction);
1175 scrollListener.setScrollByBlock(true);
1176 startScrollTimerIfNecessary();
1177 }
1178
1179
1180 /**
1181 * Set the models value to the position of the thumb's top of Vertical
1182 * scrollbar, or the left/right of Horizontal scrollbar in
1183 * left-to-right/right-to-left scrollbar relative to the origin of the
1184 * track.
1185 */
1186 public void mouseDragged(MouseEvent e) {
1187 if (SwingUtilities.isRightMouseButton(e) ||
1188 (!getSupportsAbsolutePositioning() &&
1189 SwingUtilities.isMiddleMouseButton(e)))
1190 return;
1191 if(!scrollbar.isEnabled() || getThumbBounds().isEmpty()) {
1192 return;
1193 }
1194 if (isDragging) {
1195 setValueFrom(e);
1196 } else {
1197 currentMouseX = e.getX();
1198 currentMouseY = e.getY();
1199 updateThumbState(currentMouseX, currentMouseY);
1200 startScrollTimerIfNecessary();
1201 }
1202 }
1203
1204 private void setValueFrom(MouseEvent e) {
1205 boolean active = isThumbRollover();
1206 BoundedRangeModel model = scrollbar.getModel();
1207 Rectangle thumbR = getThumbBounds();
1208 float trackLength;
1209 int thumbMin, thumbMax, thumbPos;
1210
1211 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
1212 thumbMin = trackRect.y;
1213 thumbMax = trackRect.y + trackRect.height - thumbR.height;
1214 thumbPos = Math.min(thumbMax, Math.max(thumbMin, (e.getY() - offset)));
1215 setThumbBounds(thumbR.x, thumbPos, thumbR.width, thumbR.height);
1216 trackLength = getTrackBounds().height;
1217 }
1218 else {
1219 thumbMin = trackRect.x;
1220 thumbMax = trackRect.x + trackRect.width - thumbR.width;
1221 thumbPos = Math.min(thumbMax, Math.max(thumbMin, (e.getX() - offset)));
1222 setThumbBounds(thumbPos, thumbR.y, thumbR.width, thumbR.height);
1223 trackLength = getTrackBounds().width;
1224 }
1225
1226 /* Set the scrollbars value. If the thumb has reached the end of
1227 * the scrollbar, then just set the value to its maximum. Otherwise
1228 * compute the value as accurately as possible.
1229 */
1230 if (thumbPos == thumbMax) {
1231 if (scrollbar.getOrientation() == JScrollBar.VERTICAL ||
1232 scrollbar.getComponentOrientation().isLeftToRight()) {
1233 scrollbar.setValue(model.getMaximum() - model.getExtent());
1234 } else {
1235 scrollbar.setValue(model.getMinimum());
1236 }
1237 }
1238 else {
1239 float valueMax = model.getMaximum() - model.getExtent();
1240 float valueRange = valueMax - model.getMinimum();
1241 float thumbValue = thumbPos - thumbMin;
1242 float thumbRange = thumbMax - thumbMin;
1243 int value;
1244 if (scrollbar.getOrientation() == JScrollBar.VERTICAL ||
1245 scrollbar.getComponentOrientation().isLeftToRight()) {
1246 value = (int)(0.5 + ((thumbValue / thumbRange) * valueRange));
1247 } else {
1248 value = (int)(0.5 + (((thumbMax - thumbPos) / thumbRange) * valueRange));
1249 }
1250
1251 useCachedValue = true;
1252 scrollBarValue = value + model.getMinimum();
1253 scrollbar.setValue(adjustValueIfNecessary(scrollBarValue));
1254 }
1255 setThumbRollover(active);
1256 }
1257
1258 private int adjustValueIfNecessary(int value) {
1259 if (scrollbar.getParent() instanceof JScrollPane) {
1260 JScrollPane scrollpane = (JScrollPane)scrollbar.getParent();
1261 JViewport viewport = scrollpane.getViewport();
1262 Component view = viewport.getView();
1263 if (view instanceof JList) {
1264 JList list = (JList)view;
1265 if (DefaultLookup.getBoolean(list, list.getUI(),
1266 "List.lockToPositionOnScroll", false)) {
1267 int adjustedValue = value;
1268 int mode = list.getLayoutOrientation();
1269 int orientation = scrollbar.getOrientation();
1270 if (orientation == JScrollBar.VERTICAL && mode == JList.VERTICAL) {
1271 int index = list.locationToIndex(new Point(0, value));
1272 Rectangle rect = list.getCellBounds(index, index);
1273 if (rect != null) {
1274 adjustedValue = rect.y;
1275 }
1276 }
1277 if (orientation == JScrollBar.HORIZONTAL &&
1278 (mode == JList.VERTICAL_WRAP || mode == JList.HORIZONTAL_WRAP)) {
1279 if (scrollpane.getComponentOrientation().isLeftToRight()) {
1280 int index = list.locationToIndex(new Point(value, 0));
1281 Rectangle rect = list.getCellBounds(index, index);
1282 if (rect != null) {
1283 adjustedValue = rect.x;
1284 }
1285 }
1286 else {
1287 Point loc = new Point(value, 0);
1288 int extent = viewport.getExtentSize().width;
1289 loc.x += extent - 1;
1290 int index = list.locationToIndex(loc);
1291 Rectangle rect = list.getCellBounds(index, index);
1292 if (rect != null) {
1293 adjustedValue = rect.x + rect.width - extent;
1294 }
1295 }
1296 }
1297 value = adjustedValue;
1298
1299 }
1300 }
1301 }
1302 return value;
1303 }
1304
1305 private void startScrollTimerIfNecessary() {
1306 if (scrollTimer.isRunning()) {
1307 return;
1308 }
1309
1310 Rectangle tb = getThumbBounds();
1311
1312 switch (scrollbar.getOrientation()) {
1313 case JScrollBar.VERTICAL:
1314 if (direction > 0) {
1315 if (tb.y + tb.height < trackListener.currentMouseY) {
1316 scrollTimer.start();
1317 }
1318 } else if (tb.y > trackListener.currentMouseY) {
1319 scrollTimer.start();
1320 }
1321 break;
1322 case JScrollBar.HORIZONTAL:
1323 if ((direction > 0 && isMouseAfterThumb())
1324 || (direction < 0 && isMouseBeforeThumb())) {
1325
1326 scrollTimer.start();
1327 }
1328 break;
1329 }
1330 }
1331
1332 public void mouseMoved(MouseEvent e) {
1333 if (!isDragging) {
1334 updateThumbState(e.getX(), e.getY());
1335 }
1336 }
1337
1338 /**
1339 * Invoked when the mouse exits the scrollbar.
1340 *
1341 * @param e MouseEvent further describing the event
1342 * @since 1.5
1343 */
1344 public void mouseExited(MouseEvent e) {
1345 if (!isDragging) {
1346 setThumbRollover(false);
1347 }
1348 }
1349 }
1350
1351
1352 /**
1353 * Listener for cursor keys.
1354 */
1355 protected class ArrowButtonListener extends MouseAdapter
1356 {
1357 // Because we are handling both mousePressed and Actions
1358 // we need to make sure we don't fire under both conditions.
1359 // (keyfocus on scrollbars causes action without mousePress
1360 boolean handledEvent;
1361
1362 public void mousePressed(MouseEvent e) {
1363 if(!scrollbar.isEnabled()) { return; }
1364 // not an unmodified left mouse button
1365 //if(e.getModifiers() != InputEvent.BUTTON1_MASK) {return; }
1366 if( ! SwingUtilities.isLeftMouseButton(e)) { return; }
1367
1368 int direction = (e.getSource() == incrButton) ? 1 : -1;
1369
1370 scrollByUnit(direction);
1371 scrollTimer.stop();
1372 scrollListener.setDirection(direction);
1373 scrollListener.setScrollByBlock(false);
1374 scrollTimer.start();
1375
1376 handledEvent = true;
1377 if (!scrollbar.hasFocus() && scrollbar.isRequestFocusEnabled()) {
1378 scrollbar.requestFocus();
1379 }
1380 }
1381
1382 public void mouseReleased(MouseEvent e) {
1383 scrollTimer.stop();
1384 handledEvent = false;
1385 scrollbar.setValueIsAdjusting(false);
1386 }
1387 }
1388
1389
1390 /**
1391 * Listener for scrolling events initiated in the
1392 * <code>ScrollPane</code>.
1393 */
1394 protected class ScrollListener implements ActionListener
1395 {
1396 int direction = +1;
1397 boolean useBlockIncrement;
1398
1399 public ScrollListener() {
1400 direction = +1;
1401 useBlockIncrement = false;
1402 }
1403
1404 public ScrollListener(int dir, boolean block) {
1405 direction = dir;
1406 useBlockIncrement = block;
1407 }
1408
1409 public void setDirection(int direction) { this.direction = direction; }
1410 public void setScrollByBlock(boolean block) { this.useBlockIncrement = block; }
1411
1412 public void actionPerformed(ActionEvent e) {
1413 if(useBlockIncrement) {
1414 scrollByBlock(direction);
1415 // Stop scrolling if the thumb catches up with the mouse
1416 if(scrollbar.getOrientation() == JScrollBar.VERTICAL) {
1417 if(direction > 0) {
1418 if(getThumbBounds().y + getThumbBounds().height
1419 >= trackListener.currentMouseY)
1420 ((Timer)e.getSource()).stop();
1421 } else if(getThumbBounds().y <= trackListener.currentMouseY) {
1422 ((Timer)e.getSource()).stop();
1423 }
1424 } else {
1425 if ((direction > 0 && !isMouseAfterThumb())
1426 || (direction < 0 && !isMouseBeforeThumb())) {
1427
1428 ((Timer)e.getSource()).stop();
1429 }
1430 }
1431 } else {
1432 scrollByUnit(direction);
1433 }
1434
1435 if(direction > 0
1436 && scrollbar.getValue()+scrollbar.getVisibleAmount()
1437 >= scrollbar.getMaximum())
1438 ((Timer)e.getSource()).stop();
1439 else if(direction < 0
1440 && scrollbar.getValue() <= scrollbar.getMinimum())
1441 ((Timer)e.getSource()).stop();
1442 }
1443 }
1444
1445 private boolean isMouseLeftOfThumb() {
1446 return trackListener.currentMouseX < getThumbBounds().x;
1447 }
1448
1449 private boolean isMouseRightOfThumb() {
1450 Rectangle tb = getThumbBounds();
1451 return trackListener.currentMouseX > tb.x + tb.width;
1452 }
1453
1454 private boolean isMouseBeforeThumb() {
1455 return scrollbar.getComponentOrientation().isLeftToRight()
1456 ? isMouseLeftOfThumb()
1457 : isMouseRightOfThumb();
1458 }
1459
1460 private boolean isMouseAfterThumb() {
1461 return scrollbar.getComponentOrientation().isLeftToRight()
1462 ? isMouseRightOfThumb()
1463 : isMouseLeftOfThumb();
1464 }
1465
1466 private void updateButtonDirections() {
1467 int orient = scrollbar.getOrientation();
1468 if (scrollbar.getComponentOrientation().isLeftToRight()) {
1469 if (incrButton instanceof BasicArrowButton) {
1470 ((BasicArrowButton)incrButton).setDirection(
1471 orient == HORIZONTAL? EAST : SOUTH);
1472 }
1473 if (decrButton instanceof BasicArrowButton) {
1474 ((BasicArrowButton)decrButton).setDirection(
1475 orient == HORIZONTAL? WEST : NORTH);
1476 }
1477 }
1478 else {
1479 if (incrButton instanceof BasicArrowButton) {
1480 ((BasicArrowButton)incrButton).setDirection(
1481 orient == HORIZONTAL? WEST : SOUTH);
1482 }
1483 if (decrButton instanceof BasicArrowButton) {
1484 ((BasicArrowButton)decrButton).setDirection(
1485 orient == HORIZONTAL ? EAST : NORTH);
1486 }
1487 }
1488 }
1489
1490 public class PropertyChangeHandler implements PropertyChangeListener
1491 {
1492 // NOTE: This class exists only for backward compatability. All
1493 // its functionality has been moved into Handler. If you need to add
1494 // new functionality add it to the Handler, but make sure this
1495 // class calls into the Handler.
1496
1497 public void propertyChange(PropertyChangeEvent e) {
1498 getHandler().propertyChange(e);
1499 }
1500 }
1501
1502
1503 /**
1504 * Used for scrolling the scrollbar.
1505 */
1506 private static class Actions extends UIAction {
1507 private static final String POSITIVE_UNIT_INCREMENT =
1508 "positiveUnitIncrement";
1509 private static final String POSITIVE_BLOCK_INCREMENT =
1510 "positiveBlockIncrement";
1511 private static final String NEGATIVE_UNIT_INCREMENT =
1512 "negativeUnitIncrement";
1513 private static final String NEGATIVE_BLOCK_INCREMENT =
1514 "negativeBlockIncrement";
1515 private static final String MIN_SCROLL = "minScroll";
1516 private static final String MAX_SCROLL = "maxScroll";
1517
1518 Actions(String name) {
1519 super(name);
1520 }
1521
1522 public void actionPerformed(ActionEvent e) {
1523 JScrollBar scrollBar = (JScrollBar)e.getSource();
1524 String key = getName();
1525 if (key == POSITIVE_UNIT_INCREMENT) {
1526 scroll(scrollBar, POSITIVE_SCROLL, false);
1527 }
1528 else if (key == POSITIVE_BLOCK_INCREMENT) {
1529 scroll(scrollBar, POSITIVE_SCROLL, true);
1530 }
1531 else if (key == NEGATIVE_UNIT_INCREMENT) {
1532 scroll(scrollBar, NEGATIVE_SCROLL, false);
1533 }
1534 else if (key == NEGATIVE_BLOCK_INCREMENT) {
1535 scroll(scrollBar, NEGATIVE_SCROLL, true);
1536 }
1537 else if (key == MIN_SCROLL) {
1538 scroll(scrollBar, BasicScrollBarUI.MIN_SCROLL, true);
1539 }
1540 else if (key == MAX_SCROLL) {
1541 scroll(scrollBar, BasicScrollBarUI.MAX_SCROLL, true);
1542 }
1543 }
1544 private void scroll(JScrollBar scrollBar, int dir, boolean block) {
1545
1546 if (dir == NEGATIVE_SCROLL || dir == POSITIVE_SCROLL) {
1547 int amount;
1548 // Don't use the BasicScrollBarUI.scrollByXXX methods as we
1549 // don't want to use an invokeLater to reset the trackHighlight
1550 // via an invokeLater
1551 if (block) {
1552 if (dir == NEGATIVE_SCROLL) {
1553 amount = -1 * scrollBar.getBlockIncrement(-1);
1554 }
1555 else {
1556 amount = scrollBar.getBlockIncrement(1);
1557 }
1558 }
1559 else {
1560 if (dir == NEGATIVE_SCROLL) {
1561 amount = -1 * scrollBar.getUnitIncrement(-1);
1562 }
1563 else {
1564 amount = scrollBar.getUnitIncrement(1);
1565 }
1566 }
1567 scrollBar.setValue(scrollBar.getValue() + amount);
1568 }
1569 else if (dir == BasicScrollBarUI.MIN_SCROLL) {
1570 scrollBar.setValue(scrollBar.getMinimum());
1571 }
1572 else if (dir == BasicScrollBarUI.MAX_SCROLL) {
1573 scrollBar.setValue(scrollBar.getMaximum());
1574 }
1575 }
1576 }
1577
1578
1579 //
1580 // EventHandler
1581 //
1582 private class Handler implements FocusListener, PropertyChangeListener {
1583 //
1584 // FocusListener
1585 //
1586 public void focusGained(FocusEvent e) {
1587 scrollbar.repaint();
1588 }
1589
1590 public void focusLost(FocusEvent e) {
1591 scrollbar.repaint();
1592 }
1593
1594
1595 //
1596 // PropertyChangeListener
1597 //
1598 public void propertyChange(PropertyChangeEvent e) {
1599 String propertyName = e.getPropertyName();
1600
1601 if ("model" == propertyName) {
1602 BoundedRangeModel oldModel = (BoundedRangeModel)e.getOldValue();
1603 BoundedRangeModel newModel = (BoundedRangeModel)e.getNewValue();
1604 oldModel.removeChangeListener(modelListener);
1605 newModel.addChangeListener(modelListener);
1606 scrollBarValue = scrollbar.getValue();
1607 scrollbar.repaint();
1608 scrollbar.revalidate();
1609 } else if ("orientation" == propertyName) {
1610 updateButtonDirections();
1611 } else if ("componentOrientation" == propertyName) {
1612 updateButtonDirections();
1613 InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
1614 SwingUtilities.replaceUIInputMap(scrollbar, JComponent.WHEN_FOCUSED, inputMap);
1615 }
1616 }
1617 }
1618}