0001 /*
0002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025 package javax.swing.plaf.basic;
0026
0027 import sun.swing.DefaultLookup;
0028 import sun.swing.UIAction;
0029
0030 import java.awt.*;
0031 import java.awt.event.*;
0032
0033 import java.beans.*;
0034
0035 import javax.swing.*;
0036 import javax.swing.event.*;
0037 import javax.swing.plaf.*;
0038
0039 /**
0040 * Implementation of ScrollBarUI for the Basic Look and Feel
0041 *
0042 * @version 1.94 05/05/07
0043 * @author Rich Schiavi
0044 * @author David Kloba
0045 * @author Hans Muller
0046 */
0047 public class BasicScrollBarUI extends ScrollBarUI implements
0048 LayoutManager, SwingConstants {
0049 private static final int POSITIVE_SCROLL = 1;
0050 private static final int NEGATIVE_SCROLL = -1;
0051
0052 private static final int MIN_SCROLL = 2;
0053 private static final int MAX_SCROLL = 3;
0054
0055 // NOTE: DO NOT use this field directly, SynthScrollBarUI assumes you'll
0056 // call getMinimumThumbSize to access it.
0057 protected Dimension minimumThumbSize;
0058 protected Dimension maximumThumbSize;
0059
0060 protected Color thumbHighlightColor;
0061 protected Color thumbLightShadowColor;
0062 protected Color thumbDarkShadowColor;
0063 protected Color thumbColor;
0064 protected Color trackColor;
0065 protected Color trackHighlightColor;
0066
0067 protected JScrollBar scrollbar;
0068 protected JButton incrButton;
0069 protected JButton decrButton;
0070 protected boolean isDragging;
0071 protected TrackListener trackListener;
0072 protected ArrowButtonListener buttonListener;
0073 protected ModelListener modelListener;
0074
0075 protected Rectangle thumbRect;
0076 protected Rectangle trackRect;
0077
0078 protected int trackHighlight;
0079
0080 protected static final int NO_HIGHLIGHT = 0;
0081 protected static final int DECREASE_HIGHLIGHT = 1;
0082 protected static final int INCREASE_HIGHLIGHT = 2;
0083
0084 protected ScrollListener scrollListener;
0085 protected PropertyChangeListener propertyChangeListener;
0086 protected Timer scrollTimer;
0087
0088 private final static int scrollSpeedThrottle = 60; // delay in milli seconds
0089
0090 /** True indicates a middle click will absolutely position the
0091 * scrollbar. */
0092 private boolean supportsAbsolutePositioning;
0093
0094 /** Hint as to what width (when vertical) or height (when horizontal)
0095 * should be.
0096 */
0097 private int scrollBarWidth;
0098
0099 private Handler handler;
0100
0101 private boolean thumbActive;
0102
0103 /**
0104 * Determine whether scrollbar layout should use cached value or adjusted
0105 * value returned by scrollbar's <code>getValue</code>.
0106 */
0107 private boolean useCachedValue = false;
0108 /**
0109 * The scrollbar value is cached to save real value if the view is adjusted.
0110 */
0111 private int scrollBarValue;
0112
0113 static void loadActionMap(LazyActionMap map) {
0114 map.put(new Actions(Actions.POSITIVE_UNIT_INCREMENT));
0115 map.put(new Actions(Actions.POSITIVE_BLOCK_INCREMENT));
0116 map.put(new Actions(Actions.NEGATIVE_UNIT_INCREMENT));
0117 map.put(new Actions(Actions.NEGATIVE_BLOCK_INCREMENT));
0118 map.put(new Actions(Actions.MIN_SCROLL));
0119 map.put(new Actions(Actions.MAX_SCROLL));
0120 }
0121
0122 public static ComponentUI createUI(JComponent c) {
0123 return new BasicScrollBarUI();
0124 }
0125
0126 protected void configureScrollBarColors() {
0127 LookAndFeel.installColors(scrollbar, "ScrollBar.background",
0128 "ScrollBar.foreground");
0129 thumbHighlightColor = UIManager
0130 .getColor("ScrollBar.thumbHighlight");
0131 thumbLightShadowColor = UIManager
0132 .getColor("ScrollBar.thumbShadow");
0133 thumbDarkShadowColor = UIManager
0134 .getColor("ScrollBar.thumbDarkShadow");
0135 thumbColor = UIManager.getColor("ScrollBar.thumb");
0136 trackColor = UIManager.getColor("ScrollBar.track");
0137 trackHighlightColor = UIManager
0138 .getColor("ScrollBar.trackHighlight");
0139 }
0140
0141 public void installUI(JComponent c) {
0142 scrollbar = (JScrollBar) c;
0143 thumbRect = new Rectangle(0, 0, 0, 0);
0144 trackRect = new Rectangle(0, 0, 0, 0);
0145 installDefaults();
0146 installComponents();
0147 installListeners();
0148 installKeyboardActions();
0149 }
0150
0151 public void uninstallUI(JComponent c) {
0152 scrollbar = (JScrollBar) c;
0153 uninstallListeners();
0154 uninstallDefaults();
0155 uninstallComponents();
0156 uninstallKeyboardActions();
0157 thumbRect = null;
0158 scrollbar = null;
0159 incrButton = null;
0160 decrButton = null;
0161 }
0162
0163 protected void installDefaults() {
0164 scrollBarWidth = UIManager.getInt("ScrollBar.width");
0165 if (scrollBarWidth <= 0) {
0166 scrollBarWidth = 16;
0167 }
0168 minimumThumbSize = (Dimension) UIManager
0169 .get("ScrollBar.minimumThumbSize");
0170 maximumThumbSize = (Dimension) UIManager
0171 .get("ScrollBar.maximumThumbSize");
0172
0173 Boolean absB = (Boolean) UIManager
0174 .get("ScrollBar.allowsAbsolutePositioning");
0175 supportsAbsolutePositioning = (absB != null) ? absB
0176 .booleanValue() : false;
0177
0178 trackHighlight = NO_HIGHLIGHT;
0179 if (scrollbar.getLayout() == null
0180 || (scrollbar.getLayout() instanceof UIResource)) {
0181 scrollbar.setLayout(this );
0182 }
0183 configureScrollBarColors();
0184 LookAndFeel.installBorder(scrollbar, "ScrollBar.border");
0185 LookAndFeel.installProperty(scrollbar, "opaque", Boolean.TRUE);
0186
0187 scrollBarValue = scrollbar.getValue();
0188 }
0189
0190 protected void installComponents() {
0191 switch (scrollbar.getOrientation()) {
0192 case JScrollBar.VERTICAL:
0193 incrButton = createIncreaseButton(SOUTH);
0194 decrButton = createDecreaseButton(NORTH);
0195 break;
0196
0197 case JScrollBar.HORIZONTAL:
0198 if (scrollbar.getComponentOrientation().isLeftToRight()) {
0199 incrButton = createIncreaseButton(EAST);
0200 decrButton = createDecreaseButton(WEST);
0201 } else {
0202 incrButton = createIncreaseButton(WEST);
0203 decrButton = createDecreaseButton(EAST);
0204 }
0205 break;
0206 }
0207 scrollbar.add(incrButton);
0208 scrollbar.add(decrButton);
0209 // Force the children's enabled state to be updated.
0210 scrollbar.setEnabled(scrollbar.isEnabled());
0211 }
0212
0213 protected void uninstallComponents() {
0214 scrollbar.remove(incrButton);
0215 scrollbar.remove(decrButton);
0216 }
0217
0218 protected void installListeners() {
0219 trackListener = createTrackListener();
0220 buttonListener = createArrowButtonListener();
0221 modelListener = createModelListener();
0222 propertyChangeListener = createPropertyChangeListener();
0223
0224 scrollbar.addMouseListener(trackListener);
0225 scrollbar.addMouseMotionListener(trackListener);
0226 scrollbar.getModel().addChangeListener(modelListener);
0227 scrollbar.addPropertyChangeListener(propertyChangeListener);
0228 scrollbar.addFocusListener(getHandler());
0229
0230 if (incrButton != null) {
0231 incrButton.addMouseListener(buttonListener);
0232 }
0233 if (decrButton != null) {
0234 decrButton.addMouseListener(buttonListener);
0235 }
0236
0237 scrollListener = createScrollListener();
0238 scrollTimer = new Timer(scrollSpeedThrottle, scrollListener);
0239 scrollTimer.setInitialDelay(300); // default InitialDelay?
0240 }
0241
0242 protected void installKeyboardActions() {
0243 LazyActionMap.installLazyActionMap(scrollbar,
0244 BasicScrollBarUI.class, "ScrollBar.actionMap");
0245
0246 InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
0247 SwingUtilities.replaceUIInputMap(scrollbar,
0248 JComponent.WHEN_FOCUSED, inputMap);
0249 inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0250 SwingUtilities
0251 .replaceUIInputMap(scrollbar,
0252 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
0253 inputMap);
0254 }
0255
0256 protected void uninstallKeyboardActions() {
0257 SwingUtilities.replaceUIInputMap(scrollbar,
0258 JComponent.WHEN_FOCUSED, null);
0259 SwingUtilities.replaceUIActionMap(scrollbar, null);
0260 }
0261
0262 private InputMap getInputMap(int condition) {
0263 if (condition == JComponent.WHEN_FOCUSED) {
0264 InputMap keyMap = (InputMap) DefaultLookup.get(scrollbar,
0265 this , "ScrollBar.focusInputMap");
0266 InputMap rtlKeyMap;
0267
0268 if (scrollbar.getComponentOrientation().isLeftToRight()
0269 || ((rtlKeyMap = (InputMap) DefaultLookup.get(
0270 scrollbar, this ,
0271 "ScrollBar.focusInputMap.RightToLeft")) == null)) {
0272 return keyMap;
0273 } else {
0274 rtlKeyMap.setParent(keyMap);
0275 return rtlKeyMap;
0276 }
0277 } else if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
0278 InputMap keyMap = (InputMap) DefaultLookup.get(scrollbar,
0279 this , "ScrollBar.ancestorInputMap");
0280 InputMap rtlKeyMap;
0281
0282 if (scrollbar.getComponentOrientation().isLeftToRight()
0283 || ((rtlKeyMap = (InputMap) DefaultLookup.get(
0284 scrollbar, this ,
0285 "ScrollBar.ancestorInputMap.RightToLeft")) == null)) {
0286 return keyMap;
0287 } else {
0288 rtlKeyMap.setParent(keyMap);
0289 return rtlKeyMap;
0290 }
0291 }
0292 return null;
0293 }
0294
0295 protected void uninstallListeners() {
0296 scrollTimer.stop();
0297 scrollTimer = null;
0298
0299 if (decrButton != null) {
0300 decrButton.removeMouseListener(buttonListener);
0301 }
0302 if (incrButton != null) {
0303 incrButton.removeMouseListener(buttonListener);
0304 }
0305
0306 scrollbar.getModel().removeChangeListener(modelListener);
0307 scrollbar.removeMouseListener(trackListener);
0308 scrollbar.removeMouseMotionListener(trackListener);
0309 scrollbar.removePropertyChangeListener(propertyChangeListener);
0310 scrollbar.removeFocusListener(getHandler());
0311 handler = null;
0312 }
0313
0314 protected void uninstallDefaults() {
0315 LookAndFeel.uninstallBorder(scrollbar);
0316 if (scrollbar.getLayout() == this ) {
0317 scrollbar.setLayout(null);
0318 }
0319 }
0320
0321 private Handler getHandler() {
0322 if (handler == null) {
0323 handler = new Handler();
0324 }
0325 return handler;
0326 }
0327
0328 protected TrackListener createTrackListener() {
0329 return new TrackListener();
0330 }
0331
0332 protected ArrowButtonListener createArrowButtonListener() {
0333 return new ArrowButtonListener();
0334 }
0335
0336 protected ModelListener createModelListener() {
0337 return new ModelListener();
0338 }
0339
0340 protected ScrollListener createScrollListener() {
0341 return new ScrollListener();
0342 }
0343
0344 protected PropertyChangeListener createPropertyChangeListener() {
0345 return getHandler();
0346 }
0347
0348 private void updateThumbState(int x, int y) {
0349 Rectangle rect = getThumbBounds();
0350
0351 setThumbRollover(rect.contains(x, y));
0352 }
0353
0354 /**
0355 * Sets whether or not the mouse is currently over the thumb.
0356 *
0357 * @param active True indicates the thumb is currently active.
0358 * @since 1.5
0359 */
0360 protected void setThumbRollover(boolean active) {
0361 if (thumbActive != active) {
0362 thumbActive = active;
0363 scrollbar.repaint(getThumbBounds());
0364 }
0365 }
0366
0367 /**
0368 * Returns true if the mouse is currently over the thumb.
0369 *
0370 * @return true if the thumb is currently active
0371 * @since 1.5
0372 */
0373 public boolean isThumbRollover() {
0374 return thumbActive;
0375 }
0376
0377 public void paint(Graphics g, JComponent c) {
0378 paintTrack(g, c, getTrackBounds());
0379 Rectangle thumbBounds = getThumbBounds();
0380 if (thumbBounds.intersects(g.getClipBounds())) {
0381 paintThumb(g, c, thumbBounds);
0382 }
0383 }
0384
0385 /**
0386 * A vertical scrollbar's preferred width is the maximum of
0387 * preferred widths of the (non <code>null</code>)
0388 * increment/decrement buttons,
0389 * and the minimum width of the thumb. The preferred height is the
0390 * sum of the preferred heights of the same parts. The basis for
0391 * the preferred size of a horizontal scrollbar is similar.
0392 * <p>
0393 * The <code>preferredSize</code> is only computed once, subsequent
0394 * calls to this method just return a cached size.
0395 *
0396 * @param c the <code>JScrollBar</code> that's delegating this method to us
0397 * @return the preferred size of a Basic JScrollBar
0398 * @see #getMaximumSize
0399 * @see #getMinimumSize
0400 */
0401 public Dimension getPreferredSize(JComponent c) {
0402 return (scrollbar.getOrientation() == JScrollBar.VERTICAL) ? new Dimension(
0403 scrollBarWidth, 48)
0404 : new Dimension(48, scrollBarWidth);
0405 }
0406
0407 /**
0408 * @param c The JScrollBar that's delegating this method to us.
0409 * @return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
0410 * @see #getMinimumSize
0411 * @see #getPreferredSize
0412 */
0413 public Dimension getMaximumSize(JComponent c) {
0414 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
0415 }
0416
0417 protected JButton createDecreaseButton(int orientation) {
0418 return new BasicArrowButton(orientation, UIManager
0419 .getColor("ScrollBar.thumb"), UIManager
0420 .getColor("ScrollBar.thumbShadow"), UIManager
0421 .getColor("ScrollBar.thumbDarkShadow"), UIManager
0422 .getColor("ScrollBar.thumbHighlight"));
0423 }
0424
0425 protected JButton createIncreaseButton(int orientation) {
0426 return new BasicArrowButton(orientation, UIManager
0427 .getColor("ScrollBar.thumb"), UIManager
0428 .getColor("ScrollBar.thumbShadow"), UIManager
0429 .getColor("ScrollBar.thumbDarkShadow"), UIManager
0430 .getColor("ScrollBar.thumbHighlight"));
0431 }
0432
0433 protected void paintDecreaseHighlight(Graphics g) {
0434 Insets insets = scrollbar.getInsets();
0435 Rectangle thumbR = getThumbBounds();
0436 g.setColor(trackHighlightColor);
0437
0438 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
0439 int x = insets.left;
0440 int y = decrButton.getY() + decrButton.getHeight();
0441 int w = scrollbar.getWidth() - (insets.left + insets.right);
0442 int h = thumbR.y - y;
0443 g.fillRect(x, y, w, h);
0444 } else {
0445 int x, w;
0446 if (scrollbar.getComponentOrientation().isLeftToRight()) {
0447 x = decrButton.getX() + decrButton.getWidth();
0448 w = thumbR.x - x;
0449 } else {
0450 x = thumbR.x + thumbR.width;
0451 w = decrButton.getX() - x;
0452 }
0453 int y = insets.top;
0454 int h = scrollbar.getHeight()
0455 - (insets.top + insets.bottom);
0456 g.fillRect(x, y, w, h);
0457 }
0458 }
0459
0460 protected void paintIncreaseHighlight(Graphics g) {
0461 Insets insets = scrollbar.getInsets();
0462 Rectangle thumbR = getThumbBounds();
0463 g.setColor(trackHighlightColor);
0464
0465 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
0466 int x = insets.left;
0467 int y = thumbR.y + thumbR.height;
0468 int w = scrollbar.getWidth() - (insets.left + insets.right);
0469 int h = incrButton.getY() - y;
0470 g.fillRect(x, y, w, h);
0471 } else {
0472 int x, w;
0473 if (scrollbar.getComponentOrientation().isLeftToRight()) {
0474 x = thumbR.x + thumbR.width;
0475 w = incrButton.getX() - x;
0476 } else {
0477 x = incrButton.getX() + incrButton.getWidth();
0478 w = thumbR.x - x;
0479 }
0480 int y = insets.top;
0481 int h = scrollbar.getHeight()
0482 - (insets.top + insets.bottom);
0483 g.fillRect(x, y, w, h);
0484 }
0485 }
0486
0487 protected void paintTrack(Graphics g, JComponent c,
0488 Rectangle trackBounds) {
0489 g.setColor(trackColor);
0490 g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width,
0491 trackBounds.height);
0492
0493 if (trackHighlight == DECREASE_HIGHLIGHT) {
0494 paintDecreaseHighlight(g);
0495 } else if (trackHighlight == INCREASE_HIGHLIGHT) {
0496 paintIncreaseHighlight(g);
0497 }
0498 }
0499
0500 protected void paintThumb(Graphics g, JComponent c,
0501 Rectangle thumbBounds) {
0502 if (thumbBounds.isEmpty() || !scrollbar.isEnabled()) {
0503 return;
0504 }
0505
0506 int w = thumbBounds.width;
0507 int h = thumbBounds.height;
0508
0509 g.translate(thumbBounds.x, thumbBounds.y);
0510
0511 g.setColor(thumbDarkShadowColor);
0512 g.drawRect(0, 0, w - 1, h - 1);
0513 g.setColor(thumbColor);
0514 g.fillRect(0, 0, w - 1, h - 1);
0515
0516 g.setColor(thumbHighlightColor);
0517 g.drawLine(1, 1, 1, h - 2);
0518 g.drawLine(2, 1, w - 3, 1);
0519
0520 g.setColor(thumbLightShadowColor);
0521 g.drawLine(2, h - 2, w - 2, h - 2);
0522 g.drawLine(w - 2, 1, w - 2, h - 3);
0523
0524 g.translate(-thumbBounds.x, -thumbBounds.y);
0525 }
0526
0527 /**
0528 * Return the smallest acceptable size for the thumb. If the scrollbar
0529 * becomes so small that this size isn't available, the thumb will be
0530 * hidden.
0531 * <p>
0532 * <b>Warning </b>: the value returned by this method should not be
0533 * be modified, it's a shared static constant.
0534 *
0535 * @return The smallest acceptable size for the thumb.
0536 * @see #getMaximumThumbSize
0537 */
0538 protected Dimension getMinimumThumbSize() {
0539 return minimumThumbSize;
0540 }
0541
0542 /**
0543 * Return the largest acceptable size for the thumb. To create a fixed
0544 * size thumb one make this method and <code>getMinimumThumbSize</code>
0545 * return the same value.
0546 * <p>
0547 * <b>Warning </b>: the value returned by this method should not be
0548 * be modified, it's a shared static constant.
0549 *
0550 * @return The largest acceptable size for the thumb.
0551 * @see #getMinimumThumbSize
0552 */
0553 protected Dimension getMaximumThumbSize() {
0554 return maximumThumbSize;
0555 }
0556
0557 /*
0558 * LayoutManager Implementation
0559 */
0560
0561 public void addLayoutComponent(String name, Component child) {
0562 }
0563
0564 public void removeLayoutComponent(Component child) {
0565 }
0566
0567 public Dimension preferredLayoutSize(Container scrollbarContainer) {
0568 return getPreferredSize((JComponent) scrollbarContainer);
0569 }
0570
0571 public Dimension minimumLayoutSize(Container scrollbarContainer) {
0572 return getMinimumSize((JComponent) scrollbarContainer);
0573 }
0574
0575 private int getValue(JScrollBar sb) {
0576 return (useCachedValue) ? scrollBarValue : sb.getValue();
0577 }
0578
0579 protected void layoutVScrollbar(JScrollBar sb) {
0580 Dimension sbSize = sb.getSize();
0581 Insets sbInsets = sb.getInsets();
0582
0583 /*
0584 * Width and left edge of the buttons and thumb.
0585 */
0586 int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
0587 int itemX = sbInsets.left;
0588
0589 /* Nominal locations of the buttons, assuming their preferred
0590 * size will fit.
0591 */
0592 boolean squareButtons = DefaultLookup.getBoolean(scrollbar,
0593 this , "ScrollBar.squareButtons", false);
0594 int decrButtonH = squareButtons ? itemW : decrButton
0595 .getPreferredSize().height;
0596 int decrButtonY = sbInsets.top;
0597
0598 int incrButtonH = squareButtons ? itemW : incrButton
0599 .getPreferredSize().height;
0600 int incrButtonY = sbSize.height
0601 - (sbInsets.bottom + incrButtonH);
0602
0603 /* The thumb must fit within the height left over after we
0604 * subtract the preferredSize of the buttons and the insets.
0605 */
0606 int sbInsetsH = sbInsets.top + sbInsets.bottom;
0607 int sbButtonsH = decrButtonH + incrButtonH;
0608 float trackH = sbSize.height - (sbInsetsH + sbButtonsH);
0609
0610 /* Compute the height and origin of the thumb. The case
0611 * where the thumb is at the bottom edge is handled specially
0612 * to avoid numerical problems in computing thumbY. Enforce
0613 * the thumbs min/max dimensions. If the thumb doesn't
0614 * fit in the track (trackH) we'll hide it later.
0615 */
0616 float min = sb.getMinimum();
0617 float extent = sb.getVisibleAmount();
0618 float range = sb.getMaximum() - min;
0619 float value = getValue(sb);
0620
0621 int thumbH = (range <= 0) ? getMaximumThumbSize().height
0622 : (int) (trackH * (extent / range));
0623 thumbH = Math.max(thumbH, getMinimumThumbSize().height);
0624 thumbH = Math.min(thumbH, getMaximumThumbSize().height);
0625
0626 int thumbY = incrButtonY - thumbH;
0627 if (value < (sb.getMaximum() - sb.getVisibleAmount())) {
0628 float thumbRange = trackH - thumbH;
0629 thumbY = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
0630 thumbY += decrButtonY + decrButtonH;
0631 }
0632
0633 /* If the buttons don't fit, allocate half of the available
0634 * space to each and move the lower one (incrButton) down.
0635 */
0636 int sbAvailButtonH = (sbSize.height - sbInsetsH);
0637 if (sbAvailButtonH < sbButtonsH) {
0638 incrButtonH = decrButtonH = sbAvailButtonH / 2;
0639 incrButtonY = sbSize.height
0640 - (sbInsets.bottom + incrButtonH);
0641 }
0642 decrButton.setBounds(itemX, decrButtonY, itemW, decrButtonH);
0643 incrButton.setBounds(itemX, incrButtonY, itemW, incrButtonH);
0644
0645 /* Update the trackRect field.
0646 */
0647 int itrackY = decrButtonY + decrButtonH;
0648 int itrackH = incrButtonY - itrackY;
0649 trackRect.setBounds(itemX, itrackY, itemW, itrackH);
0650
0651 /* If the thumb isn't going to fit, zero it's bounds. Otherwise
0652 * make sure it fits between the buttons. Note that setting the
0653 * thumbs bounds will cause a repaint.
0654 */
0655 if (thumbH >= (int) trackH) {
0656 if (UIManager.getBoolean("ScrollBar.alwaysShowThumb")) {
0657 // This is used primarily for GTK L&F, which expands the
0658 // thumb to fit the track when it would otherwise be hidden.
0659 setThumbBounds(itemX, itrackY, itemW, itrackH);
0660 } else {
0661 // Other L&F's simply hide the thumb in this case.
0662 setThumbBounds(0, 0, 0, 0);
0663 }
0664 } else {
0665 if ((thumbY + thumbH) > incrButtonY) {
0666 thumbY = incrButtonY - thumbH;
0667 }
0668 if (thumbY < (decrButtonY + decrButtonH)) {
0669 thumbY = decrButtonY + decrButtonH + 1;
0670 }
0671 setThumbBounds(itemX, thumbY, itemW, thumbH);
0672 }
0673 }
0674
0675 protected void layoutHScrollbar(JScrollBar sb) {
0676 Dimension sbSize = sb.getSize();
0677 Insets sbInsets = sb.getInsets();
0678
0679 /* Height and top edge of the buttons and thumb.
0680 */
0681 int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);
0682 int itemY = sbInsets.top;
0683
0684 boolean ltr = sb.getComponentOrientation().isLeftToRight();
0685
0686 /* Nominal locations of the buttons, assuming their preferred
0687 * size will fit.
0688 */
0689 boolean squareButtons = DefaultLookup.getBoolean(scrollbar,
0690 this , "ScrollBar.squareButtons", false);
0691 int leftButtonW = squareButtons ? itemH : decrButton
0692 .getPreferredSize().width;
0693 int rightButtonW = squareButtons ? itemH : incrButton
0694 .getPreferredSize().width;
0695 if (!ltr) {
0696 int temp = leftButtonW;
0697 leftButtonW = rightButtonW;
0698 rightButtonW = temp;
0699 }
0700 int leftButtonX = sbInsets.left;
0701 int rightButtonX = sbSize.width
0702 - (sbInsets.right + rightButtonW);
0703
0704 /* The thumb must fit within the width left over after we
0705 * subtract the preferredSize of the buttons and the insets.
0706 */
0707 int sbInsetsW = sbInsets.left + sbInsets.right;
0708 int sbButtonsW = leftButtonW + rightButtonW;
0709 float trackW = sbSize.width - (sbInsetsW + sbButtonsW);
0710
0711 /* Compute the width and origin of the thumb. Enforce
0712 * the thumbs min/max dimensions. The case where the thumb
0713 * is at the right edge is handled specially to avoid numerical
0714 * problems in computing thumbX. If the thumb doesn't
0715 * fit in the track (trackH) we'll hide it later.
0716 */
0717 float min = sb.getMinimum();
0718 float max = sb.getMaximum();
0719 float extent = sb.getVisibleAmount();
0720 float range = max - min;
0721 float value = getValue(sb);
0722
0723 int thumbW = (range <= 0) ? getMaximumThumbSize().width
0724 : (int) (trackW * (extent / range));
0725 thumbW = Math.max(thumbW, getMinimumThumbSize().width);
0726 thumbW = Math.min(thumbW, getMaximumThumbSize().width);
0727
0728 int thumbX = ltr ? rightButtonX - thumbW : leftButtonX
0729 + leftButtonW;
0730 if (value < (max - sb.getVisibleAmount())) {
0731 float thumbRange = trackW - thumbW;
0732 if (ltr) {
0733 thumbX = (int) (0.5f + (thumbRange * ((value - min) / (range - extent))));
0734 } else {
0735 thumbX = (int) (0.5f + (thumbRange * ((max - extent - value) / (range - extent))));
0736 }
0737 thumbX += leftButtonX + leftButtonW;
0738 }
0739
0740 /* If the buttons don't fit, allocate half of the available
0741 * space to each and move the right one over.
0742 */
0743 int sbAvailButtonW = (sbSize.width - sbInsetsW);
0744 if (sbAvailButtonW < sbButtonsW) {
0745 rightButtonW = leftButtonW = sbAvailButtonW / 2;
0746 rightButtonX = sbSize.width
0747 - (sbInsets.right + rightButtonW);
0748 }
0749
0750 (ltr ? decrButton : incrButton).setBounds(leftButtonX, itemY,
0751 leftButtonW, itemH);
0752 (ltr ? incrButton : decrButton).setBounds(rightButtonX, itemY,
0753 rightButtonW, itemH);
0754
0755 /* Update the trackRect field.
0756 */
0757 int itrackX = leftButtonX + leftButtonW;
0758 int itrackW = rightButtonX - itrackX;
0759 trackRect.setBounds(itrackX, itemY, itrackW, itemH);
0760
0761 /* Make sure the thumb fits between the buttons. Note
0762 * that setting the thumbs bounds causes a repaint.
0763 */
0764 if (thumbW >= (int) trackW) {
0765 if (UIManager.getBoolean("ScrollBar.alwaysShowThumb")) {
0766 // This is used primarily for GTK L&F, which expands the
0767 // thumb to fit the track when it would otherwise be hidden.
0768 setThumbBounds(itrackX, itemY, itrackW, itemH);
0769 } else {
0770 // Other L&F's simply hide the thumb in this case.
0771 setThumbBounds(0, 0, 0, 0);
0772 }
0773 } else {
0774 if (thumbX + thumbW > rightButtonX) {
0775 thumbX = rightButtonX - thumbW;
0776 }
0777 if (thumbX < leftButtonX + leftButtonW) {
0778 thumbX = leftButtonX + leftButtonW + 1;
0779 }
0780 setThumbBounds(thumbX, itemY, thumbW, itemH);
0781 }
0782 }
0783
0784 public void layoutContainer(Container scrollbarContainer) {
0785 /* If the user is dragging the value, we'll assume that the
0786 * scrollbars layout is OK modulo the thumb which is being
0787 * handled by the dragging code.
0788 */
0789 if (isDragging) {
0790 return;
0791 }
0792
0793 JScrollBar scrollbar = (JScrollBar) scrollbarContainer;
0794 switch (scrollbar.getOrientation()) {
0795 case JScrollBar.VERTICAL:
0796 layoutVScrollbar(scrollbar);
0797 break;
0798
0799 case JScrollBar.HORIZONTAL:
0800 layoutHScrollbar(scrollbar);
0801 break;
0802 }
0803 }
0804
0805 /**
0806 * Set the bounds of the thumb and force a repaint that includes
0807 * the old thumbBounds and the new one.
0808 *
0809 * @see #getThumbBounds
0810 */
0811 protected void setThumbBounds(int x, int y, int width, int height) {
0812 /* If the thumbs bounds haven't changed, we're done.
0813 */
0814 if ((thumbRect.x == x) && (thumbRect.y == y)
0815 && (thumbRect.width == width)
0816 && (thumbRect.height == height)) {
0817 return;
0818 }
0819
0820 /* Update thumbRect, and repaint the union of x,y,w,h and
0821 * the old thumbRect.
0822 */
0823 int minX = Math.min(x, thumbRect.x);
0824 int minY = Math.min(y, thumbRect.y);
0825 int maxX = Math.max(x + width, thumbRect.x + thumbRect.width);
0826 int maxY = Math.max(y + height, thumbRect.y + thumbRect.height);
0827
0828 thumbRect.setBounds(x, y, width, height);
0829 scrollbar.repaint(minX, minY, maxX - minX, maxY - minY);
0830
0831 // Once there is API to determine the mouse location this will need
0832 // to be changed.
0833 setThumbRollover(false);
0834 }
0835
0836 /**
0837 * Return the current size/location of the thumb.
0838 * <p>
0839 * <b>Warning </b>: the value returned by this method should not be
0840 * be modified, it's a reference to the actual rectangle, not a copy.
0841 *
0842 * @return The current size/location of the thumb.
0843 * @see #setThumbBounds
0844 */
0845 protected Rectangle getThumbBounds() {
0846 return thumbRect;
0847 }
0848
0849 /**
0850 * Returns the current bounds of the track, i.e. the space in between
0851 * the increment and decrement buttons, less the insets. The value
0852 * returned by this method is updated each time the scrollbar is
0853 * laid out (validated).
0854 * <p>
0855 * <b>Warning </b>: the value returned by this method should not be
0856 * be modified, it's a reference to the actual rectangle, not a copy.
0857 *
0858 * @return the current bounds of the scrollbar track
0859 * @see #layoutContainer
0860 */
0861 protected Rectangle getTrackBounds() {
0862 return trackRect;
0863 }
0864
0865 /*
0866 * Method for scrolling by a block increment.
0867 * Added for mouse wheel scrolling support, RFE 4202656.
0868 */
0869 static void scrollByBlock(JScrollBar scrollbar, int direction) {
0870 // This method is called from BasicScrollPaneUI to implement wheel
0871 // scrolling, and also from scrollByBlock().
0872 int oldValue = scrollbar.getValue();
0873 int blockIncrement = scrollbar.getBlockIncrement(direction);
0874 int delta = blockIncrement * ((direction > 0) ? +1 : -1);
0875 int newValue = oldValue + delta;
0876
0877 // Check for overflow.
0878 if (delta > 0 && newValue < oldValue) {
0879 newValue = scrollbar.getMaximum();
0880 } else if (delta < 0 && newValue > oldValue) {
0881 newValue = scrollbar.getMinimum();
0882 }
0883
0884 scrollbar.setValue(newValue);
0885 }
0886
0887 protected void scrollByBlock(int direction) {
0888 scrollByBlock(scrollbar, direction);
0889 trackHighlight = direction > 0 ? INCREASE_HIGHLIGHT
0890 : DECREASE_HIGHLIGHT;
0891 Rectangle dirtyRect = getTrackBounds();
0892 scrollbar.repaint(dirtyRect.x, dirtyRect.y, dirtyRect.width,
0893 dirtyRect.height);
0894 }
0895
0896 /*
0897 * Method for scrolling by a unit increment.
0898 * Added for mouse wheel scrolling support, RFE 4202656.
0899 *
0900 * If limitByBlock is set to true, the scrollbar will scroll at least 1
0901 * unit increment, but will not scroll farther than the block increment.
0902 * See BasicScrollPaneUI.Handler.mouseWheelMoved().
0903 */
0904 static void scrollByUnits(JScrollBar scrollbar, int direction,
0905 int units, boolean limitToBlock) {
0906 // This method is called from BasicScrollPaneUI to implement wheel
0907 // scrolling, as well as from scrollByUnit().
0908 int delta;
0909 int limit = -1;
0910
0911 if (limitToBlock) {
0912 if (direction < 0) {
0913 limit = scrollbar.getValue()
0914 - scrollbar.getBlockIncrement(direction);
0915 } else {
0916 limit = scrollbar.getValue()
0917 + scrollbar.getBlockIncrement(direction);
0918 }
0919 }
0920
0921 for (int i = 0; i < units; i++) {
0922 if (direction > 0) {
0923 delta = scrollbar.getUnitIncrement(direction);
0924 } else {
0925 delta = -scrollbar.getUnitIncrement(direction);
0926 }
0927
0928 int oldValue = scrollbar.getValue();
0929 int newValue = oldValue + delta;
0930
0931 // Check for overflow.
0932 if (delta > 0 && newValue < oldValue) {
0933 newValue = scrollbar.getMaximum();
0934 } else if (delta < 0 && newValue > oldValue) {
0935 newValue = scrollbar.getMinimum();
0936 }
0937 if (oldValue == newValue) {
0938 break;
0939 }
0940
0941 if (limitToBlock && i > 0) {
0942 assert limit != -1;
0943 if ((direction < 0 && newValue < limit)
0944 || (direction > 0 && newValue > limit)) {
0945 break;
0946 }
0947 }
0948 scrollbar.setValue(newValue);
0949 }
0950 }
0951
0952 protected void scrollByUnit(int direction) {
0953 scrollByUnits(scrollbar, direction, 1, false);
0954 }
0955
0956 /**
0957 * Indicates whether the user can absolutely position the thumb with
0958 * a mouse gesture (usually the middle mouse button).
0959 *
0960 * @return true if a mouse gesture can absolutely position the thumb
0961 * @since 1.5
0962 */
0963 public boolean getSupportsAbsolutePositioning() {
0964 return supportsAbsolutePositioning;
0965 }
0966
0967 /**
0968 * A listener to listen for model changes.
0969 *
0970 */
0971 protected class ModelListener implements ChangeListener {
0972 public void stateChanged(ChangeEvent e) {
0973 if (!useCachedValue) {
0974 scrollBarValue = scrollbar.getValue();
0975 }
0976 layoutContainer(scrollbar);
0977 useCachedValue = false;
0978 }
0979 }
0980
0981 /**
0982 * Track mouse drags.
0983 */
0984 protected class TrackListener extends MouseAdapter implements
0985 MouseMotionListener {
0986 protected transient int offset;
0987 protected transient int currentMouseX, currentMouseY;
0988 private transient int direction = +1;
0989
0990 public void mouseReleased(MouseEvent e) {
0991 if (isDragging) {
0992 updateThumbState(e.getX(), e.getY());
0993 }
0994 if (SwingUtilities.isRightMouseButton(e)
0995 || (!getSupportsAbsolutePositioning() && SwingUtilities
0996 .isMiddleMouseButton(e)))
0997 return;
0998 if (!scrollbar.isEnabled())
0999 return;
1000
1001 Rectangle r = getTrackBounds();
1002 scrollbar.repaint(r.x, r.y, r.width, r.height);
1003
1004 trackHighlight = NO_HIGHLIGHT;
1005 isDragging = false;
1006 offset = 0;
1007 scrollTimer.stop();
1008 useCachedValue = true;
1009 scrollbar.setValueIsAdjusting(false);
1010 }
1011
1012 /**
1013 * If the mouse is pressed above the "thumb" component
1014 * then reduce the scrollbars value by one page ("page up"),
1015 * otherwise increase it by one page. If there is no
1016 * thumb then page up if the mouse is in the upper half
1017 * of the track.
1018 */
1019 public void mousePressed(MouseEvent e) {
1020 if (SwingUtilities.isRightMouseButton(e)
1021 || (!getSupportsAbsolutePositioning() && SwingUtilities
1022 .isMiddleMouseButton(e)))
1023 return;
1024 if (!scrollbar.isEnabled())
1025 return;
1026
1027 if (!scrollbar.hasFocus()
1028 && scrollbar.isRequestFocusEnabled()) {
1029 scrollbar.requestFocus();
1030 }
1031
1032 useCachedValue = true;
1033 scrollbar.setValueIsAdjusting(true);
1034
1035 currentMouseX = e.getX();
1036 currentMouseY = e.getY();
1037
1038 // Clicked in the Thumb area?
1039 if (getThumbBounds().contains(currentMouseX, currentMouseY)) {
1040 switch (scrollbar.getOrientation()) {
1041 case JScrollBar.VERTICAL:
1042 offset = currentMouseY - getThumbBounds().y;
1043 break;
1044 case JScrollBar.HORIZONTAL:
1045 offset = currentMouseX - getThumbBounds().x;
1046 break;
1047 }
1048 isDragging = true;
1049 return;
1050 } else if (getSupportsAbsolutePositioning()
1051 && SwingUtilities.isMiddleMouseButton(e)) {
1052 switch (scrollbar.getOrientation()) {
1053 case JScrollBar.VERTICAL:
1054 offset = getThumbBounds().height / 2;
1055 break;
1056 case JScrollBar.HORIZONTAL:
1057 offset = getThumbBounds().width / 2;
1058 break;
1059 }
1060 isDragging = true;
1061 setValueFrom(e);
1062 return;
1063 }
1064 isDragging = false;
1065
1066 Dimension sbSize = scrollbar.getSize();
1067 direction = +1;
1068
1069 switch (scrollbar.getOrientation()) {
1070 case JScrollBar.VERTICAL:
1071 if (getThumbBounds().isEmpty()) {
1072 int scrollbarCenter = sbSize.height / 2;
1073 direction = (currentMouseY < scrollbarCenter) ? -1
1074 : +1;
1075 } else {
1076 int thumbY = getThumbBounds().y;
1077 direction = (currentMouseY < thumbY) ? -1 : +1;
1078 }
1079 break;
1080 case JScrollBar.HORIZONTAL:
1081 if (getThumbBounds().isEmpty()) {
1082 int scrollbarCenter = sbSize.width / 2;
1083 direction = (currentMouseX < scrollbarCenter) ? -1
1084 : +1;
1085 } else {
1086 int thumbX = getThumbBounds().x;
1087 direction = (currentMouseX < thumbX) ? -1 : +1;
1088 }
1089 if (!scrollbar.getComponentOrientation()
1090 .isLeftToRight()) {
1091 direction = -direction;
1092 }
1093 break;
1094 }
1095 scrollByBlock(direction);
1096
1097 scrollTimer.stop();
1098 scrollListener.setDirection(direction);
1099 scrollListener.setScrollByBlock(true);
1100 startScrollTimerIfNecessary();
1101 }
1102
1103 /**
1104 * Set the models value to the position of the thumb's top of Vertical
1105 * scrollbar, or the left/right of Horizontal scrollbar in
1106 * left-to-right/right-to-left scrollbar relative to the origin of the
1107 * track.
1108 */
1109 public void mouseDragged(MouseEvent e) {
1110 if (SwingUtilities.isRightMouseButton(e)
1111 || (!getSupportsAbsolutePositioning() && SwingUtilities
1112 .isMiddleMouseButton(e)))
1113 return;
1114 if (!scrollbar.isEnabled() || getThumbBounds().isEmpty()) {
1115 return;
1116 }
1117 if (isDragging) {
1118 setValueFrom(e);
1119 } else {
1120 currentMouseX = e.getX();
1121 currentMouseY = e.getY();
1122 updateThumbState(currentMouseX, currentMouseY);
1123 startScrollTimerIfNecessary();
1124 }
1125 }
1126
1127 private void setValueFrom(MouseEvent e) {
1128 boolean active = isThumbRollover();
1129 BoundedRangeModel model = scrollbar.getModel();
1130 Rectangle thumbR = getThumbBounds();
1131 float trackLength;
1132 int thumbMin, thumbMax, thumbPos;
1133
1134 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
1135 thumbMin = decrButton.getY() + decrButton.getHeight();
1136 thumbMax = incrButton.getY() - thumbR.height;
1137 thumbPos = Math.min(thumbMax, Math.max(thumbMin, (e
1138 .getY() - offset)));
1139 setThumbBounds(thumbR.x, thumbPos, thumbR.width,
1140 thumbR.height);
1141 trackLength = getTrackBounds().height;
1142 } else {
1143 if (scrollbar.getComponentOrientation().isLeftToRight()) {
1144 thumbMin = decrButton.getX()
1145 + decrButton.getWidth();
1146 thumbMax = incrButton.getX() - thumbR.width;
1147 } else {
1148 thumbMin = incrButton.getX()
1149 + incrButton.getWidth();
1150 thumbMax = decrButton.getX() - thumbR.width;
1151 }
1152 thumbPos = Math.min(thumbMax, Math.max(thumbMin, (e
1153 .getX() - offset)));
1154 setThumbBounds(thumbPos, thumbR.y, thumbR.width,
1155 thumbR.height);
1156 trackLength = getTrackBounds().width;
1157 }
1158
1159 /* Set the scrollbars value. If the thumb has reached the end of
1160 * the scrollbar, then just set the value to its maximum. Otherwise
1161 * compute the value as accurately as possible.
1162 */
1163 if (thumbPos == thumbMax) {
1164 if (scrollbar.getOrientation() == JScrollBar.VERTICAL
1165 || scrollbar.getComponentOrientation()
1166 .isLeftToRight()) {
1167 scrollbar.setValue(model.getMaximum()
1168 - model.getExtent());
1169 } else {
1170 scrollbar.setValue(model.getMinimum());
1171 }
1172 } else {
1173 float valueMax = model.getMaximum() - model.getExtent();
1174 float valueRange = valueMax - model.getMinimum();
1175 float thumbValue = thumbPos - thumbMin;
1176 float thumbRange = thumbMax - thumbMin;
1177 int value;
1178 if (scrollbar.getOrientation() == JScrollBar.VERTICAL
1179 || scrollbar.getComponentOrientation()
1180 .isLeftToRight()) {
1181 value = (int) (0.5 + ((thumbValue / thumbRange) * valueRange));
1182 } else {
1183 value = (int) (0.5 + (((thumbMax - thumbPos) / thumbRange) * valueRange));
1184 }
1185
1186 useCachedValue = true;
1187 scrollBarValue = value + model.getMinimum();
1188 scrollbar
1189 .setValue(adjustValueIfNecessary(scrollBarValue));
1190 }
1191 setThumbRollover(active);
1192 }
1193
1194 private int adjustValueIfNecessary(int value) {
1195 if (scrollbar.getParent() instanceof JScrollPane) {
1196 JScrollPane scrollpane = (JScrollPane) scrollbar
1197 .getParent();
1198 JViewport viewport = scrollpane.getViewport();
1199 Component view = viewport.getView();
1200 if (view instanceof JList) {
1201 JList list = (JList) view;
1202 if (DefaultLookup.getBoolean(list, list.getUI(),
1203 "List.lockToPositionOnScroll", false)) {
1204 int adjustedValue = value;
1205 int mode = list.getLayoutOrientation();
1206 int orientation = scrollbar.getOrientation();
1207 if (orientation == JScrollBar.VERTICAL
1208 && mode == JList.VERTICAL) {
1209 int index = list.locationToIndex(new Point(
1210 0, value));
1211 Rectangle rect = list.getCellBounds(index,
1212 index);
1213 if (rect != null) {
1214 adjustedValue = rect.y;
1215 }
1216 }
1217 if (orientation == JScrollBar.HORIZONTAL
1218 && (mode == JList.VERTICAL_WRAP || mode == JList.HORIZONTAL_WRAP)) {
1219 if (scrollpane.getComponentOrientation()
1220 .isLeftToRight()) {
1221 int index = list
1222 .locationToIndex(new Point(
1223 value, 0));
1224 Rectangle rect = list.getCellBounds(
1225 index, index);
1226 if (rect != null) {
1227 adjustedValue = rect.x;
1228 }
1229 } else {
1230 Point loc = new Point(value, 0);
1231 int extent = viewport.getExtentSize().width;
1232 loc.x += extent - 1;
1233 int index = list.locationToIndex(loc);
1234 Rectangle rect = list.getCellBounds(
1235 index, index);
1236 if (rect != null) {
1237 adjustedValue = rect.x + rect.width
1238 - extent;
1239 }
1240 }
1241 }
1242 value = adjustedValue;
1243
1244 }
1245 }
1246 }
1247 return value;
1248 }
1249
1250 private void startScrollTimerIfNecessary() {
1251 if (scrollTimer.isRunning()) {
1252 return;
1253 }
1254
1255 Rectangle tb = getThumbBounds();
1256
1257 switch (scrollbar.getOrientation()) {
1258 case JScrollBar.VERTICAL:
1259 if (direction > 0) {
1260 if (tb.y + tb.height < trackListener.currentMouseY) {
1261 scrollTimer.start();
1262 }
1263 } else if (tb.y > trackListener.currentMouseY) {
1264 scrollTimer.start();
1265 }
1266 break;
1267 case JScrollBar.HORIZONTAL:
1268 if ((direction > 0 && isMouseAfterThumb())
1269 || (direction < 0 && isMouseBeforeThumb())) {
1270
1271 scrollTimer.start();
1272 }
1273 break;
1274 }
1275 }
1276
1277 public void mouseMoved(MouseEvent e) {
1278 if (!isDragging) {
1279 updateThumbState(e.getX(), e.getY());
1280 }
1281 }
1282
1283 /**
1284 * Invoked when the mouse exits the scrollbar.
1285 *
1286 * @param e MouseEvent further describing the event
1287 * @since 1.5
1288 */
1289 public void mouseExited(MouseEvent e) {
1290 if (!isDragging) {
1291 setThumbRollover(false);
1292 }
1293 }
1294 }
1295
1296 /**
1297 * Listener for cursor keys.
1298 */
1299 protected class ArrowButtonListener extends MouseAdapter {
1300 // Because we are handling both mousePressed and Actions
1301 // we need to make sure we don't fire under both conditions.
1302 // (keyfocus on scrollbars causes action without mousePress
1303 boolean handledEvent;
1304
1305 public void mousePressed(MouseEvent e) {
1306 if (!scrollbar.isEnabled()) {
1307 return;
1308 }
1309 // not an unmodified left mouse button
1310 //if(e.getModifiers() != InputEvent.BUTTON1_MASK) {return; }
1311 if (!SwingUtilities.isLeftMouseButton(e)) {
1312 return;
1313 }
1314
1315 int direction = (e.getSource() == incrButton) ? 1 : -1;
1316
1317 scrollByUnit(direction);
1318 scrollTimer.stop();
1319 scrollListener.setDirection(direction);
1320 scrollListener.setScrollByBlock(false);
1321 scrollTimer.start();
1322
1323 handledEvent = true;
1324 if (!scrollbar.hasFocus()
1325 && scrollbar.isRequestFocusEnabled()) {
1326 scrollbar.requestFocus();
1327 }
1328 }
1329
1330 public void mouseReleased(MouseEvent e) {
1331 scrollTimer.stop();
1332 handledEvent = false;
1333 scrollbar.setValueIsAdjusting(false);
1334 }
1335 }
1336
1337 /**
1338 * Listener for scrolling events initiated in the
1339 * <code>ScrollPane</code>.
1340 */
1341 protected class ScrollListener implements ActionListener {
1342 int direction = +1;
1343 boolean useBlockIncrement;
1344
1345 public ScrollListener() {
1346 direction = +1;
1347 useBlockIncrement = false;
1348 }
1349
1350 public ScrollListener(int dir, boolean block) {
1351 direction = dir;
1352 useBlockIncrement = block;
1353 }
1354
1355 public void setDirection(int direction) {
1356 this .direction = direction;
1357 }
1358
1359 public void setScrollByBlock(boolean block) {
1360 this .useBlockIncrement = block;
1361 }
1362
1363 public void actionPerformed(ActionEvent e) {
1364 if (useBlockIncrement) {
1365 scrollByBlock(direction);
1366 // Stop scrolling if the thumb catches up with the mouse
1367 if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
1368 if (direction > 0) {
1369 if (getThumbBounds().y
1370 + getThumbBounds().height >= trackListener.currentMouseY)
1371 ((Timer) e.getSource()).stop();
1372 } else if (getThumbBounds().y <= trackListener.currentMouseY) {
1373 ((Timer) e.getSource()).stop();
1374 }
1375 } else {
1376 if ((direction > 0 && !isMouseAfterThumb())
1377 || (direction < 0 && !isMouseBeforeThumb())) {
1378
1379 ((Timer) e.getSource()).stop();
1380 }
1381 }
1382 } else {
1383 scrollByUnit(direction);
1384 }
1385
1386 if (direction > 0
1387 && scrollbar.getValue()
1388 + scrollbar.getVisibleAmount() >= scrollbar
1389 .getMaximum())
1390 ((Timer) e.getSource()).stop();
1391 else if (direction < 0
1392 && scrollbar.getValue() <= scrollbar.getMinimum())
1393 ((Timer) e.getSource()).stop();
1394 }
1395 }
1396
1397 private boolean isMouseLeftOfThumb() {
1398 return trackListener.currentMouseX < getThumbBounds().x;
1399 }
1400
1401 private boolean isMouseRightOfThumb() {
1402 Rectangle tb = getThumbBounds();
1403 return trackListener.currentMouseX > tb.x + tb.width;
1404 }
1405
1406 private boolean isMouseBeforeThumb() {
1407 return scrollbar.getComponentOrientation().isLeftToRight() ? isMouseLeftOfThumb()
1408 : isMouseRightOfThumb();
1409 }
1410
1411 private boolean isMouseAfterThumb() {
1412 return scrollbar.getComponentOrientation().isLeftToRight() ? isMouseRightOfThumb()
1413 : isMouseLeftOfThumb();
1414 }
1415
1416 private void updateButtonDirections() {
1417 int orient = scrollbar.getOrientation();
1418 if (scrollbar.getComponentOrientation().isLeftToRight()) {
1419 if (incrButton instanceof BasicArrowButton) {
1420 ((BasicArrowButton) incrButton)
1421 .setDirection(orient == HORIZONTAL ? EAST
1422 : SOUTH);
1423 }
1424 if (decrButton instanceof BasicArrowButton) {
1425 ((BasicArrowButton) decrButton)
1426 .setDirection(orient == HORIZONTAL ? WEST
1427 : NORTH);
1428 }
1429 } else {
1430 if (incrButton instanceof BasicArrowButton) {
1431 ((BasicArrowButton) incrButton)
1432 .setDirection(orient == HORIZONTAL ? WEST
1433 : SOUTH);
1434 }
1435 if (decrButton instanceof BasicArrowButton) {
1436 ((BasicArrowButton) decrButton)
1437 .setDirection(orient == HORIZONTAL ? EAST
1438 : NORTH);
1439 }
1440 }
1441 }
1442
1443 public class PropertyChangeHandler implements
1444 PropertyChangeListener {
1445 // NOTE: This class exists only for backward compatability. All
1446 // its functionality has been moved into Handler. If you need to add
1447 // new functionality add it to the Handler, but make sure this
1448 // class calls into the Handler.
1449
1450 public void propertyChange(PropertyChangeEvent e) {
1451 getHandler().propertyChange(e);
1452 }
1453 }
1454
1455 /**
1456 * Used for scrolling the scrollbar.
1457 */
1458 private static class Actions extends UIAction {
1459 private static final String POSITIVE_UNIT_INCREMENT = "positiveUnitIncrement";
1460 private static final String POSITIVE_BLOCK_INCREMENT = "positiveBlockIncrement";
1461 private static final String NEGATIVE_UNIT_INCREMENT = "negativeUnitIncrement";
1462 private static final String NEGATIVE_BLOCK_INCREMENT = "negativeBlockIncrement";
1463 private static final String MIN_SCROLL = "minScroll";
1464 private static final String MAX_SCROLL = "maxScroll";
1465
1466 Actions(String name) {
1467 super (name);
1468 }
1469
1470 public void actionPerformed(ActionEvent e) {
1471 JScrollBar scrollBar = (JScrollBar) e.getSource();
1472 String key = getName();
1473 if (key == POSITIVE_UNIT_INCREMENT) {
1474 scroll(scrollBar, POSITIVE_SCROLL, false);
1475 } else if (key == POSITIVE_BLOCK_INCREMENT) {
1476 scroll(scrollBar, POSITIVE_SCROLL, true);
1477 } else if (key == NEGATIVE_UNIT_INCREMENT) {
1478 scroll(scrollBar, NEGATIVE_SCROLL, false);
1479 } else if (key == NEGATIVE_BLOCK_INCREMENT) {
1480 scroll(scrollBar, NEGATIVE_SCROLL, true);
1481 } else if (key == MIN_SCROLL) {
1482 scroll(scrollBar, BasicScrollBarUI.MIN_SCROLL, true);
1483 } else if (key == MAX_SCROLL) {
1484 scroll(scrollBar, BasicScrollBarUI.MAX_SCROLL, true);
1485 }
1486 }
1487
1488 private void scroll(JScrollBar scrollBar, int dir, boolean block) {
1489
1490 if (dir == NEGATIVE_SCROLL || dir == POSITIVE_SCROLL) {
1491 int amount;
1492 // Don't use the BasicScrollBarUI.scrollByXXX methods as we
1493 // don't want to use an invokeLater to reset the trackHighlight
1494 // via an invokeLater
1495 if (block) {
1496 if (dir == NEGATIVE_SCROLL) {
1497 amount = -1 * scrollBar.getBlockIncrement(-1);
1498 } else {
1499 amount = scrollBar.getBlockIncrement(1);
1500 }
1501 } else {
1502 if (dir == NEGATIVE_SCROLL) {
1503 amount = -1 * scrollBar.getUnitIncrement(-1);
1504 } else {
1505 amount = scrollBar.getUnitIncrement(1);
1506 }
1507 }
1508 scrollBar.setValue(scrollBar.getValue() + amount);
1509 } else if (dir == BasicScrollBarUI.MIN_SCROLL) {
1510 scrollBar.setValue(scrollBar.getMinimum());
1511 } else if (dir == BasicScrollBarUI.MAX_SCROLL) {
1512 scrollBar.setValue(scrollBar.getMaximum());
1513 }
1514 }
1515 }
1516
1517 //
1518 // EventHandler
1519 //
1520 private class Handler implements FocusListener,
1521 PropertyChangeListener {
1522 //
1523 // FocusListener
1524 //
1525 public void focusGained(FocusEvent e) {
1526 scrollbar.repaint();
1527 }
1528
1529 public void focusLost(FocusEvent e) {
1530 scrollbar.repaint();
1531 }
1532
1533 //
1534 // PropertyChangeListener
1535 //
1536 public void propertyChange(PropertyChangeEvent e) {
1537 String propertyName = e.getPropertyName();
1538
1539 if ("model" == propertyName) {
1540 BoundedRangeModel oldModel = (BoundedRangeModel) e
1541 .getOldValue();
1542 BoundedRangeModel newModel = (BoundedRangeModel) e
1543 .getNewValue();
1544 oldModel.removeChangeListener(modelListener);
1545 newModel.addChangeListener(modelListener);
1546 scrollbar.repaint();
1547 scrollbar.revalidate();
1548 } else if ("orientation" == propertyName) {
1549 updateButtonDirections();
1550 } else if ("componentOrientation" == propertyName) {
1551 updateButtonDirections();
1552 InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
1553 SwingUtilities.replaceUIInputMap(scrollbar,
1554 JComponent.WHEN_FOCUSED, inputMap);
1555 }
1556 }
1557 }
1558 }
|