1. /*
  2. * @(#)AbstractLayoutCache.java 1.10 00/02/02
  3. *
  4. * Copyright 1998-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.tree;
  11. import javax.swing.event.TreeModelEvent;
  12. import java.awt.Dimension;
  13. import java.awt.Rectangle;
  14. import java.util.Enumeration;
  15. /**
  16. * <strong>Warning:</strong>
  17. * Serialized objects of this class will not be compatible with
  18. * future Swing releases. The current serialization support is appropriate
  19. * for short term storage or RMI between applications running the same
  20. * version of Swing. A future release of Swing will provide support for
  21. * long term persistence.
  22. *
  23. * @version 1.10 02/02/00
  24. * @author Scott Violet
  25. */
  26. public abstract class AbstractLayoutCache implements RowMapper {
  27. /** Object responsible for getting the size of a node. */
  28. protected NodeDimensions nodeDimensions;
  29. /** Model providing information. */
  30. protected TreeModel treeModel;
  31. /** Selection model. */
  32. protected TreeSelectionModel treeSelectionModel;
  33. /**
  34. * True if the root node is displayed, false if its children are
  35. * the highest visible nodes.
  36. */
  37. protected boolean rootVisible;
  38. /**
  39. * Height to use for each row. If this is <= 0 the renderer will be
  40. * used to determine the height for each row.
  41. */
  42. protected int rowHeight;
  43. /**
  44. * Sets the renderer that is responsible for drawing nodes in the tree
  45. * and which is threfore responsible foc calculating the dimensions of
  46. * individual nodes.
  47. *
  48. * @param nd a NodeDimensions object
  49. */
  50. public void setNodeDimensions(NodeDimensions nd) {
  51. this.nodeDimensions = nd;
  52. }
  53. /**
  54. * Returns the object that renders nodes in the tree, and which is
  55. * responsible for calculating the dimensions of individual nodes.
  56. *
  57. * @return the NodeDimensions object
  58. */
  59. public NodeDimensions getNodeDimensions() {
  60. return nodeDimensions;
  61. }
  62. /**
  63. * Sets the TreeModel that will provide the data.
  64. *
  65. * @param newModel the TreeModel that is to provide the data
  66. */
  67. public void setModel(TreeModel newModel) {
  68. treeModel = newModel;
  69. }
  70. /**
  71. * Returns the TreeModel that is providing the data.
  72. *
  73. * @return the TreeModel 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 TreeModel 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. * @param the height of each cell, in pixels. Zero or negative if the
  120. * height of each row is determined by the tree cell renderer
  121. */
  122. public int getRowHeight() {
  123. return rowHeight;
  124. }
  125. /**
  126. * Sets the TreeSelectionModel used to manage the selection to
  127. * new LSM.
  128. */
  129. public void setSelectionModel(TreeSelectionModel newLSM) {
  130. if(treeSelectionModel != null)
  131. treeSelectionModel.setRowMapper(null);
  132. treeSelectionModel = newLSM;
  133. if(treeSelectionModel != null)
  134. treeSelectionModel.setRowMapper(this);
  135. }
  136. /**
  137. * Returns the model used to maintain the selection.
  138. */
  139. public TreeSelectionModel getSelectionModel() {
  140. return treeSelectionModel;
  141. }
  142. /**
  143. * Returns the preferred height.
  144. */
  145. public int getPreferredHeight() {
  146. // Get the height
  147. int rowCount = getRowCount();
  148. if(rowCount > 0) {
  149. Rectangle bounds = getBounds(getPathForRow(rowCount - 1),
  150. null);
  151. if(bounds != null)
  152. return bounds.y + bounds.height;
  153. }
  154. return 0;
  155. }
  156. /**
  157. * Returns the preferred width for the passed in region. If
  158. * <code>bounds</code> is null, the preferred width for all the nodes
  159. * will be returned (and this may be VERY expensive).
  160. */
  161. public int getPreferredWidth(Rectangle bounds) {
  162. int rowCount = getRowCount();
  163. if(rowCount > 0) {
  164. // Get the width
  165. TreePath firstPath;
  166. int endY;
  167. if(bounds == null) {
  168. firstPath = getPathForRow(0);
  169. endY = Integer.MAX_VALUE;
  170. }
  171. else {
  172. firstPath = getPathClosestTo(bounds.x, bounds.y);
  173. endY = bounds.height + bounds.y;
  174. }
  175. Enumeration paths = getVisiblePathsFrom(firstPath);
  176. if(paths != null && paths.hasMoreElements()) {
  177. Rectangle pBounds = getBounds((TreePath)paths.nextElement(),
  178. null);
  179. int width;
  180. if(pBounds != null) {
  181. width = pBounds.x + pBounds.width;
  182. if (pBounds.y >= endY) {
  183. return width;
  184. }
  185. }
  186. else
  187. width = 0;
  188. while (pBounds != null && paths.hasMoreElements()) {
  189. pBounds = getBounds((TreePath)paths.nextElement(),
  190. pBounds);
  191. if (pBounds != null && pBounds.y < endY) {
  192. width = Math.max(width, pBounds.x + pBounds.width);
  193. }
  194. else {
  195. pBounds = null;
  196. }
  197. }
  198. return width;
  199. }
  200. }
  201. return 0;
  202. }
  203. //
  204. // Abstract methods that must be implemented to be concrete.
  205. //
  206. /**
  207. * Returns true if the value identified by row is currently expanded.
  208. */
  209. public abstract boolean isExpanded(TreePath path);
  210. /**
  211. * Returns a rectangle giving the bounds needed to draw path.
  212. *
  213. * @param path a TreePath specifying a node
  214. * @param placeIn a Rectangle object giving the available space
  215. * @return a Rectangle object specifying the space to be used
  216. */
  217. public abstract Rectangle getBounds(TreePath path, Rectangle placeIn);
  218. /**
  219. * Returns the path for passed in row. If row is not visible
  220. * null is returned.
  221. */
  222. public abstract TreePath getPathForRow(int row);
  223. /**
  224. * Returns the row that the last item identified in path is visible
  225. * at. Will return -1 if any of the elements in path are not
  226. * currently visible.
  227. */
  228. public abstract int getRowForPath(TreePath path);
  229. /**
  230. * Returns the path to the node that is closest to x,y. If
  231. * there is nothing currently visible this will return null, otherwise
  232. * it'll always return a valid path. If you need to test if the
  233. * returned object is exactly at x, y you should get the bounds for
  234. * the returned path and test x, y against that.
  235. */
  236. public abstract TreePath getPathClosestTo(int x, int y);
  237. /**
  238. * Returns an Enumerator that increments over the visible paths
  239. * starting at the passed in location. The ordering of the enumeration
  240. * is based on how the paths are displayed. The first element of the
  241. * returned enumeration will be path, unless it isn't visible, in
  242. * which case null will be returned.
  243. */
  244. public abstract Enumeration getVisiblePathsFrom(TreePath path);
  245. /**
  246. * Returns the number of visible children for row.
  247. */
  248. public abstract int getVisibleChildCount(TreePath path);
  249. /**
  250. * Marks the path <code>path</code> expanded state to
  251. * <code>isExpanded</code>.
  252. */
  253. public abstract void setExpandedState(TreePath path, boolean isExpanded);
  254. /**
  255. * Returns true if the path is expanded, and visible.
  256. */
  257. public abstract boolean getExpandedState(TreePath path);
  258. /**
  259. * Number of rows being displayed.
  260. */
  261. public abstract int getRowCount();
  262. /**
  263. * Informs the TreeState that it needs to recalculate all the sizes
  264. * it is referencing.
  265. */
  266. public abstract void invalidateSizes();
  267. /**
  268. * Instructs the LayoutCache that the bounds for <code>path</code>
  269. * are invalid, and need to be updated.
  270. */
  271. public abstract void invalidatePathBounds(TreePath path);
  272. //
  273. // TreeModelListener methods
  274. // AbstractTreeState does not directly become a TreeModelListener on
  275. // the model, it is up to some other object to forward these methods.
  276. //
  277. /**
  278. * <p>Invoked after a node (or a set of siblings) has changed in some
  279. * way. The node(s) have not changed locations in the tree or
  280. * altered their children arrays, but other attributes have
  281. * changed and may affect presentation. Example: the name of a
  282. * file has changed, but it is in the same location in the file
  283. * system.</p>
  284. *
  285. * <p>e.path() returns the path the parent of the changed node(s).</p>
  286. *
  287. * <p>e.childIndices() returns the index(es) of the changed node(s).</p>
  288. */
  289. public abstract void treeNodesChanged(TreeModelEvent e);
  290. /**
  291. * <p>Invoked after nodes have been inserted into the tree.</p>
  292. *
  293. * <p>e.path() returns the parent of the new nodes
  294. * <p>e.childIndices() returns the indices of the new nodes in
  295. * ascending order.
  296. */
  297. public abstract void treeNodesInserted(TreeModelEvent e);
  298. /**
  299. * <p>Invoked after nodes have been removed from the tree. Note that
  300. * if a subtree is removed from the tree, this method may only be
  301. * invoked once for the root of the removed subtree, not once for
  302. * each individual set of siblings removed.</p>
  303. *
  304. * <p>e.path() returns the former parent of the deleted nodes.</p>
  305. *
  306. * <p>e.childIndices() returns the indices the nodes had before they were deleted in ascending order.</p>
  307. */
  308. public abstract void treeNodesRemoved(TreeModelEvent e);
  309. /**
  310. * <p>Invoked after the tree has drastically changed structure from a
  311. * given node down. If the path returned by e.getPath() is of length
  312. * one and the first element does not identify the current root node
  313. * the first element should become the new root of the tree.<p>
  314. *
  315. * <p>e.path() holds the path to the node.</p>
  316. * <p>e.childIndices() returns null.</p>
  317. */
  318. public abstract void treeStructureChanged(TreeModelEvent e);
  319. //
  320. // RowMapper
  321. //
  322. /**
  323. * Returns the rows that the TreePath instances in <code>path</code>
  324. * are being displayed at. The receiver should return an array of
  325. * the same length as that passed in, and if one of the TreePaths
  326. * in <code>path</code> is not valid its entry in the array should
  327. * be set to -1.
  328. */
  329. public int[] getRowsForPaths(TreePath[] paths) {
  330. if(paths == null)
  331. return null;
  332. int numPaths = paths.length;
  333. int[] rows = new int[numPaths];
  334. for(int counter = 0; counter < numPaths; counter++)
  335. rows[counter] = getRowForPath(paths[counter]);
  336. return rows;
  337. }
  338. //
  339. // Local methods that subclassers may wish to use that are primarly
  340. // convenience methods.
  341. //
  342. /**
  343. * Returns, by reference in size, the size needed to reprensent
  344. * value. If size is null, a newly created Dimension should be returned,
  345. * otherwise the value should be placed in size and returned. This will
  346. * return null if there is no renderer.
  347. */
  348. protected Rectangle getNodeDimensions(Object value, int row, int depth,
  349. boolean expanded,
  350. Rectangle placeIn) {
  351. NodeDimensions nd = getNodeDimensions();
  352. if(nd != null) {
  353. return nd.getNodeDimensions(value, row, depth, expanded, placeIn);
  354. }
  355. return null;
  356. }
  357. /**
  358. * Returns true if the height of each row is a fixed size.
  359. */
  360. protected boolean isFixedRowHeight() {
  361. return (rowHeight > 0);
  362. }
  363. /**
  364. * Used by AbstractLayoutCache to determing the size and x origin
  365. * of a particular node.
  366. */
  367. static public abstract class NodeDimensions {
  368. /**
  369. * Returns, by reference in bounds, the size and x origin to
  370. * place value at. The receiver is responsible for determing
  371. * the Y location. If bounds is null, a newly created
  372. * Rectangle should be returned, otherwise the value should be
  373. * placed in bounds and returned.
  374. */
  375. public abstract Rectangle getNodeDimensions(Object value, int row,
  376. int depth,
  377. boolean expanded,
  378. Rectangle bounds);
  379. }
  380. }