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