1. /*
  2. * @(#)BasicScrollPaneUI.java 1.44 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 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. /**
  25. * A default L&F implementation of ScrollPaneUI.
  26. *
  27. * @version 1.44 11/29/01
  28. * @author Hans Muller
  29. */
  30. public class BasicScrollPaneUI
  31. extends ScrollPaneUI implements ScrollPaneConstants
  32. {
  33. protected JScrollPane scrollpane;
  34. protected ChangeListener vsbChangeListener;
  35. protected ChangeListener hsbChangeListener;
  36. protected ChangeListener viewportChangeListener;
  37. protected PropertyChangeListener spPropertyChangeListener;
  38. public static ComponentUI createUI(JComponent x) {
  39. return new BasicScrollPaneUI();
  40. }
  41. public void paint(Graphics g, JComponent c) {
  42. Border vpBorder = scrollpane.getViewportBorder();
  43. if (vpBorder != null) {
  44. Rectangle r = scrollpane.getViewportBorderBounds();
  45. vpBorder.paintBorder(scrollpane, g, r.x, r.y, r.width, r.height);
  46. }
  47. }
  48. /**
  49. * @return null which indicates that the LayoutManager will compute the value
  50. * @see JComponent#getPreferredSize
  51. */
  52. public Dimension getPreferredSize(JComponent c) {
  53. return null;
  54. }
  55. /**
  56. * @return the preferred size
  57. * @see #getPreferredSize
  58. */
  59. public Dimension getMinimumSize(JComponent c) {
  60. return getPreferredSize(c);
  61. }
  62. /**
  63. * @return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE)
  64. */
  65. public Dimension getMaximumSize(JComponent c) {
  66. return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
  67. }
  68. protected void installDefaults(JScrollPane scrollpane)
  69. {
  70. LookAndFeel.installBorder(scrollpane, "ScrollPane.border");
  71. LookAndFeel.installColorsAndFont(scrollpane,
  72. "ScrollPane.background",
  73. "ScrollPane.foreground",
  74. "ScrollPane.font");
  75. Border vpBorder = scrollpane.getViewportBorder();
  76. if ((vpBorder == null) ||( vpBorder instanceof UIResource)) {
  77. vpBorder = UIManager.getBorder("ScrollPane.viewportBorder");
  78. scrollpane.setViewportBorder(vpBorder);
  79. }
  80. }
  81. protected void installListeners(JScrollPane c)
  82. {
  83. vsbChangeListener = createVSBChangeListener();
  84. hsbChangeListener = createHSBChangeListener();
  85. viewportChangeListener = createViewportChangeListener();
  86. spPropertyChangeListener = createPropertyChangeListener();
  87. JViewport viewport = scrollpane.getViewport();
  88. JScrollBar vsb = scrollpane.getVerticalScrollBar();
  89. JScrollBar hsb = scrollpane.getHorizontalScrollBar();
  90. if (viewport != null) {
  91. viewport.addChangeListener(viewportChangeListener);
  92. }
  93. if (vsb != null) {
  94. vsb.getModel().addChangeListener(vsbChangeListener);
  95. }
  96. if (hsb != null) {
  97. hsb.getModel().addChangeListener(hsbChangeListener);
  98. }
  99. scrollpane.addPropertyChangeListener(spPropertyChangeListener);
  100. }
  101. protected void installKeyboardActions(JScrollPane c) {
  102. // up
  103. c.registerKeyboardAction(new ScrollAction("ScrollUp", SwingConstants.
  104. VERTICAL, -1), KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
  105. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  106. // down
  107. c.registerKeyboardAction(new ScrollAction("ScrollDown", SwingConstants.
  108. VERTICAL, 1), KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
  109. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  110. // left
  111. c.registerKeyboardAction(new ScrollAction("ScrollLeft", SwingConstants.
  112. HORIZONTAL, -1), KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP,
  113. InputEvent.CTRL_MASK),
  114. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  115. // right
  116. c.registerKeyboardAction(new ScrollAction("ScrollDown", SwingConstants.
  117. HORIZONTAL, 1), KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN,
  118. InputEvent.CTRL_MASK),
  119. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  120. // home
  121. c.registerKeyboardAction(new ScrollHomeAction("ScrollHome"),
  122. KeyStroke.getKeyStroke(KeyEvent.VK_HOME,
  123. InputEvent.CTRL_MASK),
  124. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  125. // down
  126. c.registerKeyboardAction(new ScrollEndAction("ScrollEnd"),
  127. KeyStroke.getKeyStroke(KeyEvent.VK_END,
  128. InputEvent.CTRL_MASK),
  129. JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  130. }
  131. public void installUI(JComponent x) {
  132. scrollpane = (JScrollPane)x;
  133. installDefaults(scrollpane);
  134. installListeners(scrollpane);
  135. installKeyboardActions(scrollpane);
  136. }
  137. protected void uninstallDefaults(JScrollPane c) {
  138. LookAndFeel.uninstallBorder(scrollpane);
  139. if (scrollpane.getViewportBorder() instanceof UIResource) {
  140. scrollpane.setViewportBorder(null);
  141. }
  142. }
  143. protected void uninstallListeners(JComponent c) {
  144. JViewport viewport = scrollpane.getViewport();
  145. JScrollBar vsb = scrollpane.getVerticalScrollBar();
  146. JScrollBar hsb = scrollpane.getHorizontalScrollBar();
  147. if (viewport != null) {
  148. viewport.removeChangeListener(viewportChangeListener);
  149. }
  150. if (vsb != null) {
  151. vsb.getModel().removeChangeListener(vsbChangeListener);
  152. }
  153. if (hsb != null) {
  154. hsb.getModel().removeChangeListener(hsbChangeListener);
  155. }
  156. scrollpane.removePropertyChangeListener(spPropertyChangeListener);
  157. vsbChangeListener = null;
  158. hsbChangeListener = null;
  159. viewportChangeListener = null;
  160. spPropertyChangeListener = null;
  161. }
  162. protected void uninstallKeyboardActions(JScrollPane c) {
  163. // up
  164. c.unregisterKeyboardAction(KeyStroke.getKeyStroke
  165. (KeyEvent.VK_PAGE_UP, 0));
  166. // down
  167. c.unregisterKeyboardAction(KeyStroke.getKeyStroke
  168. (KeyEvent.VK_PAGE_DOWN, 0));
  169. // left
  170. c.unregisterKeyboardAction(KeyStroke.getKeyStroke
  171. (KeyEvent.VK_PAGE_UP, InputEvent.CTRL_MASK));
  172. // right
  173. c.unregisterKeyboardAction(KeyStroke.getKeyStroke
  174. (KeyEvent.VK_PAGE_DOWN, InputEvent.CTRL_MASK));
  175. // home
  176. c.unregisterKeyboardAction(KeyStroke.getKeyStroke
  177. (KeyEvent.VK_HOME, InputEvent.CTRL_MASK));
  178. // end
  179. c.unregisterKeyboardAction(KeyStroke.getKeyStroke
  180. (KeyEvent.VK_END, InputEvent.CTRL_MASK));
  181. }
  182. public void uninstallUI(JComponent c) {
  183. uninstallDefaults(scrollpane);
  184. uninstallListeners(scrollpane);
  185. uninstallKeyboardActions(scrollpane);
  186. scrollpane = null;
  187. }
  188. protected void syncScrollPaneWithViewport()
  189. {
  190. JViewport viewport = scrollpane.getViewport();
  191. JScrollBar vsb = scrollpane.getVerticalScrollBar();
  192. JScrollBar hsb = scrollpane.getHorizontalScrollBar();
  193. JViewport rowHead = scrollpane.getRowHeader();
  194. JViewport colHead = scrollpane.getColumnHeader();
  195. if (viewport != null) {
  196. Dimension extentSize = viewport.getExtentSize();
  197. Dimension viewSize = viewport.getViewSize();
  198. Point viewPosition = viewport.getViewPosition();
  199. if (vsb != null) {
  200. int extent = extentSize.height;
  201. int max = viewSize.height;
  202. int value = Math.max(0, Math.min(viewPosition.y, max - extent));
  203. vsb.setValues(value, extent, 0, max);
  204. }
  205. if (hsb != null) {
  206. int extent = extentSize.width;
  207. int max = viewSize.width;
  208. int value = Math.max(0, Math.min(viewPosition.x, max - extent));
  209. hsb.setValues(value, extent, 0, max);
  210. }
  211. if (rowHead != null) {
  212. Point p = rowHead.getViewPosition();
  213. p.y = viewport.getViewPosition().y;
  214. rowHead.setViewPosition(p);
  215. }
  216. if (colHead != null) {
  217. Point p = colHead.getViewPosition();
  218. p.x = viewport.getViewPosition().x;
  219. colHead.setViewPosition(p);
  220. }
  221. }
  222. }
  223. /**
  224. * Listener for viewport events.
  225. */
  226. public class ViewportChangeHandler implements ChangeListener
  227. {
  228. public void stateChanged(ChangeEvent e) {
  229. syncScrollPaneWithViewport();
  230. }
  231. }
  232. protected ChangeListener createViewportChangeListener() {
  233. return new ViewportChangeHandler();
  234. }
  235. /**
  236. * Horizontal scrollbar listener.
  237. */
  238. public class HSBChangeListener implements ChangeListener
  239. {
  240. public void stateChanged(ChangeEvent e)
  241. {
  242. JViewport viewport = scrollpane.getViewport();
  243. if (viewport != null) {
  244. BoundedRangeModel model = (BoundedRangeModel)(e.getSource());
  245. Point p = viewport.getViewPosition();
  246. p.x = model.getValue();
  247. viewport.setViewPosition(p);
  248. }
  249. }
  250. }
  251. protected ChangeListener createHSBChangeListener() {
  252. return new HSBChangeListener();
  253. }
  254. /**
  255. * Vertical scrollbar listener.
  256. */
  257. public class VSBChangeListener implements ChangeListener
  258. {
  259. public void stateChanged(ChangeEvent e)
  260. {
  261. JViewport viewport = scrollpane.getViewport();
  262. if (viewport != null) {
  263. BoundedRangeModel model = (BoundedRangeModel)(e.getSource());
  264. Point p = viewport.getViewPosition();
  265. p.y = model.getValue();
  266. viewport.setViewPosition(p);
  267. }
  268. }
  269. }
  270. protected ChangeListener createVSBChangeListener() {
  271. return new VSBChangeListener();
  272. }
  273. protected void updateScrollBarDisplayPolicy(PropertyChangeEvent e) {
  274. scrollpane.revalidate();
  275. scrollpane.repaint();
  276. }
  277. protected void updateViewport(PropertyChangeEvent e)
  278. {
  279. JViewport oldViewport = (JViewport)(e.getOldValue());
  280. JViewport newViewport = (JViewport)(e.getNewValue());
  281. if (oldViewport != null) {
  282. oldViewport.removeChangeListener(viewportChangeListener);
  283. }
  284. if (newViewport != null) {
  285. Point p = newViewport.getViewPosition();
  286. p.x = Math.max(p.x, 0);
  287. p.y = Math.max(p.y, 0);
  288. newViewport.setViewPosition(p);
  289. newViewport.addChangeListener(viewportChangeListener);
  290. }
  291. }
  292. protected void updateRowHeader(PropertyChangeEvent e)
  293. {
  294. JViewport newRowHead = (JViewport)(e.getNewValue());
  295. if (newRowHead != null) {
  296. JViewport viewport = scrollpane.getViewport();
  297. Point p = newRowHead.getViewPosition();
  298. p.y = (viewport != null) ? viewport.getViewPosition().y : 0;
  299. newRowHead.setViewPosition(p);
  300. }
  301. }
  302. protected void updateColumnHeader(PropertyChangeEvent e)
  303. {
  304. JViewport newColHead = (JViewport)(e.getNewValue());
  305. if (newColHead != null) {
  306. JViewport viewport = scrollpane.getViewport();
  307. Point p = newColHead.getViewPosition();
  308. p.x = (viewport != null) ? viewport.getViewPosition().x : 0;
  309. newColHead.setViewPosition(p);
  310. scrollpane.add(newColHead, COLUMN_HEADER);
  311. }
  312. }
  313. public class PropertyChangeHandler implements PropertyChangeListener
  314. {
  315. public void propertyChange(PropertyChangeEvent e)
  316. {
  317. String propertyName = e.getPropertyName();
  318. if (propertyName.equals("verticalScrollBarDisplayPolicy")) {
  319. updateScrollBarDisplayPolicy(e);
  320. }
  321. else if (propertyName.equals("horizontalScrollBarDisplayPolicy")) {
  322. updateScrollBarDisplayPolicy(e);
  323. }
  324. else if (propertyName.equals("viewport")) {
  325. updateViewport(e);
  326. }
  327. else if (propertyName.equals("rowHeader")) {
  328. updateRowHeader(e);
  329. }
  330. else if (propertyName.equals("columnHeader")) {
  331. updateColumnHeader(e);
  332. }
  333. }
  334. }
  335. /**
  336. * Creates an instance of PropertyChangeListener that's added to
  337. * the JScrollPane by installUI(). Subclasses can override this method
  338. * to return a custom PropertyChangeListener, e.g.
  339. * <pre>
  340. * class MyScrollPaneUI extends BasicScrollPaneUI {
  341. * protected PropertyChangeListener <b>createPropertyListener</b>() {
  342. * return new MyPropertyListener();
  343. * }
  344. * public class MyPropertyListener extends PropertyListener {
  345. * public void propertyChange(PropertyChangeEvent e) {
  346. * if (e.getPropertyName().equals("viewport")) {
  347. * // do some extra work when the viewport changes
  348. * }
  349. * super.propertyChange(e);
  350. * }
  351. * }
  352. * }
  353. * </pre>
  354. *
  355. * @see PropertyListener
  356. * @see #installUI
  357. */
  358. protected PropertyChangeListener createPropertyChangeListener() {
  359. return new PropertyChangeHandler();
  360. }
  361. /**
  362. * A generaic action this is only enabled when the JList is enabled.
  363. */
  364. private abstract class ScrollPaneAction implements ActionListener {
  365. protected ScrollPaneAction(String name) {
  366. }
  367. public boolean isEnabled() { return (scrollpane != null &&
  368. scrollpane.isEnabled()); }
  369. }
  370. /**
  371. * Action to scroll left/right/up/down.
  372. */
  373. private class ScrollAction extends ScrollPaneAction {
  374. /** Direction to scroll. */
  375. protected int orientation;
  376. /** 1 indicates scroll down, -1 up. */
  377. protected int direction;
  378. protected ScrollAction(String name, int orientation, int direction) {
  379. super(name);
  380. this.orientation = orientation;
  381. this.direction = direction;
  382. }
  383. public void actionPerformed(ActionEvent e) {
  384. JViewport vp = scrollpane.getViewport();
  385. Component view;
  386. if (vp != null && (view = vp.getView()) != null) {
  387. Rectangle visRect = vp.getViewRect();
  388. Dimension vSize = view.getSize();
  389. int amount;
  390. if (view instanceof Scrollable) {
  391. amount = ((Scrollable)view).getScrollableBlockIncrement
  392. (visRect, orientation, direction);
  393. }
  394. else {
  395. if (orientation == SwingConstants.VERTICAL) {
  396. amount = visRect.height;
  397. }
  398. else {
  399. amount = visRect.width;
  400. }
  401. }
  402. if (orientation == SwingConstants.VERTICAL) {
  403. visRect.y += (amount * direction);
  404. if ((visRect.y + visRect.height) > vSize.height) {
  405. visRect.y = Math.max(0, vSize.height - visRect.height);
  406. }
  407. }
  408. else {
  409. visRect.x += (amount * direction);
  410. if ((visRect.x + visRect.width) > vSize.width) {
  411. visRect.x = Math.max(0, vSize.width - visRect.width);
  412. }
  413. }
  414. vp.setViewPosition(visRect.getLocation());
  415. }
  416. }
  417. }
  418. /**
  419. * Action to scroll to x,y location of 0,0.
  420. */
  421. private class ScrollHomeAction extends ScrollPaneAction {
  422. protected ScrollHomeAction(String name) {
  423. super(name);
  424. }
  425. public void actionPerformed(ActionEvent e) {
  426. JViewport vp = scrollpane.getViewport();
  427. Component view;
  428. if (vp != null && (view = vp.getView()) != null) {
  429. vp.setViewPosition(new Point(0, 0));
  430. }
  431. }
  432. }
  433. /**
  434. * Action to scroll to last visible location.
  435. */
  436. private class ScrollEndAction extends ScrollPaneAction {
  437. protected ScrollEndAction(String name) {
  438. super(name);
  439. }
  440. public void actionPerformed(ActionEvent e) {
  441. JViewport vp = scrollpane.getViewport();
  442. Component view;
  443. if (vp != null && (view = vp.getView()) != null) {
  444. Rectangle visRect = vp.getViewRect();
  445. Rectangle bounds = view.getBounds();
  446. vp.setViewPosition(new Point(bounds.width - visRect.width,
  447. bounds.height - visRect.height));
  448. }
  449. }
  450. }
  451. }