1. /*
  2. * @(#)BasicScrollPaneUI.java 1.66 03/01/23
  3. *
  4. * Copyright 2003 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 javax.swing.*;
  9. import javax.swing.event.*;
  10. import javax.swing.border.*;
  11. import javax.swing.plaf.*;
  12. import java.beans.PropertyChangeListener;
  13. import java.beans.PropertyChangeEvent;
  14. import java.awt.Component;
  15. import java.awt.Container;
  16. import java.awt.LayoutManager;
  17. import java.awt.Rectangle;
  18. import java.awt.Dimension;
  19. import java.awt.Point;
  20. import java.awt.Insets;
  21. import java.awt.Graphics;
  22. import java.awt.event.*;
  23. import java.io.Serializable;
  24. import java.awt.Toolkit;
  25. /**
  26. * A default L&F implementation of ScrollPaneUI.
  27. *
  28. * @version 1.66 01/23/03
  29. * @author Hans Muller
  30. */
  31. public class BasicScrollPaneUI
  32. extends ScrollPaneUI implements ScrollPaneConstants
  33. {
  34. protected JScrollPane scrollpane;
  35. protected ChangeListener vsbChangeListener;
  36. protected ChangeListener hsbChangeListener;
  37. protected ChangeListener viewportChangeListener;
  38. protected PropertyChangeListener spPropertyChangeListener;
  39. private MouseWheelListener mouseScrollListener;
  40. /**
  41. * PropertyChangeListener installed on the vertical scrollbar.
  42. */
  43. private PropertyChangeListener vsbPropertyChangeListener;
  44. /**
  45. * PropertyChangeListener installed on the horizontal scrollbar.
  46. */
  47. private PropertyChangeListener hsbPropertyChangeListener;
  48. /**
  49. * The default implementation of createHSBPropertyChangeListener and
  50. * createVSBPropertyChangeListener share the PropertyChangeListener, which
  51. * is this ivar.
  52. */
  53. private PropertyChangeListener sbPropertyChangeListener;
  54. /**
  55. * State flag that shows whether setValue() was called from a user program
  56. * before the value of "extent" was set in right-to-left component
  57. * orientation.
  58. */
  59. private boolean setValueCalled = false;
  60. public static ComponentUI createUI(JComponent x) {
  61. return new BasicScrollPaneUI();
  62. }
  63. public void paint(Graphics g, JComponent c) {
  64. Border vpBorder = scrollpane.getViewportBorder();
  65. if (vpBorder != null) {
  66. Rectangle r = scrollpane.getViewportBorderBounds();
  67. vpBorder.paintBorder(scrollpane, g, r.x, r.y, r.width, r.height);
  68. }
  69. }
  70. /**
  71. * @return null which indicates that the LayoutManager will compute the value
  72. * @see JComponent#getPreferredSize
  73. */
  74. public Dimension getPreferredSize(JComponent c) {
  75. return null;
  76. }
  77. /**
  78. * @return the preferred size
  79. * @see #getPreferredSize
  80. */
  81. public Dimension getMinimumSize(JComponent c) {
  82. return getPreferredSize(c);
  83. }
  84. /**
  85. * @return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE)
  86. */
  87. public Dimension getMaximumSize(JComponent c) {
  88. return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
  89. }
  90. protected void installDefaults(JScrollPane scrollpane)
  91. {
  92. LookAndFeel.installBorder(scrollpane, "ScrollPane.border");
  93. LookAndFeel.installColorsAndFont(scrollpane,
  94. "ScrollPane.background",
  95. "ScrollPane.foreground",
  96. "ScrollPane.font");
  97. Border vpBorder = scrollpane.getViewportBorder();
  98. if ((vpBorder == null) ||( vpBorder instanceof UIResource)) {
  99. vpBorder = UIManager.getBorder("ScrollPane.viewportBorder");
  100. scrollpane.setViewportBorder(vpBorder);
  101. }
  102. }
  103. protected void installListeners(JScrollPane c)
  104. {
  105. vsbChangeListener = createVSBChangeListener();
  106. vsbPropertyChangeListener = createVSBPropertyChangeListener();
  107. hsbChangeListener = createHSBChangeListener();
  108. hsbPropertyChangeListener = createHSBPropertyChangeListener();
  109. viewportChangeListener = createViewportChangeListener();
  110. spPropertyChangeListener = createPropertyChangeListener();
  111. JViewport viewport = scrollpane.getViewport();
  112. JScrollBar vsb = scrollpane.getVerticalScrollBar();
  113. JScrollBar hsb = scrollpane.getHorizontalScrollBar();
  114. if (viewport != null) {
  115. viewport.addChangeListener(viewportChangeListener);
  116. }
  117. if (vsb != null) {
  118. vsb.getModel().addChangeListener(vsbChangeListener);
  119. vsb.addPropertyChangeListener(vsbPropertyChangeListener);
  120. }
  121. if (hsb != null) {
  122. hsb.getModel().addChangeListener(hsbChangeListener);
  123. hsb.addPropertyChangeListener(hsbPropertyChangeListener);
  124. }
  125. scrollpane.addPropertyChangeListener(spPropertyChangeListener);
  126. mouseScrollListener = createMouseWheelListener();
  127. scrollpane.addMouseWheelListener(mouseScrollListener);
  128. }
  129. protected void installKeyboardActions(JScrollPane c) {
  130. InputMap inputMap = getInputMap(JComponent.
  131. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  132. SwingUtilities.replaceUIInputMap(c, JComponent.
  133. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
  134. ActionMap actionMap = getActionMap();
  135. SwingUtilities.replaceUIActionMap(c, actionMap);
  136. }
  137. InputMap getInputMap(int condition) {
  138. if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
  139. InputMap keyMap = (InputMap)UIManager.get("ScrollPane.ancestorInputMap");
  140. InputMap rtlKeyMap;
  141. if (scrollpane.getComponentOrientation().isLeftToRight() ||
  142. ((rtlKeyMap = (InputMap)UIManager.get("ScrollPane.ancestorInputMap.RightToLeft")) == null)) {
  143. return keyMap;
  144. } else {
  145. rtlKeyMap.setParent(keyMap);
  146. return rtlKeyMap;
  147. }
  148. }
  149. return null;
  150. }
  151. ActionMap getActionMap() {
  152. ActionMap map = (ActionMap)UIManager.get("ScrollPane.actionMap");
  153. if (map == null) {
  154. map = createActionMap();
  155. if (map != null) {
  156. UIManager.getLookAndFeelDefaults().put("ScrollPane.actionMap",
  157. map);
  158. }
  159. }
  160. return map;
  161. }
  162. ActionMap createActionMap() {
  163. ActionMap map = new ActionMapUIResource();
  164. map.put("scrollUp", new ScrollAction("scrollUp", SwingConstants.
  165. VERTICAL, -1, true));
  166. map.put("scrollDown", new ScrollAction("scrollDown",
  167. SwingConstants.VERTICAL, 1, true));
  168. map.put("scrollHome", new ScrollHomeAction("ScrollHome"));
  169. map.put("scrollEnd", new ScrollEndAction("ScrollEnd"));
  170. map.put("unitScrollUp", new ScrollAction
  171. ("UnitScrollUp", SwingConstants.VERTICAL, -1,false));
  172. map.put("unitScrollDown", new ScrollAction
  173. ("UnitScrollDown", SwingConstants.VERTICAL, 1, false));
  174. if (scrollpane.getComponentOrientation().isLeftToRight()) {
  175. map.put("scrollLeft", new ScrollAction("scrollLeft",
  176. SwingConstants.HORIZONTAL, -1, true));
  177. map.put("scrollRight", new ScrollAction("ScrollRight",
  178. SwingConstants.HORIZONTAL, 1, true));
  179. map.put("unitScrollRight", new ScrollAction
  180. ("UnitScrollRight", SwingConstants.HORIZONTAL, 1, false));
  181. map.put("unitScrollLeft", new ScrollAction
  182. ("UnitScrollLeft", SwingConstants.HORIZONTAL, -1, false));
  183. } else {
  184. map.put("scrollLeft", new ScrollAction("scrollLeft",
  185. SwingConstants.HORIZONTAL, 1, true));
  186. map.put("scrollRight", new ScrollAction("ScrollRight",
  187. SwingConstants.HORIZONTAL, -1, true));
  188. map.put("unitScrollRight", new ScrollAction
  189. ("UnitScrollRight", SwingConstants.HORIZONTAL, -1, false));
  190. map.put("unitScrollLeft", new ScrollAction
  191. ("UnitScrollLeft", SwingConstants.HORIZONTAL, 1, false));
  192. }
  193. return map;
  194. }
  195. public void installUI(JComponent x) {
  196. scrollpane = (JScrollPane)x;
  197. installDefaults(scrollpane);
  198. installListeners(scrollpane);
  199. installKeyboardActions(scrollpane);
  200. }
  201. protected void uninstallDefaults(JScrollPane c) {
  202. LookAndFeel.uninstallBorder(scrollpane);
  203. if (scrollpane.getViewportBorder() instanceof UIResource) {
  204. scrollpane.setViewportBorder(null);
  205. }
  206. }
  207. protected void uninstallListeners(JComponent c) {
  208. JViewport viewport = scrollpane.getViewport();
  209. JScrollBar vsb = scrollpane.getVerticalScrollBar();
  210. JScrollBar hsb = scrollpane.getHorizontalScrollBar();
  211. if (viewport != null) {
  212. viewport.removeChangeListener(viewportChangeListener);
  213. }
  214. if (vsb != null) {
  215. vsb.getModel().removeChangeListener(vsbChangeListener);
  216. vsb.removePropertyChangeListener(vsbPropertyChangeListener);
  217. }
  218. if (hsb != null) {
  219. hsb.getModel().removeChangeListener(hsbChangeListener);
  220. hsb.removePropertyChangeListener(hsbPropertyChangeListener);
  221. }
  222. scrollpane.removePropertyChangeListener(spPropertyChangeListener);
  223. if (mouseScrollListener != null) {
  224. scrollpane.removeMouseWheelListener(mouseScrollListener);
  225. }
  226. vsbChangeListener = null;
  227. hsbChangeListener = null;
  228. viewportChangeListener = null;
  229. spPropertyChangeListener = null;
  230. mouseScrollListener = null;
  231. }
  232. protected void uninstallKeyboardActions(JScrollPane c) {
  233. SwingUtilities.replaceUIActionMap(c, null);
  234. SwingUtilities.replaceUIInputMap(c, JComponent.
  235. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
  236. }
  237. public void uninstallUI(JComponent c) {
  238. uninstallDefaults(scrollpane);
  239. uninstallListeners(scrollpane);
  240. uninstallKeyboardActions(scrollpane);
  241. scrollpane = null;
  242. }
  243. protected void syncScrollPaneWithViewport()
  244. {
  245. JViewport viewport = scrollpane.getViewport();
  246. JScrollBar vsb = scrollpane.getVerticalScrollBar();
  247. JScrollBar hsb = scrollpane.getHorizontalScrollBar();
  248. JViewport rowHead = scrollpane.getRowHeader();
  249. JViewport colHead = scrollpane.getColumnHeader();
  250. boolean ltr = scrollpane.getComponentOrientation().isLeftToRight();
  251. if (viewport != null) {
  252. Dimension extentSize = viewport.getExtentSize();
  253. Dimension viewSize = viewport.getViewSize();
  254. Point viewPosition = viewport.getViewPosition();
  255. if (vsb != null) {
  256. int extent = extentSize.height;
  257. int max = viewSize.height;
  258. int value = Math.max(0, Math.min(viewPosition.y, max - extent));
  259. vsb.setValues(value, extent, 0, max);
  260. }
  261. if (hsb != null) {
  262. int extent = extentSize.width;
  263. int max = viewSize.width;
  264. int value;
  265. if (ltr) {
  266. value = Math.max(0, Math.min(viewPosition.x, max - extent));
  267. } else {
  268. int currentValue = hsb.getValue();
  269. /* Use a particular formula to calculate "value"
  270. * until effective x coordinate is calculated.
  271. */
  272. if (setValueCalled && ((max - currentValue) == viewPosition.x)) {
  273. value = Math.max(0, Math.min(max - extent, currentValue));
  274. /* After "extent" is set, turn setValueCalled flag off.
  275. */
  276. if (extent != 0) {
  277. setValueCalled = false;
  278. }
  279. } else {
  280. if (extent > max) {
  281. viewPosition.x = max - extent;
  282. viewport.setViewPosition(viewPosition);
  283. value = 0;
  284. } else {
  285. /* The following line can't handle a small value of
  286. * viewPosition.x like Integer.MIN_VALUE correctly
  287. * because (max - extent - viewPositoiin.x) causes
  288. * an overflow. As a result, value becomes zero.
  289. * (e.g. setViewPosition(Integer.MAX_VALUE, ...)
  290. * in a user program causes a overflow.
  291. * Its expected value is (max - extent).)
  292. * However, this seems a trivial bug and adding a
  293. * fix makes this often-called method slow, so I'll
  294. * leave it until someone claims.
  295. */
  296. value = Math.max(0, Math.min(max - extent, max - extent - viewPosition.x));
  297. }
  298. }
  299. }
  300. hsb.setValues(value, extent, 0, max);
  301. }
  302. if (rowHead != null) {
  303. Point p = rowHead.getViewPosition();
  304. p.y = viewport.getViewPosition().y;
  305. p.x = 0;
  306. rowHead.setViewPosition(p);
  307. }
  308. if (colHead != null) {
  309. Point p = colHead.getViewPosition();
  310. if (ltr) {
  311. p.x = viewport.getViewPosition().x;
  312. } else {
  313. p.x = Math.max(0, viewport.getViewPosition().x);
  314. }
  315. p.y = 0;
  316. colHead.setViewPosition(p);
  317. }
  318. }
  319. }
  320. /**
  321. * Listener for viewport events.
  322. */
  323. public class ViewportChangeHandler implements ChangeListener
  324. {
  325. public void stateChanged(ChangeEvent e) {
  326. syncScrollPaneWithViewport();
  327. }
  328. }
  329. protected ChangeListener createViewportChangeListener() {
  330. return new ViewportChangeHandler();
  331. }
  332. /**
  333. * Horizontal scrollbar listener.
  334. */
  335. public class HSBChangeListener implements ChangeListener
  336. {
  337. public void stateChanged(ChangeEvent e)
  338. {
  339. JViewport viewport = scrollpane.getViewport();
  340. if (viewport != null) {
  341. BoundedRangeModel model = (BoundedRangeModel)(e.getSource());
  342. Point p = viewport.getViewPosition();
  343. int value = model.getValue();
  344. if (scrollpane.getComponentOrientation().isLeftToRight()) {
  345. p.x = value;
  346. } else {
  347. int max = viewport.getViewSize().width;
  348. int extent = viewport.getExtentSize().width;
  349. int oldX = p.x;
  350. /* Set new X coordinate based on "value".
  351. */
  352. p.x = max - extent - value;
  353. /* If setValue() was called before "extent" was fixed,
  354. * turn setValueCalled flag on.
  355. */
  356. if ((extent == 0) && (value != 0) && (oldX == max)) {
  357. setValueCalled = true;
  358. } else {
  359. /* When a pane without a horizontal scroll bar was
  360. * reduced and the bar appeared, the viewport should
  361. * show the right side of the view.
  362. */
  363. if ((extent != 0) && (oldX < 0) && (p.x == 0)) {
  364. p.x += value;
  365. }
  366. }
  367. }
  368. viewport.setViewPosition(p);
  369. }
  370. }
  371. }
  372. /**
  373. * Returns a <code>PropertyChangeListener</code> that will be installed
  374. * on the horizontal <code>JScrollBar</code>.
  375. */
  376. private PropertyChangeListener createHSBPropertyChangeListener() {
  377. return getSBPropertyChangeListener();
  378. }
  379. /**
  380. * Returns a shared <code>PropertyChangeListener</code> that will update
  381. * the listeners installed on the scrollbars as the model changes.
  382. */
  383. private PropertyChangeListener getSBPropertyChangeListener() {
  384. if (sbPropertyChangeListener == null) {
  385. sbPropertyChangeListener = new ScrollBarPropertyChangeHandler();
  386. }
  387. return sbPropertyChangeListener;
  388. }
  389. protected ChangeListener createHSBChangeListener() {
  390. return new HSBChangeListener();
  391. }
  392. /**
  393. * Vertical scrollbar listener.
  394. */
  395. public class VSBChangeListener implements ChangeListener
  396. {
  397. public void stateChanged(ChangeEvent e)
  398. {
  399. JViewport viewport = scrollpane.getViewport();
  400. if (viewport != null) {
  401. BoundedRangeModel model = (BoundedRangeModel)(e.getSource());
  402. Point p = viewport.getViewPosition();
  403. p.y = model.getValue();
  404. viewport.setViewPosition(p);
  405. }
  406. }
  407. }
  408. /**
  409. * PropertyChangeListener for the ScrollBars.
  410. */
  411. private class ScrollBarPropertyChangeHandler implements
  412. PropertyChangeListener {
  413. // Listens for changes in the model property and reinstalls the
  414. // horizontal/vertical PropertyChangeListeners.
  415. public void propertyChange(PropertyChangeEvent e) {
  416. String propertyName = e.getPropertyName();
  417. Object source = e.getSource();
  418. if ("model".equals(propertyName)) {
  419. JScrollBar sb = scrollpane.getVerticalScrollBar();
  420. BoundedRangeModel oldModel = (BoundedRangeModel)e.
  421. getOldValue();
  422. ChangeListener cl = null;
  423. if (source == sb) {
  424. cl = vsbChangeListener;
  425. }
  426. else if (source == scrollpane.getHorizontalScrollBar()) {
  427. sb = scrollpane.getHorizontalScrollBar();
  428. cl = hsbChangeListener;
  429. }
  430. if (cl != null) {
  431. if (oldModel != null) {
  432. oldModel.removeChangeListener(cl);
  433. }
  434. if (sb.getModel() != null) {
  435. sb.getModel().addChangeListener(cl);
  436. }
  437. }
  438. }
  439. else if ("componentOrientation".equals(propertyName)) {
  440. if (source == scrollpane.getHorizontalScrollBar()) {
  441. JScrollBar hsb = scrollpane.getHorizontalScrollBar();
  442. JViewport viewport = scrollpane.getViewport();
  443. Point p = viewport.getViewPosition();
  444. if (scrollpane.getComponentOrientation().isLeftToRight()) {
  445. p.x = hsb.getValue();
  446. } else {
  447. p.x = viewport.getViewSize().width - viewport.getExtentSize().width - hsb.getValue();
  448. }
  449. viewport.setViewPosition(p);
  450. }
  451. }
  452. }
  453. }
  454. /**
  455. * Returns a <code>PropertyChangeListener</code> that will be installed
  456. * on the vertical <code>JScrollBar</code>.
  457. */
  458. private PropertyChangeListener createVSBPropertyChangeListener() {
  459. return getSBPropertyChangeListener();
  460. }
  461. protected ChangeListener createVSBChangeListener() {
  462. return new VSBChangeListener();
  463. }
  464. /**
  465. * MouseWheelHandler is an inner class which implements the
  466. * MouseWheelListener interface. MouseWheelHandler responds to
  467. * MouseWheelEvents by scrolling the JScrollPane appropriately.
  468. * If the scroll pane's
  469. * <code>isWheelScrollingEnabled</code>
  470. * method returns false, no scrolling occurs.
  471. *
  472. * @see javax.swing.JScrollPane#isWheelScrollingEnabled
  473. * @see #createMouseWheelListener
  474. * @see java.awt.event.MouseWheelListener
  475. * @see java.awt.event.MouseWheelEvent
  476. * @since 1.4
  477. */
  478. protected class MouseWheelHandler implements MouseWheelListener {
  479. /**
  480. * Called when the mouse wheel is rotated while over a
  481. * JScrollPane.
  482. *
  483. * @param e MouseWheelEvent to be handled
  484. * @since 1.4
  485. */
  486. public void mouseWheelMoved(MouseWheelEvent e) {
  487. if (scrollpane.isWheelScrollingEnabled() &&
  488. e.getScrollAmount() != 0) {
  489. JScrollBar toScroll = scrollpane.getVerticalScrollBar();
  490. int direction = 0;
  491. // find which scrollbar to scroll, or return if none
  492. if (toScroll == null || !toScroll.isVisible()) {
  493. toScroll = scrollpane.getHorizontalScrollBar();
  494. if (toScroll == null || !toScroll.isVisible()) {
  495. return;
  496. }
  497. }
  498. direction = e.getWheelRotation() < 0 ? -1 : 1;
  499. if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
  500. BasicScrollBarUI.scrollByUnits(toScroll, direction,
  501. e.getScrollAmount());
  502. }
  503. else if (e.getScrollType() ==
  504. MouseWheelEvent.WHEEL_BLOCK_SCROLL) {
  505. BasicScrollBarUI.scrollByBlock(toScroll, direction);
  506. }
  507. }
  508. }
  509. }
  510. /**
  511. * Creates an instance of MouseWheelListener, which is added to the
  512. * JScrollPane by installUI(). The returned MouseWheelListener is used
  513. * to handle mouse wheel-driven scrolling.
  514. *
  515. * @return MouseWheelListener which implements wheel-driven scrolling
  516. * @see #installUI
  517. * @see MouseWheelHandler
  518. * @since 1.4
  519. */
  520. protected MouseWheelListener createMouseWheelListener() {
  521. return new MouseWheelHandler();
  522. }
  523. protected void updateScrollBarDisplayPolicy(PropertyChangeEvent e) {
  524. scrollpane.revalidate();
  525. scrollpane.repaint();
  526. }
  527. protected void updateViewport(PropertyChangeEvent e)
  528. {
  529. JViewport oldViewport = (JViewport)(e.getOldValue());
  530. JViewport newViewport = (JViewport)(e.getNewValue());
  531. if (oldViewport != null) {
  532. oldViewport.removeChangeListener(viewportChangeListener);
  533. }
  534. if (newViewport != null) {
  535. Point p = newViewport.getViewPosition();
  536. if (scrollpane.getComponentOrientation().isLeftToRight()) {
  537. p.x = Math.max(p.x, 0);
  538. } else {
  539. int max = newViewport.getViewSize().width;
  540. int extent = newViewport.getExtentSize().width;
  541. if (extent > max) {
  542. p.x = max - extent;
  543. } else {
  544. p.x = Math.max(0, Math.min(max - extent, p.x));
  545. }
  546. }
  547. p.y = Math.max(p.y, 0);
  548. newViewport.setViewPosition(p);
  549. newViewport.addChangeListener(viewportChangeListener);
  550. }
  551. }
  552. protected void updateRowHeader(PropertyChangeEvent e)
  553. {
  554. JViewport newRowHead = (JViewport)(e.getNewValue());
  555. if (newRowHead != null) {
  556. JViewport viewport = scrollpane.getViewport();
  557. Point p = newRowHead.getViewPosition();
  558. p.y = (viewport != null) ? viewport.getViewPosition().y : 0;
  559. newRowHead.setViewPosition(p);
  560. }
  561. }
  562. protected void updateColumnHeader(PropertyChangeEvent e)
  563. {
  564. JViewport newColHead = (JViewport)(e.getNewValue());
  565. if (newColHead != null) {
  566. JViewport viewport = scrollpane.getViewport();
  567. Point p = newColHead.getViewPosition();
  568. if (viewport == null) {
  569. p.x = 0;
  570. } else {
  571. if (scrollpane.getComponentOrientation().isLeftToRight()) {
  572. p.x = viewport.getViewPosition().x;
  573. } else {
  574. p.x = Math.max(0, viewport.getViewPosition().x);
  575. }
  576. }
  577. newColHead.setViewPosition(p);
  578. scrollpane.add(newColHead, COLUMN_HEADER);
  579. }
  580. }
  581. private void updateHorizontalScrollBar(PropertyChangeEvent pce) {
  582. updateScrollBar(pce, hsbChangeListener, hsbPropertyChangeListener);
  583. }
  584. private void updateVerticalScrollBar(PropertyChangeEvent pce) {
  585. updateScrollBar(pce, vsbChangeListener, vsbPropertyChangeListener);
  586. }
  587. private void updateScrollBar(PropertyChangeEvent pce, ChangeListener cl,
  588. PropertyChangeListener pcl) {
  589. JScrollBar sb = (JScrollBar)pce.getOldValue();
  590. if (sb != null) {
  591. if (cl != null) {
  592. sb.getModel().removeChangeListener(cl);
  593. }
  594. if (pcl != null) {
  595. sb.removePropertyChangeListener(pcl);
  596. }
  597. }
  598. sb = (JScrollBar)pce.getNewValue();
  599. if (sb != null) {
  600. if (cl != null) {
  601. sb.getModel().addChangeListener(cl);
  602. }
  603. if (pcl != null) {
  604. sb.addPropertyChangeListener(pcl);
  605. }
  606. }
  607. }
  608. public class PropertyChangeHandler implements PropertyChangeListener
  609. {
  610. public void propertyChange(PropertyChangeEvent e)
  611. {
  612. String propertyName = e.getPropertyName();
  613. if (propertyName.equals("verticalScrollBarDisplayPolicy")) {
  614. updateScrollBarDisplayPolicy(e);
  615. }
  616. else if (propertyName.equals("horizontalScrollBarDisplayPolicy")) {
  617. updateScrollBarDisplayPolicy(e);
  618. }
  619. else if (propertyName.equals("viewport")) {
  620. updateViewport(e);
  621. }
  622. else if (propertyName.equals("rowHeader")) {
  623. updateRowHeader(e);
  624. }
  625. else if (propertyName.equals("columnHeader")) {
  626. updateColumnHeader(e);
  627. }
  628. else if (propertyName.equals("verticalScrollBar")) {
  629. updateVerticalScrollBar(e);
  630. }
  631. else if (propertyName.equals("horizontalScrollBar")) {
  632. updateHorizontalScrollBar(e);
  633. }
  634. else if (propertyName.equals("componentOrientation")) {
  635. scrollpane.revalidate();
  636. scrollpane.repaint();
  637. InputMap inputMap = getInputMap(JComponent.
  638. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  639. SwingUtilities.replaceUIInputMap(scrollpane, JComponent.
  640. WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
  641. UIManager.getLookAndFeelDefaults().put("ScrollPane.actionMap",
  642. null);
  643. ActionMap actionMap = getActionMap();
  644. SwingUtilities.replaceUIActionMap(scrollpane, actionMap);
  645. }
  646. }
  647. }
  648. /**
  649. * Creates an instance of PropertyChangeListener that's added to
  650. * the JScrollPane by installUI(). Subclasses can override this method
  651. * to return a custom PropertyChangeListener, e.g.
  652. * <pre>
  653. * class MyScrollPaneUI extends BasicScrollPaneUI {
  654. * protected PropertyChangeListener <b>createPropertyChangeListener</b>() {
  655. * return new MyPropertyChangeListener();
  656. * }
  657. * public class MyPropertyChangeListener extends PropertyChangeListener {
  658. * public void propertyChange(PropertyChangeEvent e) {
  659. * if (e.getPropertyName().equals("viewport")) {
  660. * // do some extra work when the viewport changes
  661. * }
  662. * super.propertyChange(e);
  663. * }
  664. * }
  665. * }
  666. * </pre>
  667. *
  668. * @see java.beans.PropertyChangeListener
  669. * @see #installUI
  670. */
  671. protected PropertyChangeListener createPropertyChangeListener() {
  672. return new PropertyChangeHandler();
  673. }
  674. /**
  675. * Action to scroll left/right/up/down.
  676. */
  677. private static class ScrollAction extends AbstractAction {
  678. /** Direction to scroll. */
  679. protected int orientation;
  680. /** 1 indicates scroll down, -1 up. */
  681. protected int direction;
  682. /** True indicates a block scroll, otherwise a unit scroll. */
  683. private boolean block;
  684. protected ScrollAction(String name, int orientation, int direction,
  685. boolean block) {
  686. super(name);
  687. this.orientation = orientation;
  688. this.direction = direction;
  689. this.block = block;
  690. }
  691. public void actionPerformed(ActionEvent e) {
  692. JScrollPane scrollpane = (JScrollPane)e.getSource();
  693. JViewport vp = scrollpane.getViewport();
  694. Component view;
  695. if (vp != null && (view = vp.getView()) != null) {
  696. Rectangle visRect = vp.getViewRect();
  697. Dimension vSize = view.getSize();
  698. int amount;
  699. if (view instanceof Scrollable) {
  700. if (block) {
  701. amount = ((Scrollable)view).getScrollableBlockIncrement
  702. (visRect, orientation, direction);
  703. }
  704. else {
  705. amount = ((Scrollable)view).getScrollableUnitIncrement
  706. (visRect, orientation, direction);
  707. }
  708. }
  709. else {
  710. if (block) {
  711. if (orientation == SwingConstants.VERTICAL) {
  712. amount = visRect.height;
  713. }
  714. else {
  715. amount = visRect.width;
  716. }
  717. }
  718. else {
  719. amount = 10;
  720. }
  721. }
  722. if (orientation == SwingConstants.VERTICAL) {
  723. visRect.y += (amount * direction);
  724. if ((visRect.y + visRect.height) > vSize.height) {
  725. visRect.y = Math.max(0, vSize.height - visRect.height);
  726. }
  727. else if (visRect.y < 0) {
  728. visRect.y = 0;
  729. }
  730. }
  731. else {
  732. if (scrollpane.getComponentOrientation().isLeftToRight()) {
  733. visRect.x += (amount * direction);
  734. if ((visRect.x + visRect.width) > vSize.width) {
  735. visRect.x = Math.max(0, vSize.width - visRect.width);
  736. } else if (visRect.x < 0) {
  737. visRect.x = 0;
  738. }
  739. } else {
  740. visRect.x -= (amount * direction);
  741. if (visRect.width > vSize.width) {
  742. visRect.x = vSize.width - visRect.width;
  743. } else {
  744. visRect.x = Math.max(0, Math.min(vSize.width - visRect.width, visRect.x));
  745. }
  746. }
  747. }
  748. vp.setViewPosition(visRect.getLocation());
  749. }
  750. }
  751. }
  752. /**
  753. * Action to scroll to x,y location of 0,0.
  754. */
  755. private static class ScrollHomeAction extends AbstractAction {
  756. protected ScrollHomeAction(String name) {
  757. super(name);
  758. }
  759. public void actionPerformed(ActionEvent e) {
  760. JScrollPane scrollpane = (JScrollPane)e.getSource();
  761. JViewport vp = scrollpane.getViewport();
  762. Component view;
  763. if (vp != null && (view = vp.getView()) != null) {
  764. if (scrollpane.getComponentOrientation().isLeftToRight()) {
  765. vp.setViewPosition(new Point(0, 0));
  766. } else {
  767. Rectangle visRect = vp.getViewRect();
  768. Rectangle bounds = view.getBounds();
  769. vp.setViewPosition(new Point(bounds.width - visRect.width, 0));
  770. }
  771. }
  772. }
  773. }
  774. /**
  775. * Action to scroll to last visible location.
  776. */
  777. private static class ScrollEndAction extends AbstractAction {
  778. protected ScrollEndAction(String name) {
  779. super(name);
  780. }
  781. public void actionPerformed(ActionEvent e) {
  782. JScrollPane scrollpane = (JScrollPane)e.getSource();
  783. JViewport vp = scrollpane.getViewport();
  784. Component view;
  785. if (vp != null && (view = vp.getView()) != null) {
  786. Rectangle visRect = vp.getViewRect();
  787. Rectangle bounds = view.getBounds();
  788. if (scrollpane.getComponentOrientation().isLeftToRight()) {
  789. vp.setViewPosition(new Point(bounds.width - visRect.width,
  790. bounds.height - visRect.height));
  791. } else {
  792. vp.setViewPosition(new Point(0,
  793. bounds.height - visRect.height));
  794. }
  795. }
  796. }
  797. }
  798. }