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+ */
25+package javax.swing.plaf.basic;
26+
27+
28+import sun.swing.DefaultLookup;
29+import sun.swing.UIAction;
30+
31+import java.awt.*;
32+import java.awt.event.*;
33+
34+import java.beans.*;
35+
36+import javax.swing.*;
37+import javax.swing.event.*;
38+import 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+ */
48+public 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+}