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