1. /*
  2. * @(#)BasicScrollBarUI.java 1.53 01/11/29
  3. *
  4. * Copyright 2002 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 java.awt.*;
  9. import java.awt.event.*;
  10. import java.beans.*;
  11. import javax.swing.*;
  12. import javax.swing.event.*;
  13. import javax.swing.plaf.*;
  14. /**
  15. * Implementation of ScrollBarUI for the Basic Look and Feel
  16. *
  17. * @version 1.53 11/29/01
  18. * @author Rich Schiavi
  19. * @author David Kloba
  20. * @author Hans Muller
  21. */
  22. public class BasicScrollBarUI
  23. extends ScrollBarUI implements LayoutManager, SwingConstants
  24. {
  25. protected Dimension minimumThumbSize;
  26. protected Dimension maximumThumbSize;
  27. protected Color thumbHighlightColor;
  28. protected Color thumbLightShadowColor;
  29. protected Color thumbDarkShadowColor;
  30. protected Color thumbColor;
  31. protected Color trackColor;
  32. protected Color trackHighlightColor;
  33. protected JScrollBar scrollbar;
  34. protected JButton incrButton;
  35. protected JButton decrButton;
  36. protected boolean isDragging;
  37. protected TrackListener trackListener;
  38. protected ArrowButtonListener buttonListener;
  39. protected ModelListener modelListener;
  40. protected Rectangle thumbRect;
  41. protected Rectangle trackRect;
  42. protected int trackHighlight;
  43. protected static final int NO_HIGHLIGHT = 0;
  44. protected static final int DECREASE_HIGHLIGHT = 1;
  45. protected static final int INCREASE_HIGHLIGHT = 2;
  46. protected ScrollListener scrollListener;
  47. protected PropertyChangeListener propertyChangeListener;
  48. protected Timer scrollTimer;
  49. private final static int scrollSpeedThrottle = 60; // delay in milli seconds
  50. public static ComponentUI createUI(JComponent c) {
  51. return new BasicScrollBarUI();
  52. }
  53. protected void configureScrollBarColors()
  54. {
  55. thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
  56. thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbLightShadow");
  57. thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow");
  58. thumbColor = UIManager.getColor("ScrollBar.thumb");
  59. trackColor = UIManager.getColor("ScrollBar.track");
  60. trackHighlightColor = UIManager.getColor("ScrollBar.trackHighlight");
  61. }
  62. public void installUI(JComponent c) {
  63. scrollbar = (JScrollBar)c;
  64. thumbRect = new Rectangle(0, 0, 0, 0);
  65. trackRect = new Rectangle(0, 0, 0, 0);
  66. installDefaults();
  67. installComponents();
  68. installListeners();
  69. installKeyboardActions();
  70. }
  71. public void uninstallUI(JComponent c) {
  72. scrollbar = (JScrollBar)c;
  73. uninstallDefaults();
  74. uninstallComponents();
  75. uninstallListeners();
  76. uninstallKeyboardActions();
  77. c.remove(incrButton);
  78. c.remove(decrButton);
  79. c.setLayout(null);
  80. thumbRect = null;
  81. scrollbar = null;
  82. incrButton = null;
  83. decrButton = null;
  84. }
  85. protected void installDefaults()
  86. {
  87. minimumThumbSize = (Dimension)UIManager.get("ScrollBar.minimumThumbSize");
  88. maximumThumbSize = (Dimension)UIManager.get("ScrollBar.maximumThumbSize");
  89. trackHighlight = NO_HIGHLIGHT;
  90. switch (scrollbar.getOrientation()) {
  91. case JScrollBar.VERTICAL:
  92. incrButton = createIncreaseButton(SOUTH);
  93. decrButton = createDecreaseButton(NORTH);
  94. break;
  95. case JScrollBar.HORIZONTAL:
  96. incrButton = createIncreaseButton(EAST);
  97. decrButton = createDecreaseButton(WEST);
  98. break;
  99. }
  100. scrollbar.setLayout(this);
  101. scrollbar.add(incrButton);
  102. scrollbar.add(decrButton);
  103. scrollbar.setEnabled(scrollbar.isEnabled());
  104. scrollbar.setOpaque(true);
  105. configureScrollBarColors();
  106. LookAndFeel.installBorder(scrollbar, "ScrollBar.border");
  107. }
  108. protected void installComponents(){
  109. }
  110. protected void uninstallComponents(){
  111. }
  112. protected void installListeners(){
  113. trackListener = createTrackListener();
  114. buttonListener = createArrowButtonListener();
  115. modelListener = createModelListener();
  116. propertyChangeListener = createPropertyChangeListener();
  117. scrollbar.addMouseListener(trackListener);
  118. scrollbar.addMouseMotionListener(trackListener);
  119. scrollbar.getModel().addChangeListener(modelListener);
  120. scrollbar.addPropertyChangeListener(propertyChangeListener);
  121. if (incrButton != null) {
  122. incrButton.addMouseListener(buttonListener);
  123. }
  124. if (decrButton != null) {
  125. decrButton.addMouseListener(buttonListener);
  126. }
  127. scrollListener = createScrollListener();
  128. scrollTimer = new Timer(scrollSpeedThrottle, scrollListener);
  129. scrollTimer.setInitialDelay(300); // default InitialDelay?
  130. }
  131. protected void installKeyboardActions(){
  132. }
  133. protected void uninstallKeyboardActions(){
  134. }
  135. protected void uninstallListeners() {
  136. scrollTimer.stop();
  137. scrollTimer = null;
  138. if (decrButton != null){
  139. decrButton.removeMouseListener(buttonListener);
  140. }
  141. if (incrButton != null){
  142. incrButton.removeMouseListener(buttonListener);
  143. }
  144. scrollbar.getModel().removeChangeListener(modelListener);
  145. scrollbar.removeMouseListener(trackListener);
  146. scrollbar.removeMouseMotionListener(trackListener);
  147. scrollbar.removePropertyChangeListener(propertyChangeListener);
  148. }
  149. protected void uninstallDefaults(){
  150. LookAndFeel.uninstallBorder(scrollbar);
  151. }
  152. protected TrackListener createTrackListener(){
  153. return new TrackListener();
  154. }
  155. protected ArrowButtonListener createArrowButtonListener(){
  156. return new ArrowButtonListener();
  157. }
  158. protected ModelListener createModelListener(){
  159. return new ModelListener();
  160. }
  161. protected ScrollListener createScrollListener(){
  162. return new ScrollListener();
  163. }
  164. protected PropertyChangeListener createPropertyChangeListener() {
  165. return new PropertyChangeHandler();
  166. }
  167. public void paint(Graphics g, JComponent c) {
  168. paintTrack(g, c, getTrackBounds());
  169. paintThumb(g, c, getThumbBounds());
  170. }
  171. /**
  172. * A vertical scrollbars preferred width is the maximum of
  173. * preferred widths of the (non null) increment/decrement buttons,
  174. * and the minimum width of the thumb. The preferred height is the
  175. * sum of the preferred heights of the same parts. The basis for
  176. * the preferred size of a horizontal scrollbar is similar.
  177. * <p>
  178. * The preferredSize is only computed once, subequent
  179. * calls to this method just return a cached size. T
  180. *
  181. * @param c The JScrollBar that's delegating this method to us.
  182. * @return The preferred size of a Basic JScrollBar.
  183. * @see #getMaximumSize
  184. * @see #getMinimumSize
  185. */
  186. public Dimension getPreferredSize(JComponent c) {
  187. return (scrollbar.getOrientation() == JScrollBar.VERTICAL)
  188. ? new Dimension(16, 48)
  189. : new Dimension(48, 16);
  190. }
  191. /**
  192. * A vertical scrollbars minimum width is the largest
  193. * minimum width of the (non null) increment/decrement buttons,
  194. * and the minimum width of the thumb. The minimum height is the
  195. * sum of the minimum heights of the same parts. The basis for
  196. * the preferred size of a horizontal scrollbar is similar.
  197. * <p>
  198. * The minimumSize is only computed once, subequent
  199. * calls to this method just return a cached size. T
  200. *
  201. * @param c The JScrollBar that's delegating this method to us.
  202. * @return The minimum size of a Basic JScrollBar.
  203. * @see #getMaximumSize
  204. * @see #getPreferredSize
  205. */
  206. public Dimension getMinimumSize(JComponent c) {
  207. return getPreferredSize(c);
  208. }
  209. /**
  210. * @param c The JScrollBar that's delegating this method to us.
  211. * @return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  212. * @see #getMinimumSize
  213. * @see #getPreferredSize
  214. */
  215. public Dimension getMaximumSize(JComponent c) {
  216. return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
  217. }
  218. protected JButton createDecreaseButton(int orientation) {
  219. return new BasicArrowButton(orientation);
  220. }
  221. protected JButton createIncreaseButton(int orientation) {
  222. return new BasicArrowButton(orientation);
  223. }
  224. protected void paintDecreaseHighlight(Graphics g)
  225. {
  226. Insets insets = scrollbar.getInsets();
  227. Rectangle thumbR = getThumbBounds();
  228. g.setColor(trackHighlightColor);
  229. if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
  230. int x = insets.left;
  231. int y = decrButton.getY() + decrButton.getHeight();
  232. int w = scrollbar.getWidth() - (insets.left + insets.right);
  233. int h = thumbR.y - y;
  234. g.fillRect(x, y, w, h);
  235. }
  236. else {
  237. int x = decrButton.getX() + decrButton.getHeight();
  238. int y = insets.top;
  239. int w = thumbR.x - x;
  240. int h = scrollbar.getHeight() - (insets.top + insets.bottom);
  241. g.fillRect(x, y, w, h);
  242. }
  243. }
  244. protected void paintIncreaseHighlight(Graphics g)
  245. {
  246. Insets insets = scrollbar.getInsets();
  247. Rectangle thumbR = getThumbBounds();
  248. g.setColor(trackHighlightColor);
  249. if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
  250. int x = insets.left;
  251. int y = thumbR.y + thumbR.height;
  252. int w = scrollbar.getWidth() - (insets.left + insets.right);
  253. int h = incrButton.getY() - y;
  254. g.fillRect(x, y, w, h);
  255. }
  256. else {
  257. int x = thumbR.x + thumbR.width;
  258. int y = insets.top;
  259. int w = incrButton.getX() - x;
  260. int h = scrollbar.getHeight() - (insets.top + insets.bottom);
  261. g.fillRect(x, y, w, h);
  262. }
  263. }
  264. protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
  265. {
  266. g.setColor(trackColor);
  267. g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height);
  268. if(trackHighlight == DECREASE_HIGHLIGHT) {
  269. paintDecreaseHighlight(g);
  270. }
  271. else if(trackHighlight == INCREASE_HIGHLIGHT) {
  272. paintIncreaseHighlight(g);
  273. }
  274. }
  275. protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
  276. {
  277. if(thumbBounds.isEmpty() || !scrollbar.isEnabled()) {
  278. return;
  279. }
  280. int w = thumbBounds.width;
  281. int h = thumbBounds.height;
  282. g.translate(thumbBounds.x, thumbBounds.y);
  283. g.setColor(thumbDarkShadowColor);
  284. g.drawRect(0, 0, w-1, h-1);
  285. g.setColor(thumbColor);
  286. g.fillRect(0, 0, w-1, h-1);
  287. g.setColor(thumbHighlightColor);
  288. g.drawLine(1, 1, 1, h-2);
  289. g.drawLine(2, 1, w-3, 1);
  290. g.setColor(thumbLightShadowColor);
  291. g.drawLine(2, h-2, w-2, h-2);
  292. g.drawLine(w-2, 1, w-2, h-3);
  293. g.translate(-thumbBounds.x, -thumbBounds.y);
  294. }
  295. /**
  296. * Return the smallest acceptable size for the thumb. If the scrollbar
  297. * becomes so small that this size isn't available, the thumb will be
  298. * hidden.
  299. * <p>
  300. * <b>Warning </b>: the value returned by this method should not be
  301. * be modified, it's a shared static constant.
  302. *
  303. * @return The smallest acceptable size for the thumb.
  304. * @see #getMaximumThumbSize
  305. */
  306. protected Dimension getMinimumThumbSize() {
  307. return minimumThumbSize;
  308. }
  309. /**
  310. * Return the smallest acceptable size for the thumb. If the scrollbar
  311. * becomes so small that this size isn't available, the thumb will be
  312. * hidden. To create a fixed size thumb one make this
  313. * method and <code>getMinimumThumbSize</code> return the same value.
  314. * <p>
  315. * <b>Warning </b>: the value returned by this method should not be
  316. * be modified, it's a shared static constant.
  317. *
  318. * @return The smallest acceptable size for the thumb.
  319. * @see #getMinimumThumbSize
  320. */
  321. protected Dimension getMaximumThumbSize() {
  322. return maximumThumbSize;
  323. }
  324. /*
  325. * LayoutManager Implementation
  326. */
  327. public void addLayoutComponent(String name, Component child) {}
  328. public void removeLayoutComponent(Component child) {}
  329. public Dimension preferredLayoutSize(Container scrollbarContainer) {
  330. return getPreferredSize((JComponent)scrollbarContainer);
  331. }
  332. public Dimension minimumLayoutSize(Container scrollbarContainer) {
  333. return getMinimumSize((JComponent)scrollbarContainer);
  334. }
  335. protected void layoutVScrollbar(JScrollBar sb)
  336. {
  337. Dimension sbSize = sb.getSize();
  338. Insets sbInsets = sb.getInsets();
  339. /*
  340. * Width and left edge of the buttons and thumb.
  341. */
  342. int itemW = sbSize.width - (sbInsets.left + sbInsets.right);
  343. int itemX = sbInsets.left;
  344. /* Nominal locations of the buttons, assuming their preferred
  345. * size will fit.
  346. */
  347. int decrButtonH = decrButton.getPreferredSize().height;
  348. int decrButtonY = sbInsets.top;
  349. int incrButtonH = incrButton.getPreferredSize().height;
  350. int incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
  351. /* The thumb must fit within the height left over after we
  352. * subtract the preferredSize of the buttons and the insets.
  353. */
  354. int sbInsetsH = sbInsets.top + sbInsets.bottom;
  355. int sbButtonsH = decrButtonH + incrButtonH;
  356. float trackH = sbSize.height - (sbInsetsH + sbButtonsH);
  357. /* Compute the height and origin of the thumb. The case
  358. * where the thumb is at the bottom edge is handled specially
  359. * to avoid numerical problems in computing thumbY. Enforce
  360. * the thumbs min/max dimensions. If the thumb doesn't
  361. * fit in the track (trackH) we'll hide it later.
  362. */
  363. float min = sb.getMinimum();
  364. float extent = sb.getVisibleAmount();
  365. float range = sb.getMaximum() - min;
  366. float value = sb.getValue();
  367. int thumbH = (range <= 0)
  368. ? getMaximumThumbSize().height : (int)(trackH * (extent / range));
  369. thumbH = Math.max(thumbH, getMinimumThumbSize().height);
  370. thumbH = Math.min(thumbH, getMaximumThumbSize().height);
  371. int thumbY = incrButtonY - thumbH;
  372. if (sb.getValue() < (sb.getMaximum() - sb.getVisibleAmount())) {
  373. float thumbRange = trackH - thumbH;
  374. thumbY = (int)(0.5f + (thumbRange * ((value - min) / (range - extent))));
  375. thumbY += decrButtonY + decrButtonH;
  376. }
  377. /* If the buttons don't fit, allocate half of the available
  378. * space to each and move the lower one (incrButton) down.
  379. */
  380. int sbAvailButtonH = (sbSize.height - sbInsetsH);
  381. if (sbAvailButtonH < sbButtonsH) {
  382. incrButtonH = decrButtonH = sbAvailButtonH / 2;
  383. incrButtonY = sbSize.height - (sbInsets.bottom + incrButtonH);
  384. }
  385. decrButton.setBounds(itemX, decrButtonY, itemW, decrButtonH);
  386. incrButton.setBounds(itemX, incrButtonY, itemW, incrButtonH);
  387. /* Update the trackRect field.
  388. */
  389. int itrackY = decrButtonY + decrButtonH;
  390. int itrackH = incrButtonY - itrackY;
  391. trackRect.setBounds(itemX, itrackY, itemW, itrackH);
  392. /* If the thumb isn't going to fit, zero it's bounds. Otherwise
  393. * make sure it fits between the buttons. Note that setting the
  394. * thumbs bounds will cause a repaint.
  395. */
  396. if(thumbH >= (int)trackH) {
  397. setThumbBounds(0, 0, 0, 0);
  398. }
  399. else {
  400. if ((thumbY + thumbH) > incrButtonY) {
  401. thumbY = incrButtonY - thumbH;
  402. }
  403. if (thumbY < (decrButtonY + decrButtonH)) {
  404. thumbY = decrButtonY + decrButtonH + 1;
  405. }
  406. setThumbBounds(itemX, thumbY, itemW, thumbH);
  407. }
  408. }
  409. protected void layoutHScrollbar(JScrollBar sb)
  410. {
  411. Dimension sbSize = sb.getSize();
  412. Insets sbInsets = sb.getInsets();
  413. /* Height and top edge of the buttons and thumb.
  414. */
  415. int itemH = sbSize.height - (sbInsets.top + sbInsets.bottom);
  416. int itemY = sbInsets.top;
  417. /* Nominal locations of the buttons, assuming their preferred
  418. * size will fit.
  419. */
  420. int decrButtonW = decrButton.getPreferredSize().width;
  421. int decrButtonX = sbInsets.left;
  422. int incrButtonW = incrButton.getPreferredSize().width;
  423. int incrButtonX = sbSize.width - (sbInsets.right + incrButtonW);
  424. /* The thumb must fit within the width left over after we
  425. * subtract the preferredSize of the buttons and the insets.
  426. */
  427. int sbInsetsW = sbInsets.left + sbInsets.right;
  428. int sbButtonsW = decrButtonW + incrButtonW;
  429. float trackW = sbSize.width - (sbInsetsW + sbButtonsW);
  430. /* Compute the width and origin of the thumb. Enforce
  431. * the thumbs min/max dimensions. The case where the thumb
  432. * is at the right edge is handled specially to avoid numerical
  433. * problems in computing thumbX. If the thumb doesn't
  434. * fit in the track (trackH) we'll hide it later.
  435. */
  436. float min = sb.getMinimum();
  437. float extent = sb.getVisibleAmount();
  438. float range = sb.getMaximum() - min;
  439. float value = sb.getValue();
  440. int thumbW = (range <= 0)
  441. ? getMaximumThumbSize().width : (int)(trackW * (extent / range));
  442. thumbW = Math.max(thumbW, getMinimumThumbSize().width);
  443. thumbW = Math.min(thumbW, getMaximumThumbSize().width);
  444. int thumbX = incrButtonX - thumbW;
  445. if (sb.getValue() < (sb.getMaximum() - sb.getVisibleAmount())) {
  446. float thumbRange = trackW - thumbW;
  447. thumbX = (int)(0.5f + (thumbRange * ((value - min) / (range - extent))));
  448. thumbX += decrButtonX + decrButtonW;
  449. }
  450. /* If the buttons don't fit, allocate half of the available
  451. * space to each and move the right one (incrButton) over.
  452. */
  453. int sbAvailButtonW = (sbSize.width - sbInsetsW);
  454. if (sbAvailButtonW < sbButtonsW) {
  455. incrButtonW = decrButtonW = sbAvailButtonW / 2;
  456. incrButtonX = sbSize.width - (sbInsets.right + incrButtonW);
  457. }
  458. decrButton.setBounds(decrButtonX, itemY, decrButtonW, itemH);
  459. incrButton.setBounds(incrButtonX, itemY, incrButtonW, itemH);
  460. /* Update the trackRect field.
  461. */
  462. int itrackX = decrButtonX + decrButtonW;
  463. int itrackW = incrButtonX - itrackX;
  464. trackRect.setBounds(itrackX, itemY, itrackW, itemH);
  465. /* Make sure the thumb fits between the buttons. Note
  466. * that setting the thumbs bounds causes a repaint.
  467. */
  468. if (thumbW >= (int)trackW) {
  469. setThumbBounds(0, 0, 0, 0);
  470. }
  471. else {
  472. if (thumbX + thumbW > incrButtonX) {
  473. thumbX = incrButtonX - thumbW;
  474. }
  475. if (thumbX < decrButtonX + decrButtonW) {
  476. thumbX = decrButtonX + decrButtonW + 1;
  477. }
  478. setThumbBounds(thumbX, itemY, thumbW, itemH);
  479. }
  480. }
  481. public void layoutContainer(Container scrollbarContainer)
  482. {
  483. /* If the user is dragging the value, we'll assume that the
  484. * scrollbars layout is OK modulo the thumb which is being
  485. * handled by the dragging code.
  486. */
  487. if (isDragging) {
  488. return;
  489. }
  490. JScrollBar scrollbar = (JScrollBar)scrollbarContainer;
  491. switch (scrollbar.getOrientation()) {
  492. case JScrollBar.VERTICAL:
  493. layoutVScrollbar(scrollbar);
  494. break;
  495. case JScrollBar.HORIZONTAL:
  496. layoutHScrollbar(scrollbar);
  497. break;
  498. }
  499. }
  500. /**
  501. * Set the bounds of the thumb and force a repaint that includes
  502. * the old thumbBounds and the new one.
  503. *
  504. * @see #getThumbBounds
  505. */
  506. protected void setThumbBounds(int x, int y, int width, int height)
  507. {
  508. /* If the thumbs bounds haven't changed, we're done.
  509. */
  510. if ((thumbRect.x == x) &&
  511. (thumbRect.y == y) &&
  512. (thumbRect.width == width) &&
  513. (thumbRect.height == height)) {
  514. return;
  515. }
  516. /* Update thumbRect, and repaint the union of x,y,w,h and
  517. * the old thumbRect.
  518. */
  519. int minX = Math.min(x, thumbRect.x);
  520. int minY = Math.min(y, thumbRect.y);
  521. int maxX = Math.max(x + width, thumbRect.x + thumbRect.width);
  522. int maxY = Math.max(y + height, thumbRect.y + thumbRect.height);
  523. thumbRect.setBounds(x, y, width, height);
  524. scrollbar.repaint(minX, minY, maxX - minX, maxY - minY);
  525. }
  526. /**
  527. * Return the current size/location of the thumb.
  528. * <p>
  529. * <b>Warning </b>: the value returned by this method should not be
  530. * be modified, it's a reference to the actual rectangle, not a copy.
  531. *
  532. * @return The current size/location of the thumb.
  533. * @see #setThumbBounds
  534. */
  535. protected Rectangle getThumbBounds() {
  536. return thumbRect;
  537. }
  538. /**
  539. * Return the current bounds of the track, i.e. the space in between
  540. * the increment and decrement buttons, less the insets. The value
  541. * returned by this method is updated each time the scrollbar is
  542. * layed out (validated).
  543. * <p>
  544. * <b>Warning </b>: the value returned by this method should not be
  545. * be modified, it's a reference to the actual rectangle, not a copy.
  546. *
  547. * @return The current bounds of the scrollbar track.
  548. * @see #layoutContainer
  549. */
  550. protected Rectangle getTrackBounds() {
  551. return trackRect;
  552. }
  553. protected void scrollByBlock(int direction)
  554. {
  555. synchronized(scrollbar) {
  556. int oldValue = scrollbar.getValue();
  557. int blockIncrement = scrollbar.getBlockIncrement(direction);
  558. int delta = blockIncrement * ((direction > 0) ? +1 : -1);
  559. scrollbar.setValue(oldValue + delta);
  560. trackHighlight = direction > 0 ? INCREASE_HIGHLIGHT : DECREASE_HIGHLIGHT;
  561. Rectangle dirtyRect = getTrackBounds();
  562. scrollbar.repaint(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
  563. }
  564. }
  565. protected void scrollByUnit(int direction) {
  566. synchronized(scrollbar) {
  567. int delta;
  568. if(direction > 0)
  569. delta = scrollbar.getUnitIncrement(direction);
  570. else
  571. delta = -scrollbar.getUnitIncrement(direction);
  572. scrollbar.setValue(delta + scrollbar.getValue());
  573. }
  574. }
  575. /**
  576. * A listener to listen for model changes.
  577. *
  578. */
  579. protected class ModelListener implements ChangeListener {
  580. public void stateChanged(ChangeEvent e) {
  581. layoutContainer(scrollbar);
  582. }
  583. }
  584. /**
  585. * Track mouse drags.
  586. */
  587. protected class TrackListener
  588. extends MouseAdapter implements MouseMotionListener
  589. {
  590. protected transient int offset;
  591. protected transient int currentMouseX, currentMouseY;
  592. public void mouseReleased(MouseEvent e)
  593. {
  594. if(!scrollbar.isEnabled())
  595. return;
  596. if(trackHighlight != NO_HIGHLIGHT) {
  597. Rectangle r = getTrackBounds();
  598. scrollbar.repaint(r.x, r.y, r.width, r.height);
  599. }
  600. trackHighlight = NO_HIGHLIGHT;
  601. isDragging = false;
  602. offset = 0;
  603. scrollTimer.stop();
  604. scrollbar.setValueIsAdjusting(false);
  605. }
  606. /**
  607. * If the mouse is pressed above the "thumb" component
  608. * then reduce the scrollbars value by one page ("page up"),
  609. * otherwise increase it by one page. If there is no
  610. * thumb then page up if the mouse is in the upper half
  611. * of the track.
  612. */
  613. public void mousePressed(MouseEvent e)
  614. {
  615. if(!scrollbar.isEnabled())
  616. return;
  617. scrollbar.setValueIsAdjusting(true);
  618. currentMouseX = e.getX();
  619. currentMouseY = e.getY();
  620. // Clicked in the Thumb area?
  621. if(getThumbBounds().contains(currentMouseX, currentMouseY)) {
  622. switch (scrollbar.getOrientation()) {
  623. case JScrollBar.VERTICAL:
  624. offset = currentMouseY - getThumbBounds().y;
  625. break;
  626. case JScrollBar.HORIZONTAL:
  627. offset = currentMouseX - getThumbBounds().x;
  628. break;
  629. }
  630. isDragging = true;
  631. return;
  632. }
  633. isDragging = false;
  634. Dimension sbSize = scrollbar.getSize();
  635. int direction = +1;
  636. switch (scrollbar.getOrientation()) {
  637. case JScrollBar.VERTICAL:
  638. if (getThumbBounds().isEmpty()) {
  639. int scrollbarCenter = sbSize.height / 2;
  640. direction = (currentMouseY < scrollbarCenter) ? -1 : +1;
  641. } else {
  642. int thumbY = getThumbBounds().y;
  643. direction = (currentMouseY < thumbY) ? -1 : +1;
  644. }
  645. break;
  646. case JScrollBar.HORIZONTAL:
  647. if (getThumbBounds().isEmpty()) {
  648. int scrollbarCenter = sbSize.width / 2;
  649. direction = (currentMouseX < scrollbarCenter) ? -1 : +1;
  650. } else {
  651. int thumbX = getThumbBounds().x;
  652. direction = (currentMouseX < thumbX) ? -1 : +1;
  653. }
  654. break;
  655. }
  656. scrollByBlock(direction);
  657. if(!getThumbBounds().contains(currentMouseX, currentMouseY)) {
  658. scrollTimer.stop();
  659. scrollListener.setDirection(direction);
  660. scrollListener.setScrollByBlock(true);
  661. scrollTimer.start();
  662. }
  663. }
  664. /**
  665. * Set the models value to the position of the top/left
  666. * of the thumb relative to the origin of the track.
  667. */
  668. public void mouseDragged(MouseEvent e)
  669. {
  670. if(!scrollbar.isEnabled() || !isDragging) {
  671. return;
  672. }
  673. BoundedRangeModel model = scrollbar.getModel();
  674. Rectangle thumbR = getThumbBounds();
  675. float trackLength;
  676. int thumbMin, thumbMax, thumbPos;
  677. if (scrollbar.getOrientation() == JScrollBar.VERTICAL) {
  678. thumbMin = decrButton.getY() + decrButton.getHeight();
  679. thumbMax = incrButton.getY() - getThumbBounds().height;
  680. thumbPos = Math.min(thumbMax, Math.max(thumbMin, (e.getY() - offset)));
  681. setThumbBounds(thumbR.x, thumbPos, thumbR.width, thumbR.height);
  682. trackLength = getTrackBounds().height;
  683. }
  684. else {
  685. thumbMin = decrButton.getX() + decrButton.getWidth();
  686. thumbMax = incrButton.getX() - getThumbBounds().width;
  687. thumbPos = Math.min(thumbMax, Math.max(thumbMin, (e.getX() - offset)));
  688. setThumbBounds(thumbPos, thumbR.y, thumbR.width, thumbR.height);
  689. trackLength = getTrackBounds().width;
  690. }
  691. /* Set the scrollbars value. If the thumb has reached the end of
  692. * the scrollbar, then just set the value to its maximum. Otherwise
  693. * compute the value as accurately as possible.
  694. */
  695. if (thumbPos == thumbMax) {
  696. scrollbar.setValue(model.getMaximum() - model.getExtent());
  697. }
  698. else {
  699. float valueMax = model.getMaximum() - model.getExtent();
  700. float valueRange = valueMax - model.getMinimum();
  701. float thumbValue = thumbPos - thumbMin;
  702. float thumbRange = thumbMax - thumbMin;
  703. int value = (int)(0.5 + ((thumbValue / thumbRange) * valueRange));
  704. scrollbar.setValue(value + model.getMinimum());
  705. }
  706. }
  707. public void mouseMoved(MouseEvent e) {
  708. }
  709. }
  710. /**
  711. * Listener for cursor keys.
  712. */
  713. protected class ArrowButtonListener extends MouseAdapter
  714. {
  715. // Because we are handling both mousePressed and Actions
  716. // we need to make sure we don't fire under both conditions.
  717. // (keyfocus on scrollbars causes action without mousePress
  718. boolean handledEvent;
  719. public void mousePressed(MouseEvent e) {
  720. if(!scrollbar.isEnabled()) { return; }
  721. // not an unmodified left mouse button
  722. //if(e.getModifiers() != InputEvent.BUTTON1_MASK) {return; }
  723. if( ! SwingUtilities.isLeftMouseButton(e)) { return; }
  724. int direction = (e.getSource() == incrButton) ? 1 : -1;
  725. scrollByUnit(direction);
  726. scrollTimer.stop();
  727. scrollListener.setDirection(direction);
  728. scrollListener.setScrollByBlock(false);
  729. scrollTimer.start();
  730. handledEvent = true;
  731. }
  732. public void mouseReleased(MouseEvent e) {
  733. scrollTimer.stop();
  734. handledEvent = false;
  735. scrollbar.setValueIsAdjusting(false);
  736. }
  737. }
  738. /**
  739. * Listener for scrolling events intiated in the
  740. * ScrollPane.
  741. */
  742. protected class ScrollListener implements ActionListener
  743. {
  744. int direction = +1;
  745. boolean useBlockIncrement;
  746. public ScrollListener() {
  747. direction = +1;
  748. useBlockIncrement = false;
  749. }
  750. public ScrollListener(int dir, boolean block) {
  751. direction = dir;
  752. useBlockIncrement = block;
  753. }
  754. public void setDirection(int direction) { this.direction = direction; }
  755. public void setScrollByBlock(boolean block) { this.useBlockIncrement = block; }
  756. public void actionPerformed(ActionEvent e) {
  757. if(useBlockIncrement) {
  758. scrollByBlock(direction);
  759. // Stop scrolling if the thumb catches up with the mouse
  760. if(scrollbar.getOrientation() == JScrollBar.VERTICAL) {
  761. if(direction > 0) {
  762. if(getThumbBounds().y + getThumbBounds().height
  763. >= trackListener.currentMouseY)
  764. ((Timer)e.getSource()).stop();
  765. } else if(getThumbBounds().y <= trackListener.currentMouseY) {
  766. ((Timer)e.getSource()).stop();
  767. }
  768. } else {
  769. if(direction > 0) {
  770. if(getThumbBounds().x + getThumbBounds().width
  771. >= trackListener.currentMouseX)
  772. ((Timer)e.getSource()).stop();
  773. } else if(getThumbBounds().x <= trackListener.currentMouseX) {
  774. ((Timer)e.getSource()).stop();
  775. }
  776. }
  777. } else {
  778. scrollByUnit(direction);
  779. }
  780. if(direction > 0
  781. && scrollbar.getValue()+scrollbar.getVisibleAmount()
  782. >= scrollbar.getMaximum())
  783. ((Timer)e.getSource()).stop();
  784. else if(direction < 0
  785. && scrollbar.getValue() <= scrollbar.getMinimum())
  786. ((Timer)e.getSource()).stop();
  787. }
  788. }
  789. public class PropertyChangeHandler implements PropertyChangeListener
  790. {
  791. public void propertyChange(PropertyChangeEvent e) {
  792. String propertyName = e.getPropertyName();
  793. if ("model".equals(propertyName)) {
  794. BoundedRangeModel oldModel = (BoundedRangeModel)e.getOldValue();
  795. BoundedRangeModel newModel = (BoundedRangeModel)e.getNewValue();
  796. oldModel.removeChangeListener(modelListener);
  797. newModel.addChangeListener(modelListener);
  798. scrollbar.repaint();
  799. scrollbar.revalidate();
  800. } else if ("orientation".equals(propertyName)) {
  801. Integer orient = (Integer)e.getNewValue();
  802. if (incrButton instanceof BasicArrowButton) {
  803. ((BasicArrowButton)incrButton).setDirection(orient.intValue() == HORIZONTAL?
  804. EAST : SOUTH);
  805. }
  806. if (decrButton instanceof BasicArrowButton) {
  807. ((BasicArrowButton)decrButton).setDirection(orient.intValue() == HORIZONTAL?
  808. WEST : NORTH);
  809. }
  810. }
  811. }
  812. }
  813. }