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