1. /*
  2. * @(#)AbstractLayoutCache.java 1.15 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.tree;
  8. import javax.swing.event.TreeModelEvent;
  9. import java.awt.Dimension;
  10. import java.awt.Rectangle;
  11. import java.util.Enumeration;
  12. /**
  13. * <strong>Warning:</strong>
  14. * Serialized objects of this class will not be compatible with
  15. * future Swing releases. The current serialization support is
  16. * appropriate for short term storage or RMI between applications running
  17. * the same version of Swing. As of 1.4, support for long term storage
  18. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  19. * has been added to the <code>java.beans</code> package.
  20. * Please see {@link java.beans.XMLEncoder}.
  21. *
  22. * @version 1.15 01/23/03
  23. * @author Scott Violet
  24. */
  25. public abstract class AbstractLayoutCache implements RowMapper {
  26. /** Object responsible for getting the size of a node. */
  27. protected NodeDimensions nodeDimensions;
  28. /** Model providing information. */
  29. protected TreeModel treeModel;
  30. /** Selection model. */
  31. protected TreeSelectionModel treeSelectionModel;
  32. /**
  33. * True if the root node is displayed, false if its children are
  34. * the highest visible nodes.
  35. */
  36. protected boolean rootVisible;
  37. /**
  38. * Height to use for each row. If this is <= 0 the renderer will be
  39. * used to determine the height for each row.
  40. */
  41. protected int rowHeight;
  42. /**
  43. * Sets the renderer that is responsible for drawing nodes in the tree
  44. * and which is threfore responsible for calculating the dimensions of
  45. * individual nodes.
  46. *
  47. * @param nd a <code>NodeDimensions</code> object
  48. */
  49. public void setNodeDimensions(NodeDimensions nd) {
  50. this.nodeDimensions = nd;
  51. }
  52. /**
  53. * Returns the object that renders nodes in the tree, and which is
  54. * responsible for calculating the dimensions of individual nodes.
  55. *
  56. * @return the <code>NodeDimensions</code> object
  57. */
  58. public NodeDimensions getNodeDimensions() {
  59. return nodeDimensions;
  60. }
  61. /**
  62. * Sets the <code>TreeModel</code> that will provide the data.
  63. *
  64. * @param newModel the <code>TreeModel</code> that is to
  65. * provide the data
  66. */
  67. public void setModel(TreeModel newModel) {
  68. treeModel = newModel;
  69. }
  70. /**
  71. * Returns the <code>TreeModel</code> that is providing the data.
  72. *
  73. * @return the <code>TreeModel</code> that is providing the data
  74. */
  75. public TreeModel getModel() {
  76. return treeModel;
  77. }
  78. /**
  79. * Determines whether or not the root node from
  80. * the <code>TreeModel</code> is visible.
  81. *
  82. * @param rootVisible true if the root node of the tree is to be displayed
  83. * @see #rootVisible
  84. * @beaninfo
  85. * bound: true
  86. * description: Whether or not the root node
  87. * from the TreeModel is visible.
  88. */
  89. public void setRootVisible(boolean rootVisible) {
  90. this.rootVisible = rootVisible;
  91. }
  92. /**
  93. * Returns true if the root node of the tree is displayed.
  94. *
  95. * @return true if the root node of the tree is displayed
  96. * @see #rootVisible
  97. */
  98. public boolean isRootVisible() {
  99. return rootVisible;
  100. }
  101. /**
  102. * Sets the height of each cell. If the specified value
  103. * is less than or equal to zero the current cell renderer is
  104. * queried for each row's height.
  105. *
  106. * @param rowHeight the height of each cell, in pixels
  107. * @beaninfo
  108. * bound: true
  109. * description: The height of each cell.
  110. */
  111. public void setRowHeight(int rowHeight) {
  112. this.rowHeight = rowHeight;
  113. }
  114. /**
  115. * Returns the height of each row. If the returned value is less than
  116. * or equal to 0 the height for each row is determined by the
  117. * renderer.
  118. */
  119. public int getRowHeight() {
  120. return rowHeight;
  121. }
  122. /**
  123. * Sets the <code>TreeSelectionModel</code> used to manage the
  124. * selection to new LSM.
  125. *
  126. * @param newLSM the new <code>TreeSelectionModel</code>
  127. */
  128. public void setSelectionModel(TreeSelectionModel newLSM) {
  129. if(treeSelectionModel != null)
  130. treeSelectionModel.setRowMapper(null);
  131. treeSelectionModel = newLSM;
  132. if(treeSelectionModel != null)
  133. treeSelectionModel.setRowMapper(this);
  134. }
  135. /**
  136. * Returns the model used to maintain the selection.
  137. *
  138. * @return the <code>treeSelectionModel</code>
  139. */
  140. public TreeSelectionModel getSelectionModel() {
  141. return treeSelectionModel;
  142. }
  143. /**
  144. * Returns the preferred height.
  145. *
  146. * @return the preferred height
  147. */
  148. public int getPreferredHeight() {
  149. // Get the height
  150. int rowCount = getRowCount();
  151. if(rowCount > 0) {
  152. Rectangle bounds = getBounds(getPathForRow(rowCount - 1),
  153. null);
  154. if(bounds != null)
  155. return bounds.y + bounds.height;
  156. }
  157. return 0;
  158. }
  159. /**
  160. * Returns the preferred width for the passed in region.
  161. * The region is defined by the path closest to
  162. * <code>(bounds.x, bounds.y)</code> and
  163. * ends at <code>bounds.height + bounds.y</code>.
  164. * If <code>bounds</code> is <code>null</code>,
  165. * the preferred width for all the nodes
  166. * will be returned (and this may be a VERY expensive
  167. * computation).
  168. *
  169. * @param bounds the region being queried
  170. * @return the preferred width for the passed in region
  171. */
  172. public int getPreferredWidth(Rectangle bounds) {
  173. int rowCount = getRowCount();
  174. if(rowCount > 0) {
  175. // Get the width
  176. TreePath firstPath;
  177. int endY;
  178. if(bounds == null) {
  179. firstPath = getPathForRow(0);
  180. endY = Integer.MAX_VALUE;
  181. }
  182. else {
  183. firstPath = getPathClosestTo(bounds.x, bounds.y);
  184. endY = bounds.height + bounds.y;
  185. }
  186. Enumeration paths = getVisiblePathsFrom(firstPath);
  187. if(paths != null && paths.hasMoreElements()) {
  188. Rectangle pBounds = getBounds((TreePath)paths.nextElement(),
  189. null);
  190. int width;
  191. if(pBounds != null) {
  192. width = pBounds.x + pBounds.width;
  193. if (pBounds.y >= endY) {
  194. return width;
  195. }
  196. }
  197. else
  198. width = 0;
  199. while (pBounds != null && paths.hasMoreElements()) {
  200. pBounds = getBounds((TreePath)paths.nextElement(),
  201. pBounds);
  202. if (pBounds != null && pBounds.y < endY) {
  203. width = Math.max(width, pBounds.x + pBounds.width);
  204. }
  205. else {
  206. pBounds = null;
  207. }
  208. }
  209. return width;
  210. }
  211. }
  212. return 0;
  213. }
  214. //
  215. // Abstract methods that must be implemented to be concrete.
  216. //
  217. /**
  218. * Returns true if the value identified by row is currently expanded.
  219. */
  220. public abstract boolean isExpanded(TreePath path);
  221. /**
  222. * Returns a rectangle giving the bounds needed to draw path.
  223. *
  224. * @param path a <code>TreePath</code> specifying a node
  225. * @param placeIn a <code>Rectangle</code> object giving the
  226. * available space
  227. * @return a <code>Rectangle</code> object specifying the space to be used
  228. */
  229. public abstract Rectangle getBounds(TreePath path, Rectangle placeIn);
  230. /**
  231. * Returns the path for passed in row. If row is not visible
  232. * <code>null</code> is returned.
  233. *
  234. * @param row the row being queried
  235. * @return the <code>TreePath</code> for the given row
  236. */
  237. public abstract TreePath getPathForRow(int row);
  238. /**
  239. * Returns the row that the last item identified in path is visible
  240. * at. Will return -1 if any of the elements in path are not
  241. * currently visible.
  242. *
  243. * @param path the <code>TreePath</code> being queried
  244. * @return the row where the last item in path is visible or -1
  245. * if any elements in path aren't currently visible
  246. */
  247. public abstract int getRowForPath(TreePath path);
  248. /**
  249. * Returns the path to the node that is closest to x,y. If
  250. * there is nothing currently visible this will return <code>null</code>,
  251. * otherwise it'll always return a valid path.
  252. * If you need to test if the
  253. * returned object is exactly at x, y you should get the bounds for
  254. * the returned path and test x, y against that.
  255. *
  256. * @param x the horizontal component of the desired location
  257. * @param y the vertical component of the desired location
  258. * @return the <code>TreePath</code> closest to the specified point
  259. */
  260. public abstract TreePath getPathClosestTo(int x, int y);
  261. /**
  262. * Returns an <code>Enumerator</code> that increments over the visible
  263. * paths starting at the passed in location. The ordering of the
  264. * enumeration is based on how the paths are displayed.
  265. * The first element of the returned enumeration will be path,
  266. * unless it isn't visible,
  267. * in which case <code>null</code> will be returned.
  268. *
  269. * @param path the starting location for the enumeration
  270. * @return the <code>Enumerator</code> starting at the desired location
  271. */
  272. public abstract Enumeration getVisiblePathsFrom(TreePath path);
  273. /**
  274. * Returns the number of visible children for row.
  275. *
  276. * @param path the path being queried
  277. * @return the number of visible children for the specified path
  278. */
  279. public abstract int getVisibleChildCount(TreePath path);
  280. /**
  281. * Marks the path <code>path</code> expanded state to
  282. * <code>isExpanded</code>.
  283. *
  284. * @param path the path being expanded or collapsed
  285. * @param isExpanded true if the path should be expanded, false otherwise
  286. */
  287. public abstract void setExpandedState(TreePath path, boolean isExpanded);
  288. /**
  289. * Returns true if the path is expanded, and visible.
  290. *
  291. * @param path the path being queried
  292. * @return true if the path is expanded and visible, false otherwise
  293. */
  294. public abstract boolean getExpandedState(TreePath path);
  295. /**
  296. * Number of rows being displayed.
  297. *
  298. * @return the number of rows being displayed
  299. */
  300. public abstract int getRowCount();
  301. /**
  302. * Informs the <code>TreeState</code> that it needs to recalculate
  303. * all the sizes it is referencing.
  304. */
  305. public abstract void invalidateSizes();
  306. /**
  307. * Instructs the <code>LayoutCache</code> that the bounds for
  308. * <code>path</code> are invalid, and need to be updated.
  309. *
  310. * @param path the path being updated
  311. */
  312. public abstract void invalidatePathBounds(TreePath path);
  313. //
  314. // TreeModelListener methods
  315. // AbstractTreeState does not directly become a TreeModelListener on
  316. // the model, it is up to some other object to forward these methods.
  317. //
  318. /**
  319. * <p>
  320. * Invoked after a node (or a set of siblings) has changed in some
  321. * way. The node(s) have not changed locations in the tree or
  322. * altered their children arrays, but other attributes have
  323. * changed and may affect presentation. Example: the name of a
  324. * file has changed, but it is in the same location in the file
  325. * system.</p>
  326. *
  327. * <p>e.path() returns the path the parent of the changed node(s).</p>
  328. *
  329. * <p>e.childIndices() returns the index(es) of the changed node(s).</p>
  330. *
  331. * @param e the <code>TreeModelEvent</code>
  332. */
  333. public abstract void treeNodesChanged(TreeModelEvent e);
  334. /**
  335. * <p>Invoked after nodes have been inserted into the tree.</p>
  336. *
  337. * <p>e.path() returns the parent of the new nodes</p>
  338. * <p>e.childIndices() returns the indices of the new nodes in
  339. * ascending order.</p>
  340. *
  341. * @param e the <code>TreeModelEvent</code>
  342. */
  343. public abstract void treeNodesInserted(TreeModelEvent e);
  344. /**
  345. * <p>Invoked after nodes have been removed from the tree. Note that
  346. * if a subtree is removed from the tree, this method may only be
  347. * invoked once for the root of the removed subtree, not once for
  348. * each individual set of siblings removed.</p>
  349. *
  350. * <p>e.path() returns the former parent of the deleted nodes.</p>
  351. *
  352. * <p>e.childIndices() returns the indices the nodes had before they were deleted in ascending order.</p>
  353. *
  354. * @param e the <code>TreeModelEvent</code>
  355. */
  356. public abstract void treeNodesRemoved(TreeModelEvent e);
  357. /**
  358. * <p>Invoked after the tree has drastically changed structure from a
  359. * given node down. If the path returned by <code>e.getPath()</code>
  360. * is of length one and the first element does not identify the
  361. * current root node the first element should become the new root
  362. * of the tree.</p>
  363. *
  364. * <p>e.path() holds the path to the node.</p>
  365. * <p>e.childIndices() returns null.</p>
  366. *
  367. * @param e the <code>TreeModelEvent</code>
  368. */
  369. public abstract void treeStructureChanged(TreeModelEvent e);
  370. //
  371. // RowMapper
  372. //
  373. /**
  374. * Returns the rows that the <code>TreePath</code> instances in
  375. * <code>path</code> are being displayed at.
  376. * This method should return an array of the same length as that passed
  377. * in, and if one of the <code>TreePaths</code>
  378. * in <code>path</code> is not valid its entry in the array should
  379. * be set to -1.
  380. *
  381. * @param paths the array of <code>TreePath</code>s being queried
  382. * @return an array of the same length that is passed in containing
  383. * the rows that each corresponding where each
  384. * <code>TreePath</code> is displayed; if <code>paths</code>
  385. * is <code>null</code>, <code>null</code> is returned
  386. */
  387. public int[] getRowsForPaths(TreePath[] paths) {
  388. if(paths == null)
  389. return null;
  390. int numPaths = paths.length;
  391. int[] rows = new int[numPaths];
  392. for(int counter = 0; counter < numPaths; counter++)
  393. rows[counter] = getRowForPath(paths[counter]);
  394. return rows;
  395. }
  396. //
  397. // Local methods that subclassers may wish to use that are primarly
  398. // convenience methods.
  399. //
  400. /**
  401. * Returns, by reference in <code>placeIn</code>,
  402. * the size needed to represent <code>value</code>.
  403. * If <code>inPlace</code> is <code>null</code>, a newly created
  404. * <code>Rectangle</code> should be returned, otherwise the value
  405. * should be placed in <code>inPlace</code> and returned. This will
  406. * return <code>null</code> if there is no renderer.
  407. *
  408. * @param value the <code>value</code> to be represented
  409. * @param row row being queried
  410. * @param depth the depth of the row
  411. * @param expanded true if row is expanded, false otherwise
  412. * @param placeIn a <code>Rectangle</code> containing the size needed
  413. * to represent <code>value</code>
  414. * @return a <code>Rectangle</code> containing the node dimensions,
  415. * or <code>null</code> if node has no dimension
  416. */
  417. protected Rectangle getNodeDimensions(Object value, int row, int depth,
  418. boolean expanded,
  419. Rectangle placeIn) {
  420. NodeDimensions nd = getNodeDimensions();
  421. if(nd != null) {
  422. return nd.getNodeDimensions(value, row, depth, expanded, placeIn);
  423. }
  424. return null;
  425. }
  426. /**
  427. * Returns true if the height of each row is a fixed size.
  428. */
  429. protected boolean isFixedRowHeight() {
  430. return (rowHeight > 0);
  431. }
  432. /**
  433. * Used by <code>AbstractLayoutCache</code> to determine the size
  434. * and x origin of a particular node.
  435. */
  436. static public abstract class NodeDimensions {
  437. /**
  438. * Returns, by reference in bounds, the size and x origin to
  439. * place value at. The calling method is responsible for determining
  440. * the Y location. If bounds is <code>null</code>, a newly created
  441. * <code>Rectangle</code> should be returned,
  442. * otherwise the value should be placed in bounds and returned.
  443. *
  444. * @param value the <code>value</code> to be represented
  445. * @param row row being queried
  446. * @param depth the depth of the row
  447. * @param expanded true if row is expanded, false otherwise
  448. * @param bounds a <code>Rectangle</code> containing the size needed
  449. * to represent <code>value</code>
  450. * @return a <code>Rectangle</code> containing the node dimensions,
  451. * or <code>null</code> if node has no dimension
  452. */
  453. public abstract Rectangle getNodeDimensions(Object value, int row,
  454. int depth,
  455. boolean expanded,
  456. Rectangle bounds);
  457. }
  458. }