Showing the changes to java/pkg/javax/swing/plaf/basic/BasicScrollBarUI.java (1 of 18352 files). Show all
Unified · Split
| @@ -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 | } | ||