1. /*
  2. * @(#)JTree.java 1.111 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;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import java.beans.*;
  11. import java.io.*;
  12. import java.util.*;
  13. import javax.swing.event.*;
  14. import javax.swing.plaf.TreeUI;
  15. import javax.swing.tree.*;
  16. import javax.accessibility.*;
  17. /**
  18. * A control that displays a set of hierarchical data as an outline.
  19. * A specific node can be identified either by a TreePath (an object
  20. * that encapsulates a node and all of its ancestors), or by its
  21. * display row, where each row in the display area displays one node.
  22. * <p>
  23. * An <i>expanded</i> node is one displays its children. A <i>collapsed</i>
  24. * node is one which hides them. A <i>hidden</i> node is one which is
  25. * under a collapsed parent. A <i>viewable</i> node is under a collapsed
  26. * parent, but may or may not be displayed. A <i>displayed</i> node
  27. * is both viewable and in the display area, where it can be seen.
  28. * <p>
  29. * These JTree methods use "visible" to mean "displayed":<ul>
  30. * <li><code>isRootVisible()</code>
  31. * <li><code>setRootVisible()</code>
  32. * <li><code>scrollPathToVisible()</code>
  33. * <li><code>scrollRowToVisible()</code>
  34. * <li><code>getVisibleRowCount()</code>
  35. * <li><code>setVisibleRowCount()</code>
  36. * </ul>
  37. * <p>
  38. * These JTree methods use "visible" to mean "viewable" (under an
  39. * expanded parent):<ul>
  40. * <li><code>isVisible()</code>
  41. * <li><code>makeVisible()</code>
  42. * </ul>
  43. * <p>
  44. * If you are interested in knowing when the selection changes implement
  45. * the TreeSelectionListener interface and add the instance using the
  46. * method addTreeSelectionListener. valueChanged will be invoked when the
  47. * selection changes, that is if the user clicks twice on the same
  48. * node valueChanged will only be invoked once.
  49. * <p>
  50. * If you are interested in knowing either double clicks events or when
  51. * a user clicks on a node, regardless of whether or not it was selected
  52. * it is recommended you do the following:
  53. * <pre>
  54. * final JTree tree = ...;
  55. *
  56. * MouseListener ml = new MouseAdapter() {
  57. * public void <b>mouseClicked</b>(MouseEvent e) {
  58. * int selRow = tree.getRowForLocation(e.getX(), e.getY());
  59. * TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
  60. * if(selRow != -1) {
  61. * if(e.getClickCount() == 1) {
  62. * mySingleClick(selRow, selPath);
  63. * }
  64. * else if(e.getClickCount() == 2) {
  65. * myDoubleClick(selRow, selPath);
  66. * }
  67. * }
  68. * }
  69. * };
  70. * tree.addMouseListener(ml);
  71. * </pre>
  72. * NOTE: This example obtains both the path and row, but you only need to
  73. * get the one you're interested in.
  74. * <p>
  75. * To use JTree to display compound nodes (for example, nodes containing both
  76. * a graphic icon and text), subclass {@link TreeCellRenderer} and use
  77. * {@link #setTreeCellRenderer} to tell the tree to use it. To edit such nodes,
  78. * subclass {@link TreeCellEditor} and use {@link #setTreeCellEditor}.
  79. * <p>
  80. * Like all JComponent classes, you can use {@link JComponent#registerKeyboardAction}
  81. * to associate an {@link Action} object with a {@link KeyStroke} and execute the
  82. * action under specified conditions.
  83. * <p>
  84. * See <a href="http://java.sun.com/docs/books/tutorial/ui/swing/tree.html">How to Use Trees</a>
  85. * in <a href="http://java.sun.com/Series/Tutorial/index.html"><em>The Java Tutorial</em></a>
  86. * for further documentation.
  87. * <p>
  88. * For the keyboard keys used by this component in the standard Look and
  89. * Feel (L&F) renditions, see the
  90. * <a href="doc-files/Key-Index.html#JTree">JTree</a> key assignments.
  91. * <p>
  92. * <strong>Warning:</strong>
  93. * Serialized objects of this class will not be compatible with
  94. * future Swing releases. The current serialization support is appropriate
  95. * for short term storage or RMI between applications running the same
  96. * version of Swing. A future release of Swing will provide support for
  97. * long term persistence.
  98. *
  99. * @beaninfo
  100. * attribute: isContainer false
  101. *
  102. * @version 1.78 05/11/98
  103. * @author Rob Davis
  104. * @author Ray Ryan
  105. * @author Scott Violet
  106. */
  107. public class JTree extends JComponent implements Scrollable, Accessible
  108. {
  109. /**
  110. * @see #getUIClassID
  111. * @see #readObject
  112. */
  113. private static final String uiClassID = "TreeUI";
  114. /**
  115. * The model that defines the tree displayed by this object.
  116. */
  117. transient protected TreeModel treeModel;
  118. /**
  119. * Models the set of selected nodes in this tree.
  120. */
  121. transient protected TreeSelectionModel selectionModel;
  122. /**
  123. * True if the root node is displayed, false if its children are
  124. * the highest visible nodes.
  125. */
  126. protected boolean rootVisible;
  127. /**
  128. * The cell used to draw nodes. If null, the UI uses a default
  129. * cellRenderer.
  130. */
  131. transient protected TreeCellRenderer cellRenderer;
  132. /**
  133. * Height to use for each display row. If this is <= 0 the renderer
  134. * determines the height for each row.
  135. */
  136. protected int rowHeight;
  137. /**
  138. * Maps from TreePath to Boolean indicating whether or not the
  139. * particular path is expanded. This ONLY indicates whether a
  140. * given path is expanded, and NOT if it is visible or not. That
  141. * information must be determined by visiting all the parent
  142. * paths and seeing if they are visible.
  143. */
  144. transient private Hashtable expandedState;
  145. /**
  146. * True if handles are displayed at the topmost level of the tree.
  147. * <p>
  148. * A handle is a small icon that displays adjacent to the node which
  149. * allows the user to click once to expand or collapse the node. A
  150. * common interface shows a plus sign (+) for a node which can be
  151. * expanded and a minus sign (-) for a node which can be collapsed.
  152. * Handles are always shown for nodes below the topmost level.
  153. * <p>
  154. * If the <code>rootVisible</code> setting specifies that the root
  155. * node is to be displayed, then that is the only node at the topmost
  156. * level. If the root node is not displayed, then all of its
  157. * children are at the topmost level of the tree. Handles are
  158. * always displayed for nodes other than the topmost.
  159. * <p>
  160. * If the root node isn't visible, it is generally a good to make
  161. * this value true. Otherwise, the tree looks exactly like a list,
  162. * and users may not know that the "list entries" are actually
  163. * tree nodes.
  164. *
  165. * @see #rootVisible
  166. */
  167. protected boolean showsRootHandles;
  168. /**
  169. * Creates a new event and passed it off the selectionListeners.
  170. */
  171. protected transient TreeSelectionRedirector selectionRedirector;
  172. /**
  173. * Editor for the entries. Default is null (tree is not editable).
  174. */
  175. transient protected TreeCellEditor cellEditor;
  176. /**
  177. * Is the tree editable? Default is false.
  178. */
  179. protected boolean editable;
  180. /**
  181. * Is this tree a large model? This is a code-optimization setting.
  182. * A large model can be used when the cell height is the same for all
  183. * nodes. The UI will then cache very little information and instead
  184. * continually message the model. Without a large model the UI caches
  185. * most of the information, resulting in fewer method calls to the model.
  186. * <p>
  187. * This value is only a suggestion to the UI. Not all UIs will
  188. * take advantage of it. Default value is false.
  189. */
  190. protected boolean largeModel;
  191. /**
  192. * Number of rows to make visible at one time. This value is used for
  193. * the Scrollable interface. It determines the preferred size of the
  194. * display area.
  195. */
  196. protected int visibleRowCount;
  197. /**
  198. * If true, when editing is to be stopped by way of selection changing,
  199. * data in tree changing or other means stopCellEditing is invoked, and
  200. * changes are saved. If false, cancelCellEditing is invoked, and changes
  201. * are discarded. Default is false.
  202. */
  203. protected boolean invokesStopCellEditing;
  204. /**
  205. * If true, when a node is expanded, as many of the descendants are
  206. * scrolled to be visible.
  207. */
  208. protected boolean scrollsOnExpand;
  209. /**
  210. * Number of mouse clicks before a node is expanded.
  211. */
  212. protected int toggleClickCount;
  213. /**
  214. * Updates the expandedState.
  215. */
  216. transient protected TreeModelListener treeModelListener;
  217. /**
  218. * Used when setExpandedState is invoked, will be a Stack of Stacks.
  219. */
  220. transient private Stack expandedStack;
  221. /**
  222. * Max number of stacks to keep around.
  223. */
  224. private static int TEMP_STACK_SIZE = 11;
  225. //
  226. // Bound propery names
  227. //
  228. /** Bound property name for cellRenderer. */
  229. public final static String CELL_RENDERER_PROPERTY = "cellRenderer";
  230. /** Bound property name for treeModel. */
  231. public final static String TREE_MODEL_PROPERTY = "treeModel";
  232. /** Bound property name for rootVisible. */
  233. public final static String ROOT_VISIBLE_PROPERTY = "rootVisible";
  234. /** Bound property name for showsRootHandles. */
  235. public final static String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
  236. /** Bound property name for rowHeight. */
  237. public final static String ROW_HEIGHT_PROPERTY = "rowHeight";
  238. /** Bound property name for cellEditor. */
  239. public final static String CELL_EDITOR_PROPERTY = "cellEditor";
  240. /** Bound property name for editable. */
  241. public final static String EDITABLE_PROPERTY = "editable";
  242. /** Bound property name for largeModel. */
  243. public final static String LARGE_MODEL_PROPERTY = "largeModel";
  244. /** Bound property name for selectionModel. */
  245. public final static String SELECTION_MODEL_PROPERTY = "selectionModel";
  246. /** Bound property name for visibleRowCount. */
  247. public final static String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
  248. /** Bound property name for messagesStopCellEditing. */
  249. public final static String INVOKES_STOP_CELL_EDITING_PROPERTY = "messagesStopCellEditing";
  250. /** Bound property name for scrollsOnExpand. */
  251. public final static String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
  252. /** Bound property name for toggleClickCount. */
  253. //public final static String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
  254. /**
  255. * Creates and returns a sample TreeModel. Used primarily for beanbuilders.
  256. * to show something interesting.
  257. *
  258. * @return the default TreeModel
  259. */
  260. protected static TreeModel getDefaultTreeModel() {
  261. DefaultMutableTreeNode root = new DefaultMutableTreeNode("JTree");
  262. DefaultMutableTreeNode parent;
  263. parent = new DefaultMutableTreeNode("colors");
  264. root.add(parent);
  265. parent.add(new DefaultMutableTreeNode("blue"));
  266. parent.add(new DefaultMutableTreeNode("violet"));
  267. parent.add(new DefaultMutableTreeNode("red"));
  268. parent.add(new DefaultMutableTreeNode("yellow"));
  269. parent = new DefaultMutableTreeNode("sports");
  270. root.add(parent);
  271. parent.add(new DefaultMutableTreeNode("basketball"));
  272. parent.add(new DefaultMutableTreeNode("soccer"));
  273. parent.add(new DefaultMutableTreeNode("football"));
  274. parent.add(new DefaultMutableTreeNode("hockey"));
  275. parent = new DefaultMutableTreeNode("food");
  276. root.add(parent);
  277. parent.add(new DefaultMutableTreeNode("hot dogs"));
  278. parent.add(new DefaultMutableTreeNode("pizza"));
  279. parent.add(new DefaultMutableTreeNode("ravioli"));
  280. parent.add(new DefaultMutableTreeNode("bananas"));
  281. return new DefaultTreeModel(root);
  282. }
  283. /**
  284. * Returns a TreeModel wrapping the specified object. If the object
  285. * is:<ul>
  286. * <li>an array of Objects,
  287. * <li>a Hashtable, or
  288. * <li>a Vector
  289. * </ul>then a new root node is created with each of the incoming
  290. * objects as children. Otherwise, a new root is created with the
  291. * specified object as its value.
  292. *
  293. * @param value the Object used as the foundation for the TreeModel
  294. * @return a TreeModel wrapping the specified object
  295. */
  296. protected static TreeModel createTreeModel(Object value) {
  297. DefaultMutableTreeNode root;
  298. if((value instanceof Object[]) || (value instanceof Hashtable) ||
  299. (value instanceof Vector)) {
  300. root = new DefaultMutableTreeNode("root");
  301. DynamicUtilTreeNode.createChildren(root, value);
  302. }
  303. else {
  304. root = new DynamicUtilTreeNode("root", value);
  305. }
  306. return new DefaultTreeModel(root, false);
  307. }
  308. /**
  309. * Returns a JTree with a sample model.
  310. * The default model used by the tree defines a leaf node as any node without
  311. * children.
  312. *
  313. * @return a JTree with the default model, which defines a leaf node
  314. * as any node without children.
  315. * @see DefaultTreeModel#asksAllowsChildren
  316. */
  317. public JTree() {
  318. this(getDefaultTreeModel());
  319. }
  320. /**
  321. * Returns a JTree with each element of the specified array as the
  322. * child of a new root node which is not displayed.
  323. * By default, the tree defines a leaf node as any node without
  324. * children.
  325. *
  326. * @param value an array of Objects
  327. * @return a JTree with the contents of the array as children of
  328. * the root node
  329. * @see DefaultTreeModel#asksAllowsChildren
  330. */
  331. public JTree(Object[] value) {
  332. this(createTreeModel(value));
  333. this.setRootVisible(false);
  334. this.setShowsRootHandles(true);
  335. }
  336. /**
  337. * Returns a JTree with each element of the specified Vector as the
  338. * child of a new root node which is not displayed. By default, the
  339. * tree defines a leaf node as any node without children.
  340. *
  341. * @param value a Vector
  342. * @return a JTree with the contents of the Vector as children of
  343. * the root node
  344. * @see DefaultTreeModel#asksAllowsChildren
  345. */
  346. public JTree(Vector value) {
  347. this(createTreeModel(value));
  348. this.setRootVisible(false);
  349. this.setShowsRootHandles(true);
  350. }
  351. /**
  352. * Returns a JTree created from a Hashtable which does not display
  353. * the root. Each value-half of the key/value pairs in the HashTable
  354. * becomes a child of the new root node. By default, the tree defines
  355. * a leaf node as any node without children.
  356. *
  357. * @param value a Hashtable
  358. * @return a JTree with the contents of the Hashtable as children of
  359. * the root node
  360. * @see DefaultTreeModel#asksAllowsChildren
  361. */
  362. public JTree(Hashtable value) {
  363. this(createTreeModel(value));
  364. this.setRootVisible(false);
  365. this.setShowsRootHandles(true);
  366. }
  367. /**
  368. * Returns a JTree with the specified TreeNode as its root, which
  369. * displays the root node. By default, the tree defines a leaf node as any node
  370. * without children.
  371. *
  372. * @param root a TreeNode object
  373. * @return a JTree with the specified root node
  374. * @see DefaultTreeModel#asksAllowsChildren
  375. */
  376. public JTree(TreeNode root) {
  377. this(root, false);
  378. }
  379. /**
  380. * Returns a JTree with the specified TreeNode as its root, which
  381. * displays the root node and which decides whether a node is a
  382. * leaf node in the specified manner.
  383. *
  384. * @param root a TreeNode object
  385. * @param asksAllowsChildren if false, any node without children is a
  386. * leaf node. If true, only nodes that do not allow
  387. * children are leaf nodes.
  388. * @return a JTree with the specified root node
  389. * @see DefaultTreeModel#asksAllowsChildren
  390. */
  391. public JTree(TreeNode root, boolean asksAllowsChildren) {
  392. this(new DefaultTreeModel(root, asksAllowsChildren));
  393. }
  394. /**
  395. * Returns an instance of JTree which displays the root node
  396. * -- the tree is created using the specified data model.
  397. *
  398. * @param newModel the TreeModel to use as the data model
  399. * @return a JTree based on the TreeModel
  400. */
  401. public JTree(TreeModel newModel) {
  402. super();
  403. expandedStack = new Stack();
  404. toggleClickCount = 2;
  405. expandedState = new Hashtable();
  406. setLayout(null);
  407. rowHeight = 16;
  408. visibleRowCount = 20;
  409. rootVisible = true;
  410. selectionModel = new DefaultTreeSelectionModel();
  411. cellRenderer = null;
  412. scrollsOnExpand = true;
  413. setOpaque(true);
  414. updateUI();
  415. setModel(newModel);
  416. }
  417. /**
  418. * Returns the L&F object that renders this component.
  419. *
  420. * @return the TreeUI object that renders this component
  421. */
  422. public TreeUI getUI() {
  423. return (TreeUI)ui;
  424. }
  425. /**
  426. * Sets the L&F object that renders this component.
  427. *
  428. * @param ui the TreeUI L&F object
  429. * @see UIDefaults#getUI
  430. */
  431. public void setUI(TreeUI ui) {
  432. if ((TreeUI)this.ui != ui) {
  433. super.setUI(ui);
  434. repaint();
  435. }
  436. }
  437. /**
  438. * Notification from the UIManager that the L&F has changed.
  439. * Replaces the current UI object with the latest version from the
  440. * UIManager.
  441. *
  442. * @see JComponent#updateUI
  443. */
  444. public void updateUI() {
  445. setUI((TreeUI)UIManager.getUI(this));
  446. invalidate();
  447. }
  448. /**
  449. * Returns the name of the L&F class that renders this component.
  450. *
  451. * @return "TreeUI"
  452. * @see JComponent#getUIClassID
  453. * @see UIDefaults#getUI
  454. */
  455. public String getUIClassID() {
  456. return uiClassID;
  457. }
  458. /**
  459. * Returns the current TreeCellRenderer that is rendering each cell.
  460. *
  461. * @return the TreeCellRenderer that is rendering each cell
  462. */
  463. public TreeCellRenderer getCellRenderer() {
  464. return cellRenderer;
  465. }
  466. /**
  467. * Sets the TreeCellRenderer that will be used to draw each cell.
  468. *
  469. * @param x the TreeCellRenderer that is to render each cell
  470. * @beaninfo
  471. * bound: true
  472. * description: The TreeCellRenderer that will be used to draw
  473. * each cell.
  474. */
  475. public void setCellRenderer(TreeCellRenderer x) {
  476. TreeCellRenderer oldValue = cellRenderer;
  477. cellRenderer = x;
  478. firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, cellRenderer);
  479. invalidate();
  480. }
  481. /**
  482. * Determines whether the tree is editable. Fires a property
  483. * change event if the new setting is different from the existing
  484. * setting.
  485. *
  486. * @param flag a boolean value, true if the tree is editable
  487. * @beaninfo
  488. * bound: true
  489. * description: Whether the tree is editable.
  490. */
  491. public void setEditable(boolean flag) {
  492. boolean oldValue = this.editable;
  493. this.editable = flag;
  494. firePropertyChange(EDITABLE_PROPERTY, oldValue, flag);
  495. if (accessibleContext != null) {
  496. accessibleContext.firePropertyChange(
  497. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  498. (oldValue ? AccessibleState.EDITABLE : null),
  499. (flag ? AccessibleState.EDITABLE : null));
  500. }
  501. }
  502. /**
  503. * Returns true if the tree is editable.
  504. *
  505. * @return true if the tree is editable.
  506. */
  507. public boolean isEditable() {
  508. return editable;
  509. }
  510. /**
  511. * Sets the cell editor. A null value implies that the
  512. * tree cannot be edited. If this represents a change in the
  513. * cellEditor, the propertyChange method is invoked on all
  514. * listeners.
  515. *
  516. * @param cellEditor the TreeCellEditor to use
  517. * @beaninfo
  518. * bound: true
  519. * description: The cell editor. A null value implies the tree
  520. * cannot be edited.
  521. */
  522. public void setCellEditor(TreeCellEditor cellEditor) {
  523. TreeCellEditor oldEditor = this.cellEditor;
  524. this.cellEditor = cellEditor;
  525. firePropertyChange(CELL_EDITOR_PROPERTY, oldEditor, cellEditor);
  526. invalidate();
  527. }
  528. /**
  529. * Returns the editor used to edit entries in the tree.
  530. *
  531. * @return the TreeCellEditor in use, or null if the tree cannot
  532. * be edited
  533. */
  534. public TreeCellEditor getCellEditor() {
  535. return cellEditor;
  536. }
  537. /**
  538. * Returns the TreeModel that is providing the data.
  539. *
  540. * @return the TreeModel that is providing the data
  541. */
  542. public TreeModel getModel() {
  543. return treeModel;
  544. }
  545. /**
  546. * Sets the TreeModel that will provide the data.
  547. *
  548. * @param newModel the TreeModel that is to provide the data
  549. * @beaninfo
  550. * bound: true
  551. * description: The TreeModel that will provide the data.
  552. */
  553. public void setModel(TreeModel newModel) {
  554. TreeModel oldModel = treeModel;
  555. if(treeModel != null && treeModelListener != null)
  556. treeModel.removeTreeModelListener(treeModelListener);
  557. if (accessibleContext != null) {
  558. if (treeModel != null) {
  559. treeModel.removeTreeModelListener((TreeModelListener)accessibleContext);
  560. }
  561. if (newModel != null) {
  562. newModel.addTreeModelListener((TreeModelListener)accessibleContext);
  563. }
  564. }
  565. treeModel = newModel;
  566. clearToggledPaths();
  567. if(treeModel != null) {
  568. if(treeModelListener == null)
  569. treeModelListener = createTreeModelListener();
  570. if(treeModelListener != null)
  571. treeModel.addTreeModelListener(treeModelListener);
  572. // Mark the root as expanded, if it isn't a leaf.
  573. if(!treeModel.isLeaf(treeModel.getRoot()))
  574. expandedState.put(new TreePath(treeModel.getRoot()),
  575. Boolean.TRUE);
  576. }
  577. firePropertyChange(TREE_MODEL_PROPERTY, oldModel, treeModel);
  578. invalidate();
  579. }
  580. /**
  581. * Returns true if the root node of the tree is displayed.
  582. *
  583. * @return true if the root node of the tree is displayed
  584. * @see #rootVisible
  585. */
  586. public boolean isRootVisible() {
  587. return rootVisible;
  588. }
  589. /**
  590. * Determines whether or not the root node from
  591. * the TreeModel is visible.
  592. *
  593. * @param rootVisible true if the root node of the tree is to be displayed
  594. * @see #rootVisible
  595. * @beaninfo
  596. * bound: true
  597. * description: Whether or not the root node
  598. * from the TreeModel is visible.
  599. */
  600. public void setRootVisible(boolean rootVisible) {
  601. boolean oldValue = this.rootVisible;
  602. this.rootVisible = rootVisible;
  603. firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, this.rootVisible);
  604. if (accessibleContext != null) {
  605. ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
  606. }
  607. }
  608. /**
  609. * Determines whether the node handles are to be displayed.
  610. *
  611. * @param newValue true if root handles are to be displayed
  612. * @see #showsRootHandles
  613. * @beaninfo
  614. * bound: true
  615. * description: Whether the node handles are to be
  616. * displayed.
  617. */
  618. public void setShowsRootHandles(boolean newValue) {
  619. boolean oldValue = showsRootHandles;
  620. TreeModel model = getModel();
  621. showsRootHandles = newValue;
  622. firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue,
  623. showsRootHandles);
  624. if (accessibleContext != null) {
  625. ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
  626. }
  627. // Make SURE the root is expanded
  628. if(model != null) {
  629. expandPath(new TreePath(model.getRoot()));
  630. }
  631. invalidate();
  632. }
  633. /**
  634. * Returns true if handles for the root nodes are displayed.
  635. *
  636. * @return true if root handles are displayed
  637. * @see #showsRootHandles
  638. */
  639. public boolean getShowsRootHandles()
  640. {
  641. return showsRootHandles;
  642. }
  643. /**
  644. * Sets the height of each cell. If the specified value
  645. * is less than or equal to zero the current cell renderer is
  646. * queried for each row's height.
  647. *
  648. * @param rowHeight the height of each cell, in pixels
  649. * @beaninfo
  650. * bound: true
  651. * description: The height of each cell.
  652. */
  653. public void setRowHeight(int rowHeight)
  654. {
  655. int oldValue = this.rowHeight;
  656. this.rowHeight = rowHeight;
  657. firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, this.rowHeight);
  658. invalidate();
  659. }
  660. /**
  661. * Returns the height of each row. If the returned value is less than
  662. * or equal to 0 the height for each row is determined by the
  663. * renderer.
  664. *
  665. * @param the height of each cell, in pixels. Zero or negative if the
  666. * height of each row is determined by the tree cell renderer
  667. */
  668. public int getRowHeight()
  669. {
  670. return rowHeight;
  671. }
  672. /**
  673. * Returns true if the height of each display row is a fixed size.
  674. *
  675. * @return true if the height of each row is a fixed size
  676. */
  677. public boolean isFixedRowHeight()
  678. {
  679. return (rowHeight > 0);
  680. }
  681. /**
  682. * Specifies whether the UI should use a large model.
  683. * (Not all UIs will implement this.) Fires a property change
  684. * for the LARGE_MODEL_PROPERTY.
  685. *
  686. * @param newValue true to suggest a large model to the UI
  687. * @see #largeModel
  688. * @beaninfo
  689. * bound: true
  690. * description: Whether the UI should use a
  691. * large model.
  692. */
  693. public void setLargeModel(boolean newValue) {
  694. boolean oldValue = largeModel;
  695. largeModel = newValue;
  696. firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, newValue);
  697. }
  698. /**
  699. * Returns true if the tree is configured for a large model.
  700. *
  701. * @return true if a large model is suggested
  702. * @see #largeModel
  703. */
  704. public boolean isLargeModel() {
  705. return largeModel;
  706. }
  707. /**
  708. * Determines what happens when editing is interrupted by selecting
  709. * another node in the tree, a change in the tree's data, or by some
  710. * other means. Setting this property to <code>true</code> causes the
  711. * changes to be automatically saved when editing is interrupted.
  712. * <p>
  713. * Fires a property change for the INVOKES_STOP_CELL_EDITING_PROPERTY.
  714. *
  715. * @param newValue true means that stopCellEditing is invoked when
  716. * editing is interruped, and data is saved. False means that
  717. * cancelCellEditing is invoked, and changes are lost.
  718. * @beaninfo
  719. * bound: true
  720. * description: Determines what happens when editing is interrupted,
  721. * selecting another node in the tree, a change in the
  722. * tree's data, or some other means.
  723. */
  724. public void setInvokesStopCellEditing(boolean newValue) {
  725. boolean oldValue = invokesStopCellEditing;
  726. invokesStopCellEditing = newValue;
  727. firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, oldValue,
  728. newValue);
  729. }
  730. /**
  731. * Returns the indicator that tells what happens when editing is
  732. * interrupted.
  733. *
  734. * @return the indicator that tells what happens when editing is
  735. * interrupted
  736. * @see #setInvokesStopCellEditing
  737. */
  738. public boolean getInvokesStopCellEditing() {
  739. return invokesStopCellEditing;
  740. }
  741. /**
  742. * Determines whether or not when a node is expanded, as many of
  743. * the descendants are scrolled to be inside the viewport as
  744. * possible. The default is true.
  745. */
  746. public void setScrollsOnExpand(boolean newValue) {
  747. boolean oldValue = scrollsOnExpand;
  748. scrollsOnExpand = newValue;
  749. firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue,
  750. newValue);
  751. }
  752. /**
  753. * Returns true if the tree scrolls to show previously hidden children.
  754. * @return true if when a node is expanded as many of the descendants
  755. * as possible are scrolled to be visible.
  756. */
  757. public boolean getScrollsOnExpand() {
  758. return scrollsOnExpand;
  759. }
  760. // NOTE: This property will be enabled in a future release.
  761. /**
  762. * Sets the number of mouse clicks before a node will expand or close.
  763. * The default is two.
  764. */
  765. /*
  766. public void setToggleClickCount(int clickCount) {
  767. int oldCount = toggleClickCount;
  768. toggleClickCount = clickCount;
  769. firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldCount,
  770. clickCount);
  771. }
  772. */
  773. /**
  774. * Returns the number of mouse clicks needed to expand or close a node.
  775. * @return number of mouse clicks before node is expanded.
  776. */
  777. /*
  778. public int getToggleClickCount() {
  779. return toggleClickCount;
  780. }
  781. */
  782. /**
  783. * Returns <code>isEditable</code>. This is invoked from the UI before
  784. * editing begins to insure that the given path can be edited. This
  785. * is provided as an entry point for subclassers to add filtered
  786. * editing without having to resort to creating a new editor.
  787. *
  788. * @return true if every parent node and the node itself is editabled
  789. * @see #isEditable
  790. */
  791. public boolean isPathEditable(TreePath path) {
  792. return isEditable();
  793. }
  794. /**
  795. * Overrides JComponent's getToolTipText method in order to allow
  796. * renderer's tips to be used if it has text set.
  797. * <p>
  798. * NOTE: For JTree to properly display tooltips of its renderers
  799. * JTree must be a registered component with the ToolTipManager.
  800. * This can be done by invoking
  801. * <code>ToolTipManager.sharedInstance().registerComponent(tree)</code>.
  802. * This is not done automaticly!
  803. *
  804. * @param event the MouseEvent that initiated the ToolTip display
  805. */
  806. public String getToolTipText(MouseEvent event) {
  807. if(event != null) {
  808. Point p = event.getPoint();
  809. int selRow = getRowForLocation(p.x, p.y);
  810. TreeCellRenderer r = getCellRenderer();
  811. if(selRow != -1 && r != null) {
  812. TreePath path = getPathForRow(selRow);
  813. Object lastPath = path.getLastPathComponent();
  814. Component rComponent = r.getTreeCellRendererComponent
  815. (this, lastPath, isRowSelected(selRow),
  816. isExpanded(selRow), getModel().isLeaf(lastPath), selRow,
  817. true);
  818. if(rComponent instanceof JComponent) {
  819. MouseEvent newEvent;
  820. Rectangle pathBounds = getPathBounds(path);
  821. p.translate(-pathBounds.x, -pathBounds.y);
  822. newEvent = new MouseEvent(rComponent, event.getID(),
  823. event.getWhen(),
  824. event.getModifiers(),
  825. p.x, p.y, event.getClickCount(),
  826. event.isPopupTrigger());
  827. return ((JComponent)rComponent).getToolTipText(newEvent);
  828. }
  829. }
  830. }
  831. return null;
  832. }
  833. /**
  834. * Called by the renderers to convert the specified value to
  835. * text. This implementation returns value.toString(), ignoring
  836. * all other arguments. To control the conversion, subclass this
  837. * method and use any of the arguments you need.
  838. *
  839. * @param value the Object to convert to text
  840. * @param selected true if the node is selected
  841. * @param expanded true if the node is expanded
  842. * @param leaf true if the node is a leaf node
  843. * @param row an int specifying the node's display row, where 0 is
  844. * the first row in the display
  845. * @param hasFocus true if the node has the focus
  846. * @return the String representation of the node's value
  847. */
  848. public String convertValueToText(Object value, boolean selected,
  849. boolean expanded, boolean leaf, int row,
  850. boolean hasFocus) {
  851. if(value != null)
  852. return value.toString();
  853. return "";
  854. }
  855. //
  856. // The following are convenience methods that get forwarded to the
  857. // current TreeUI.
  858. //
  859. /**
  860. * Returns the number of rows that are currently being displayed.
  861. *
  862. * @return the number of rows that are being displayed
  863. */
  864. public int getRowCount() {
  865. TreeUI tree = getUI();
  866. if(tree != null)
  867. return tree.getRowCount(this);
  868. return 0;
  869. }
  870. /**
  871. * Selects the node identified by the specified path. If any
  872. * component of the path is hidden (under a collapsed node), it is
  873. * exposed (made viewable).
  874. *
  875. * @param path the TreePath specifying the node to select
  876. */
  877. public void setSelectionPath(TreePath path) {
  878. makeVisible(path);
  879. getSelectionModel().setSelectionPath(path);
  880. }
  881. /**
  882. * Selects the nodes identified by the specified array of paths.
  883. * If any component in any of the paths is hidden (under a collapsed
  884. * node), it is exposed (made viewable).
  885. *
  886. * @param paths an array of TreePath objects that specifies the nodes
  887. * to select
  888. */
  889. public void setSelectionPaths(TreePath[] paths) {
  890. if(paths != null) {
  891. for(int counter = paths.length - 1; counter >= 0; counter--)
  892. makeVisible(paths[counter]);
  893. }
  894. getSelectionModel().setSelectionPaths(paths);
  895. }
  896. /**
  897. * Selects the node at the specified row in the display.
  898. *
  899. * @param row the row to select, where 0 is the first row in
  900. * the display
  901. */
  902. public void setSelectionRow(int row) {
  903. int[] rows = { row };
  904. setSelectionRows(rows);
  905. }
  906. /**
  907. * Selects the nodes corresponding to each of the specified rows
  908. * in the display. If a particular element of <code>rows</code> is
  909. * < 0 or >= getRowCount, it will be ignored. If none of the elements
  910. * in <code>rows</code> are valid rows, the selection will
  911. * be cleared. That is it will be as if <code>clearSelection</code>
  912. * was invoked.
  913. *
  914. * @param rows an array of ints specifying the rows to select,
  915. * where 0 indicates the first row in the display
  916. */
  917. public void setSelectionRows(int[] rows) {
  918. TreeUI ui = getUI();
  919. if(ui != null && rows != null) {
  920. int numRows = rows.length;
  921. TreePath[] paths = new TreePath[numRows];
  922. for(int counter = 0; counter < numRows; counter++)
  923. paths[counter] = ui.getPathForRow(this, rows[counter]);
  924. setSelectionPaths(paths);
  925. }
  926. }
  927. /**
  928. * Adds the node identified by the specified TreePath to the current
  929. * selection. If any component of the path isn't viewable, it is
  930. * made viewable.
  931. *
  932. * @param path the TreePath to add
  933. */
  934. public void addSelectionPath(TreePath path) {
  935. makeVisible(path);
  936. getSelectionModel().addSelectionPath(path);
  937. }
  938. /**
  939. * Adds each path in the array of paths to the current selection. If
  940. * any component of any of the paths isn't viewable, it is
  941. * made viewable.
  942. *
  943. * @param paths an array of TreePath objects that specifies the nodes
  944. * to add
  945. */
  946. public void addSelectionPaths(TreePath[] paths) {
  947. if(paths != null) {
  948. for(int counter = paths.length - 1; counter >= 0; counter--)
  949. makeVisible(paths[counter]);
  950. }
  951. getSelectionModel().addSelectionPaths(paths);
  952. }
  953. /**
  954. * Adds the path at the specified row to the current selection.
  955. *
  956. * @param row an int specifying the row of the node to add,
  957. * where 0 is the first row in the display
  958. */
  959. public void addSelectionRow(int row) {
  960. int[] rows = { row };
  961. addSelectionRows(rows);
  962. }
  963. /**
  964. * Adds the paths at each of the specified rows to the current selection.
  965. *
  966. * @param rows an array of ints specifying the rows to add,
  967. * where 0 indicates the first row in the display
  968. */
  969. public void addSelectionRows(int[] rows) {
  970. TreeUI ui = getUI();
  971. if(ui != null && rows != null) {
  972. int numRows = rows.length;
  973. TreePath[] paths = new TreePath[numRows];
  974. for(int counter = 0; counter < numRows; counter++)
  975. paths[counter] = ui.getPathForRow(this, rows[counter]);
  976. addSelectionPaths(paths);
  977. }
  978. }
  979. /**
  980. * Returns the last path component in the first node of the current
  981. * selection.
  982. *
  983. * @return the last Object in the first selected node's TreePath,
  984. * or null if nothing is selected
  985. * @see TreePath#getLastPathComponent
  986. */
  987. public Object getLastSelectedPathComponent() {
  988. TreePath selPath = getSelectionModel().getSelectionPath();
  989. if(selPath != null)
  990. return selPath.getLastPathComponent();
  991. return null;
  992. }
  993. /**
  994. * Returns the path to the first selected node.
  995. *
  996. * @return the TreePath for the first selected node, or null if
  997. * nothing is currently selected
  998. */
  999. public TreePath getSelectionPath() {
  1000. return getSelectionModel().getSelectionPath();
  1001. }
  1002. /**
  1003. * Returns the paths of all selected values.
  1004. *
  1005. * @return an array of TreePath objects indicating the selected
  1006. * nodes, or null if nothing is currently selected.
  1007. */
  1008. public TreePath[] getSelectionPaths() {
  1009. return getSelectionModel().getSelectionPaths();
  1010. }
  1011. /**
  1012. * Returns all of the currently selected rows. This method is simply
  1013. * forwarded to the TreeSelectionModel. If nothing is selected null
  1014. * or an empty array with be returned, based on the TreeSelectionModel
  1015. * implementation.
  1016. *
  1017. * @return an array of ints that identifies all currently selected rows
  1018. * where 0 is the first row in the display
  1019. */
  1020. public int[] getSelectionRows() {
  1021. return getSelectionModel().getSelectionRows();
  1022. }
  1023. /**
  1024. * Returns the number of nodes selected.
  1025. *
  1026. * @return the number of nodes selected
  1027. */
  1028. public int getSelectionCount() {
  1029. return selectionModel.getSelectionCount();
  1030. }
  1031. /**
  1032. * Gets the first selected row.
  1033. *
  1034. * @return an int designating the first selected row, where 0 is the
  1035. * first row in the display
  1036. */
  1037. public int getMinSelectionRow() {
  1038. return getSelectionModel().getMinSelectionRow();
  1039. }
  1040. /**
  1041. * Gets the last selected row.
  1042. *
  1043. * @return an int designating the last selected row, where 0 is the
  1044. * first row in the display
  1045. */
  1046. public int getMaxSelectionRow() {
  1047. return getSelectionModel().getMaxSelectionRow();
  1048. }
  1049. /**
  1050. * Returns the row index of the last node added to the selection.
  1051. *
  1052. * @return an int giving the row index of the last node added to the
  1053. * selection, where 0 is the first row in the display
  1054. */
  1055. public int getLeadSelectionRow() {
  1056. return getSelectionModel().getLeadSelectionRow();
  1057. }
  1058. /**
  1059. * Returns the path of the last node added to the selection.
  1060. *
  1061. * @return the TreePath of the last node added to the selection.
  1062. */
  1063. public TreePath getLeadSelectionPath() {
  1064. return getSelectionModel().getLeadSelectionPath();
  1065. }
  1066. /**
  1067. * Returns true if the item identified by the path is currently selected.
  1068. *
  1069. * @param path a TreePath identifying a node
  1070. * @return true if the node is selected
  1071. */
  1072. public boolean isPathSelected(TreePath path) {
  1073. return getSelectionModel().isPathSelected(path);
  1074. }
  1075. /**
  1076. * Returns true if the node identitifed by row is selected.
  1077. *
  1078. * @param row an int specifying a display row, where 0 is the first
  1079. * row in the display
  1080. * @return true if the node is selected
  1081. */
  1082. public boolean isRowSelected(int row) {
  1083. return getSelectionModel().isRowSelected(row);
  1084. }
  1085. /**
  1086. * Returns an Enumeration of the descendants of <code>path</code> that
  1087. * are currently expanded. If <code>path</code> is not currently
  1088. * expanded, this will return null. If you expand/collapse nodes while
  1089. * iterating over the returned Enumeration this may not return all
  1090. * the expanded paths, or may return paths that are no longer expanded.
  1091. */
  1092. public Enumeration getExpandedDescendants(TreePath parent) {
  1093. if(!isExpanded(parent))
  1094. return null;
  1095. Enumeration toggledPaths = expandedState.keys();
  1096. Vector elements = new Vector();
  1097. TreePath path;
  1098. Object value;
  1099. if(toggledPaths != null) {
  1100. while(toggledPaths.hasMoreElements()) {
  1101. path = (TreePath)toggledPaths.nextElement();
  1102. value = expandedState.get(path);
  1103. // Add the path if it is expanded, a descendant of parent,
  1104. // and it is visible (all parents expanded). This is rather
  1105. // expensive!
  1106. if(value != null && ((Boolean)value).booleanValue() &&
  1107. parent.isDescendant(path) && isVisible(path)) {
  1108. elements.addElement(path);
  1109. }
  1110. }
  1111. }
  1112. return elements.elements();
  1113. }
  1114. /**
  1115. * Returns true if the node identified by the path has ever been
  1116. * expanded.
  1117. */
  1118. public boolean hasBeenExpanded(TreePath path) {
  1119. return (path != null && expandedState.get(path) != null);
  1120. }
  1121. /**
  1122. * Returns true if the node identified by the path is currently expanded,
  1123. *
  1124. * @param path the TreePath specifying the node to check
  1125. * @return false if any of the nodes in the node's path are collapsed,
  1126. * true if all nodes in the path are expanded
  1127. */
  1128. public boolean isExpanded(TreePath path) {
  1129. if(path == null)
  1130. return false;
  1131. // Is this node expanded?
  1132. Object value = expandedState.get(path);
  1133. if(value == null || !((Boolean)value).booleanValue())
  1134. return false;
  1135. // It is, make sure its parent is also expanded.
  1136. TreePath parentPath = path.getParentPath();
  1137. if(parentPath != null)
  1138. return isExpanded(parentPath);
  1139. return true;
  1140. }
  1141. /**
  1142. * Returns true if the node at the specified display row is currently
  1143. * expanded.
  1144. *
  1145. * @param row the row to check, where 0 is the first row in the
  1146. * display
  1147. * @return true if the node is currently expanded, otherwise false
  1148. */
  1149. public boolean isExpanded(int row) {
  1150. TreeUI tree = getUI();
  1151. if(tree != null) {
  1152. TreePath path = tree.getPathForRow(this, row);
  1153. if(path != null)
  1154. return isExpanded(path);
  1155. }
  1156. return false;
  1157. }
  1158. /**
  1159. * Returns true if the value identified by path is currently collapsed,
  1160. * this will return false if any of the values in path are currently
  1161. * not being displayed.
  1162. *
  1163. * @param path the TreePath to check
  1164. * @return true if any of the nodes in the node's path are collapsed,
  1165. * false if all nodes in the path are expanded
  1166. */
  1167. public boolean isCollapsed(TreePath path) {
  1168. return !isExpanded(path);
  1169. }
  1170. /**
  1171. * Returns true if the node at the specified display row is collapsed.
  1172. *
  1173. * @param row the row to check, where 0 is the first row in the
  1174. * display
  1175. * @return true if the node is currently collapsed, otherwise false
  1176. */
  1177. public boolean isCollapsed(int row) {
  1178. return !isExpanded(row);
  1179. }
  1180. /**
  1181. * Ensures that the node identified by path is currently viewable.
  1182. *
  1183. * @param path the TreePath to make visible
  1184. */
  1185. public void makeVisible(TreePath path) {
  1186. if(path != null) {
  1187. TreePath parentPath = path.getParentPath();
  1188. if(parentPath != null) {
  1189. expandPath(parentPath);
  1190. }
  1191. }
  1192. }
  1193. /**
  1194. * Returns true if the value identified by path is currently viewable,
  1195. * which means it is either the root or all of its parents are exapnded ,
  1196. * Otherwise, this method returns false.
  1197. *
  1198. * @return true if the node is viewable, otherwise false
  1199. */
  1200. public boolean isVisible(TreePath path) {
  1201. if(path != null) {
  1202. TreePath parentPath = path.getParentPath();
  1203. if(parentPath != null)
  1204. return isExpanded(parentPath);
  1205. // Root.
  1206. return true;
  1207. }
  1208. return false;
  1209. }
  1210. /**
  1211. * Returns the Rectangle that the specified node will be drawn
  1212. * into. Returns null if any component in the path is hidden
  1213. * (under a collapsed parent).
  1214. * <p>
  1215. * Note:<br>
  1216. * This method returns a valid rectangle, even if the specified
  1217. * node is not currently displayed.
  1218. *
  1219. * @param path the TreePath identifying the node
  1220. * @return the Rectangle the node is drawn in, or null
  1221. */
  1222. public Rectangle getPathBounds(TreePath path) {
  1223. TreeUI tree = getUI();
  1224. if(tree != null)
  1225. return tree.getPathBounds(this, path);
  1226. return null;
  1227. }
  1228. /**
  1229. * Returns the Rectangle that the node at the specified row is
  1230. * drawn in.
  1231. *
  1232. * @param row the row to be drawn, where 0 is the first row in the
  1233. * display
  1234. * @return the Rectangle the node is drawn in
  1235. */
  1236. public Rectangle getRowBounds(int row) {
  1237. TreePath path = getPathForRow(row);
  1238. return getPathBounds(getPathForRow(row));
  1239. }
  1240. /**
  1241. * Makes sure all the path components in path are expanded (except
  1242. * for the last path component) and scrolls so that the
  1243. * node identified by the path is displayed. Only works when this
  1244. * JTree is contained in a JSrollPane.
  1245. *
  1246. * @param path the TreePath identifying the node to bring into view
  1247. */
  1248. public void scrollPathToVisible(TreePath path) {
  1249. if(path != null) {
  1250. makeVisible(path);
  1251. Rectangle bounds = getPathBounds(path);
  1252. if(bounds != null) {
  1253. scrollRectToVisible(bounds);
  1254. if (accessibleContext != null) {
  1255. ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
  1256. }
  1257. }
  1258. }
  1259. }
  1260. /**
  1261. * Scrolls the item identified by row until it is displayed. The minimum
  1262. * of amount of scrolling necessary to bring the row into view
  1263. * is performed. Only works when this JTree is contained in a
  1264. * JSrollPane.
  1265. *
  1266. * @param row an int specifying the row to scroll, where 0 is the
  1267. * first row in the display
  1268. */
  1269. public void scrollRowToVisible(int row) {
  1270. scrollPathToVisible(getPathForRow(row));
  1271. }
  1272. /**
  1273. * Returns the path for the specified row.
  1274. * <!-->If row is not visible null is returned.<-->
  1275. *
  1276. * @param row an int specifying a row
  1277. * @return the TreePath to the specified node, null if
  1278. * row < 0 or row > getRowCount()
  1279. */
  1280. public TreePath getPathForRow(int row) {
  1281. TreeUI tree = getUI();
  1282. if(tree != null)
  1283. return tree.getPathForRow(this, row);
  1284. return null;
  1285. }
  1286. /**
  1287. * Returns the row that displays the node identified by the specified
  1288. * path.
  1289. *
  1290. * @param path the TreePath identifying a node
  1291. * @return an int specifying the display row, where 0 is the first
  1292. * row in the display, or -1 if any of the elements in path
  1293. * are hidden under a collapsed parent.
  1294. */
  1295. public int getRowForPath(TreePath path) {
  1296. TreeUI tree = getUI();
  1297. if(tree != null)
  1298. return tree.getRowForPath(this, path);
  1299. return -1;
  1300. }
  1301. /**
  1302. * Ensures that the node identified by the specified path is
  1303. * expanded and viewable.
  1304. *
  1305. * @param path the TreePath identifying a node
  1306. */
  1307. public void expandPath(TreePath path) {
  1308. // Only expand if not leaf!
  1309. TreeModel model = getModel();
  1310. if(path != null && model != null &&
  1311. !model.isLeaf(path.getLastPathComponent())) {
  1312. setExpandedState(path, true);
  1313. }
  1314. }
  1315. /**
  1316. * Ensures that the node in the specified row is expanded and
  1317. * viewable. <p> If <code>row</code> is < 0 or >= getRowCount this
  1318. * will have no effect.
  1319. *
  1320. * @param row an int specifying a display row, where 0 is the
  1321. * first row in the display
  1322. */
  1323. public void expandRow(int row) {
  1324. expandPath(getPathForRow(row));
  1325. }
  1326. /**
  1327. * Ensures that the node identified by the specified path is
  1328. * collapsed and viewable.
  1329. *
  1330. * @param path the TreePath identifying a node
  1331. */
  1332. public void collapsePath(TreePath path) {
  1333. setExpandedState(path, false);
  1334. }
  1335. /**
  1336. * Ensures that the node in the specified row is collapsed.
  1337. * <p> If <code>row</code> is < 0 or >= getRowCount this
  1338. * will have no effect.
  1339. *
  1340. * @param row an int specifying a display row, where 0 is the
  1341. * first row in the display
  1342. */
  1343. public void collapseRow(int row) {
  1344. collapsePath(getPathForRow(row));
  1345. }
  1346. /**
  1347. * Returns the path for the node at the specified location.
  1348. *
  1349. * @param x an int giving the number of pixels horizontally from
  1350. * the left edge of the display area, minus any left margin
  1351. * @param y an int giving the number of pixels vertically from
  1352. * the top of the display area, minus any top margin
  1353. * @return the TreePath for the node at that location
  1354. */
  1355. public TreePath getPathForLocation(int x, int y) {
  1356. TreePath closestPath = getClosestPathForLocation(x, y);
  1357. if(closestPath != null) {
  1358. Rectangle pathBounds = getPathBounds(closestPath);
  1359. if(x >= pathBounds.x && x < (pathBounds.x + pathBounds.width) &&
  1360. y >= pathBounds.y && y < (pathBounds.y + pathBounds.height))
  1361. return closestPath;
  1362. }
  1363. return null;
  1364. }
  1365. /**
  1366. * Returns the row for the specified location.
  1367. *
  1368. * @param x an int giving the number of pixels horizontally from
  1369. * the left edge of the display area, minus any left margin
  1370. * @param y an int giving the number of pixels vertically from
  1371. * the top of the display area, minus any top margin
  1372. * @return the row corresponding to the location, or -1 if the
  1373. * location is not within the bounds of a displayed cell
  1374. * @see #getClosestRowForLocation
  1375. */
  1376. public int getRowForLocation(int x, int y) {
  1377. return getRowForPath(getPathForLocation(x, y));
  1378. }
  1379. /**
  1380. * Returns the path to the node that is closest to x,y. If
  1381. * no nodes are currently viewable, or there is no model, returns
  1382. * null, otherwise it always returns a valid path. To test if
  1383. * the node is exactly at x, y, get the node's bounds and
  1384. * test x, y against that.
  1385. *
  1386. * @param x an int giving the number of pixels horizontally from
  1387. * the left edge of the display area, minus any left margin
  1388. * @param y an int giving the number of pixels vertically from
  1389. * the top of the display area, minus any top margin
  1390. * @return the TreePath for the node closest to that location,
  1391. * null if nothing is viewable or there is no model
  1392. *
  1393. * @see #getPathForLocation
  1394. * @see #getPathBounds
  1395. */
  1396. public TreePath getClosestPathForLocation(int x, int y) {
  1397. TreeUI tree = getUI();
  1398. if(tree != null)
  1399. return tree.getClosestPathForLocation(this, x, y);
  1400. return null;
  1401. }
  1402. /**
  1403. * Returns the row to the node that is closest to x,y. If no nodes
  1404. * are viewable or there is no model, returns -1. Otherwise,
  1405. * it always returns a valid row. To test if the returned object is
  1406. * exactly at x, y, get the bounds for the node at the returned
  1407. * row and test x, y against that.
  1408. *
  1409. * @param x an int giving the number of pixels horizontally from
  1410. * the left edge of the display area, minus any left margin
  1411. * @param y an int giving the number of pixels vertically from
  1412. * the top of the display area, minus any top margin
  1413. * @return the row closest to the location, -1 if nothing is
  1414. * viewable or there is no model
  1415. *
  1416. * @see #getRowForLocation
  1417. * @see #getRowBounds
  1418. */
  1419. public int getClosestRowForLocation(int x, int y) {
  1420. return getRowForPath(getClosestPathForLocation(x, y));
  1421. }
  1422. /**
  1423. * Returns true if the tree is being edited. The item that is being
  1424. * edited can be obtained using <code>getSelectionPath</code>.
  1425. *
  1426. * @return true if the user is currently editing a node
  1427. * @see #getSelectionPath
  1428. */
  1429. public boolean isEditing() {
  1430. TreeUI tree = getUI();
  1431. if(tree != null)
  1432. return tree.isEditing(this);
  1433. return false;
  1434. }
  1435. /**
  1436. * Ends the current editing session. (The DefaultTreeCellEditor
  1437. * object saves any edits that are currently in progress on a cell.
  1438. * Other implementations may operate differently.)
  1439. * Has no effect if the tree isn't being edited.
  1440. * <blockquote>
  1441. * <b>Note:</b><br>
  1442. * To make edit-saves automatic whenever the user changes
  1443. * their position in the tree, use {@link #setInvokesStopCellEditing}.
  1444. * </blockquote>
  1445. *
  1446. * @return true if editing was in progress and is now stopped,
  1447. * false if editing was not in progress
  1448. */
  1449. public boolean stopEditing() {
  1450. TreeUI tree = getUI();
  1451. if(tree != null)
  1452. return tree.stopEditing(this);
  1453. return false;
  1454. }
  1455. /**
  1456. * Cancels the current editing session. Has no effect if the
  1457. * tree isn't being edited.
  1458. */
  1459. public void cancelEditing() {
  1460. TreeUI tree = getUI();
  1461. if(tree != null)
  1462. tree.cancelEditing(this);
  1463. }
  1464. /**
  1465. * Selects the node identified by the specified path and initiates
  1466. * editing. The edit-attempt fails if the CellEditor does not allow
  1467. * editing for the specified item.
  1468. *
  1469. * @param path the TreePath identifying a node
  1470. */
  1471. public void startEditingAtPath(TreePath path) {
  1472. TreeUI tree = getUI();
  1473. if(tree != null)
  1474. tree.startEditingAtPath(this, path);
  1475. }
  1476. /**
  1477. * Returns the path to the element that is currently being edited.
  1478. *
  1479. * @return the TreePath for the node being edited
  1480. */
  1481. public TreePath getEditingPath() {
  1482. TreeUI tree = getUI();
  1483. if(tree != null)
  1484. return tree.getEditingPath(this);
  1485. return null;
  1486. }
  1487. //
  1488. // Following are primarily convenience methods for mapping from
  1489. // row based selections to path selections. Sometimes it is
  1490. // easier to deal with these than paths (mouse downs, key downs
  1491. // usually just deal with index based selections).
  1492. // Since row based selections require a UI many of these won't work
  1493. // without one.
  1494. //
  1495. /**
  1496. * Sets the tree's selection model. When a null value is specified
  1497. * an empty electionModel is used, which does not allow selections.
  1498. *
  1499. * @param selectionModel the TreeSelectionModel to use, or null to
  1500. * disable selections
  1501. * @see TreeSelectionModel
  1502. * @beaninfo
  1503. * bound: true
  1504. * description: The tree's selection model.
  1505. */
  1506. public void setSelectionModel(TreeSelectionModel selectionModel) {
  1507. if(selectionModel == null)
  1508. selectionModel = EmptySelectionModel.sharedInstance();
  1509. TreeSelectionModel oldValue = this.selectionModel;
  1510. if (this.selectionModel != null && selectionRedirector != null) {
  1511. this.selectionModel.removeTreeSelectionListener
  1512. (selectionRedirector);
  1513. }
  1514. if (accessibleContext != null) {
  1515. this.selectionModel.removeTreeSelectionListener((TreeSelectionListener)accessibleContext);
  1516. selectionModel.addTreeSelectionListener((TreeSelectionListener)accessibleContext);
  1517. }
  1518. this.selectionModel = selectionModel;
  1519. if (selectionRedirector != null) {
  1520. this.selectionModel.addTreeSelectionListener(selectionRedirector);
  1521. }
  1522. firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue,
  1523. this.selectionModel);
  1524. if (accessibleContext != null) {
  1525. accessibleContext.firePropertyChange(
  1526. AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
  1527. new Boolean(false), new Boolean(true));
  1528. }
  1529. }
  1530. /**
  1531. * Returns the model for selections. This should always return a
  1532. * non-null value. If you don't want to allow anything to be selected
  1533. * set the selection model to null, which forces an empty
  1534. * selection model to be used.
  1535. *
  1536. * @param the TreeSelectionModel in use
  1537. * @see #setSelectionModel
  1538. */
  1539. public TreeSelectionModel getSelectionModel() {
  1540. return selectionModel;
  1541. }
  1542. /**
  1543. * Returns JTreePath instances representing the path between index0
  1544. * and index1 (including index1). Returns null if there is no tree.
  1545. *
  1546. * @param index0 an int specifying a display row, where 0 is the
  1547. * first row in the display
  1548. * @param index0 an int specifying a second display row
  1549. * @return an array of TreePath objects, one for each node between
  1550. * index0 and index1, inclusive
  1551. */
  1552. protected TreePath[] getPathBetweenRows(int index0, int index1) {
  1553. int newMinIndex, newMaxIndex;
  1554. TreeUI tree = getUI();
  1555. newMinIndex = Math.min(index0, index1);
  1556. newMaxIndex = Math.max(index0, index1);
  1557. if(tree != null) {
  1558. TreePath[] selection = new TreePath[newMaxIndex -
  1559. newMinIndex + 1];
  1560. for(int counter = newMinIndex; counter <= newMaxIndex; counter++)
  1561. selection[counter - newMinIndex] = tree.getPathForRow(this,
  1562. counter);
  1563. return selection;
  1564. }
  1565. return null;
  1566. }
  1567. /**
  1568. * Selects the nodes between index0 and index1, inclusive.
  1569. *
  1570. * @param index0 an int specifying a display row, where 0 is the
  1571. * first row in the display
  1572. * @param index0 an int specifying a second display row
  1573. */
  1574. public void setSelectionInterval(int index0, int index1) {
  1575. TreePath[] paths = getPathBetweenRows(index0, index1);
  1576. this.getSelectionModel().setSelectionPaths(paths);
  1577. }
  1578. /**
  1579. * Adds the paths between index0 and index1, inclusive, to the
  1580. * selection.
  1581. *
  1582. * @param index0 an int specifying a display row, where 0 is the
  1583. * first row in the display
  1584. * @param index0 an int specifying a second display row
  1585. */
  1586. public void addSelectionInterval(int index0, int index1) {
  1587. TreePath[] paths = getPathBetweenRows(index0, index1);
  1588. this.getSelectionModel().addSelectionPaths(paths);
  1589. }
  1590. /**
  1591. * Removes the nodes between index0 and index1, inclusive, from the
  1592. * selection.
  1593. *
  1594. * @param index0 an int specifying a display row, where 0 is the
  1595. * first row in the display
  1596. * @param index0 an int specifying a second display row
  1597. */
  1598. public void removeSelectionInterval(int index0, int index1) {
  1599. TreePath[] paths = getPathBetweenRows(index0, index1);
  1600. this.getSelectionModel().removeSelectionPaths(paths);
  1601. }
  1602. /**
  1603. * Removes the node identified by the specified path from the current
  1604. * selection.
  1605. *
  1606. * @param path the TreePath identifying a node
  1607. */
  1608. public void removeSelectionPath(TreePath path) {
  1609. this.getSelectionModel().removeSelectionPath(path);
  1610. }
  1611. /**
  1612. * Removes the nodes identified by the specified paths from the
  1613. * current selection.
  1614. *
  1615. * @param paths an array of TreePath objects that specifies the nodes
  1616. * to remove
  1617. */
  1618. public void removeSelectionPaths(TreePath[] paths) {
  1619. this.getSelectionModel().removeSelectionPaths(paths);
  1620. }
  1621. /**
  1622. * Removes the path at the index <code>row</code> from the current
  1623. * selection.
  1624. *
  1625. * @param path the TreePath identifying the node to remove
  1626. */
  1627. public void removeSelectionRow(int row) {
  1628. int[] rows = { row };
  1629. removeSelectionRows(rows);
  1630. }
  1631. /**
  1632. * Removes the paths that are selected at each of the specified
  1633. * rows.
  1634. *
  1635. * @param row an array of ints specifying display rows, where 0 is
  1636. * the first row in the display
  1637. */
  1638. public void removeSelectionRows(int[] rows) {
  1639. TreeUI ui = getUI();
  1640. if(ui != null && rows != null) {
  1641. int numRows = rows.length;
  1642. TreePath[] paths = new TreePath[numRows];
  1643. for(int counter = 0; counter < numRows; counter++)
  1644. paths[counter] = ui.getPathForRow(this, rows[counter]);
  1645. removeSelectionPaths(paths);
  1646. }
  1647. }
  1648. /**
  1649. * Clears the selection.
  1650. */
  1651. public void clearSelection() {
  1652. getSelectionModel().clearSelection();
  1653. }
  1654. /**
  1655. * Returns true if the selection is currently empty.
  1656. *
  1657. * @return true if the selection is currently empty
  1658. */
  1659. public boolean isSelectionEmpty() {
  1660. return getSelectionModel().isSelectionEmpty();
  1661. }
  1662. /**
  1663. * Adds a listener for TreeExpansion events.
  1664. *
  1665. * @param tel a TreeExpansionListener that will be notified when
  1666. * a tree node is expanded or collapsed (a "negative
  1667. * expansion")
  1668. */
  1669. public void addTreeExpansionListener(TreeExpansionListener tel) {
  1670. listenerList.add(TreeExpansionListener.class, tel);
  1671. }
  1672. /**
  1673. * Removes a listener for TreeExpansion events.
  1674. *
  1675. * @param tel the TreeExpansionListener to remove
  1676. */
  1677. public void removeTreeExpansionListener(TreeExpansionListener tel) {
  1678. listenerList.remove(TreeExpansionListener.class, tel);
  1679. }
  1680. /**
  1681. * Adds a listener for TreeWillExpand events.
  1682. *
  1683. * @param tel a TreeWillExpandListener that will be notified when
  1684. * a tree node will be expanded or collapsed (a "negative
  1685. * expansion")
  1686. */
  1687. public void addTreeWillExpandListener(TreeWillExpandListener tel) {
  1688. listenerList.add(TreeWillExpandListener.class, tel);
  1689. }
  1690. /**
  1691. * Removes a listener for TreeWillExpand events.
  1692. *
  1693. * @param tel the TreeWillExpandListener to remove
  1694. */
  1695. public void removeTreeWillExpandListener(TreeWillExpandListener tel) {
  1696. listenerList.remove(TreeWillExpandListener.class, tel);
  1697. }
  1698. /**
  1699. * Notify all listeners that have registered interest for
  1700. * notification on this event type. The event instance
  1701. * is lazily created using the parameters passed into
  1702. * the fire method.
  1703. *
  1704. * @param path the TreePath indicating the node that was expanded
  1705. * @see EventListenerList
  1706. */
  1707. public void fireTreeExpanded(TreePath path) {
  1708. // Guaranteed to return a non-null array
  1709. Object[] listeners = listenerList.getListenerList();
  1710. TreeExpansionEvent e = null;
  1711. // Process the listeners last to first, notifying
  1712. // those that are interested in this event
  1713. for (int i = listeners.length-2; i>=0; i-=2) {
  1714. if (listeners[i]==TreeExpansionListener.class) {
  1715. // Lazily create the event:
  1716. if (e == null)
  1717. e = new TreeExpansionEvent(this, path);
  1718. ((TreeExpansionListener)listeners[i+1]).
  1719. treeExpanded(e);
  1720. }
  1721. }
  1722. }
  1723. /**
  1724. * Notify all listeners that have registered interest for
  1725. * notification on this event type. The event instance
  1726. * is lazily created using the parameters passed into
  1727. * the fire method.
  1728. *
  1729. * @param path the TreePath indicating the node that was collapsed
  1730. * @see EventListenerList
  1731. */
  1732. public void fireTreeCollapsed(TreePath path) {
  1733. // Guaranteed to return a non-null array
  1734. Object[] listeners = listenerList.getListenerList();
  1735. TreeExpansionEvent e = null;
  1736. // Process the listeners last to first, notifying
  1737. // those that are interested in this event
  1738. for (int i = listeners.length-2; i>=0; i-=2) {
  1739. if (listeners[i]==TreeExpansionListener.class) {
  1740. // Lazily create the event:
  1741. if (e == null)
  1742. e = new TreeExpansionEvent(this, path);
  1743. ((TreeExpansionListener)listeners[i+1]).
  1744. treeCollapsed(e);
  1745. }
  1746. }
  1747. }
  1748. /**
  1749. * Notify all listeners that have registered interest for
  1750. * notification on this event type. The event instance
  1751. * is lazily created using the parameters passed into
  1752. * the fire method.
  1753. *
  1754. * @param path the TreePath indicating the node that was expanded
  1755. * @see EventListenerList
  1756. */
  1757. public void fireTreeWillExpand(TreePath path) throws ExpandVetoException {
  1758. // Guaranteed to return a non-null array
  1759. Object[] listeners = listenerList.getListenerList();
  1760. TreeExpansionEvent e = null;
  1761. // Process the listeners last to first, notifying
  1762. // those that are interested in this event
  1763. for (int i = listeners.length-2; i>=0; i-=2) {
  1764. if (listeners[i]==TreeWillExpandListener.class) {
  1765. // Lazily create the event:
  1766. if (e == null)
  1767. e = new TreeExpansionEvent(this, path);
  1768. ((TreeWillExpandListener)listeners[i+1]).
  1769. treeWillExpand(e);
  1770. }
  1771. }
  1772. }
  1773. /**
  1774. * Notify all listeners that have registered interest for
  1775. * notification on this event type. The event instance
  1776. * is lazily created using the parameters passed into
  1777. * the fire method.
  1778. *
  1779. * @param path the TreePath indicating the node that was expanded
  1780. * @see EventListenerList
  1781. */
  1782. public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException {
  1783. // Guaranteed to return a non-null array
  1784. Object[] listeners = listenerList.getListenerList();
  1785. TreeExpansionEvent e = null;
  1786. // Process the listeners last to first, notifying
  1787. // those that are interested in this event
  1788. for (int i = listeners.length-2; i>=0; i-=2) {
  1789. if (listeners[i]==TreeWillExpandListener.class) {
  1790. // Lazily create the event:
  1791. if (e == null)
  1792. e = new TreeExpansionEvent(this, path);
  1793. ((TreeWillExpandListener)listeners[i+1]).
  1794. treeWillCollapse(e);
  1795. }
  1796. }
  1797. }
  1798. /**
  1799. * Adds a listener for TreeSelection events.
  1800. *
  1801. * @param tsl the TreeSelectionListener that will be notified when
  1802. * a node is selected or deselected (a "negative
  1803. * selection")
  1804. */
  1805. public void addTreeSelectionListener(TreeSelectionListener tsl) {
  1806. listenerList.add(TreeSelectionListener.class,tsl);
  1807. if(listenerList.getListenerCount(TreeSelectionListener.class) != 0
  1808. && selectionRedirector == null) {
  1809. selectionRedirector = new TreeSelectionRedirector();
  1810. selectionModel.addTreeSelectionListener(selectionRedirector);
  1811. }
  1812. }
  1813. /**
  1814. * Removes a TreeSelection listener.
  1815. *
  1816. * @param tsl the TreeSelectionListener to remove
  1817. */
  1818. public void removeTreeSelectionListener(TreeSelectionListener tsl) {
  1819. listenerList.remove(TreeSelectionListener.class,tsl);
  1820. if(listenerList.getListenerCount(TreeSelectionListener.class) == 0
  1821. && selectionRedirector != null) {
  1822. selectionModel.removeTreeSelectionListener
  1823. (selectionRedirector);
  1824. selectionRedirector = null;
  1825. }
  1826. }
  1827. /**
  1828. * Notify all listeners that have registered interest for
  1829. * notification on this event type. The event instance
  1830. * is lazily created using the parameters passed into
  1831. * the fire method.
  1832. *
  1833. * @param e the TreeSelectionEvent generated by the TreeSelectionModel
  1834. * when a node is selected or deselected
  1835. * @see EventListenerList
  1836. */
  1837. protected void fireValueChanged(TreeSelectionEvent e) {
  1838. // Guaranteed to return a non-null array
  1839. Object[] listeners = listenerList.getListenerList();
  1840. // Process the listeners last to first, notifying
  1841. // those that are interested in this event
  1842. for (int i = listeners.length-2; i>=0; i-=2) {
  1843. // TreeSelectionEvent e = null;
  1844. if (listeners[i]==TreeSelectionListener.class) {
  1845. // Lazily create the event:
  1846. // if (e == null)
  1847. // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  1848. ((TreeSelectionListener)listeners[i+1]).valueChanged(e);
  1849. }
  1850. }
  1851. }
  1852. /**
  1853. * Sent when the tree has changed enough that we need to resize
  1854. * the bounds, but not enough that we need to remove the
  1855. * expanded node set (e.g nodes were expanded or collapsed, or
  1856. * nodes were inserted into the tree). You should never have to
  1857. * invoke this, the UI will invoke this as it needs to.
  1858. */
  1859. public void treeDidChange() {
  1860. revalidate();
  1861. repaint();
  1862. }
  1863. /**
  1864. * Sets the number of rows that are to be displayed.
  1865. * This will only work if the reciever is contained in a JScrollPane,
  1866. * and will adjust the preferred size and size of that scrollpane.
  1867. *
  1868. * @param newCount the number of rows to display
  1869. * @beaninfo
  1870. * bound: true
  1871. * description: The number of rows that are to be displayed.
  1872. */
  1873. public void setVisibleRowCount(int newCount) {
  1874. int oldCount = visibleRowCount;
  1875. visibleRowCount = newCount;
  1876. firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldCount,
  1877. visibleRowCount);
  1878. invalidate();
  1879. if (accessibleContext != null) {
  1880. ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
  1881. }
  1882. }
  1883. /**
  1884. * Returns the number of rows that are displayed in the display area.
  1885. *
  1886. * @return the number of rows displayed
  1887. */
  1888. public int getVisibleRowCount() {
  1889. return visibleRowCount;
  1890. }
  1891. // Serialization support.
  1892. private void writeObject(ObjectOutputStream s) throws IOException {
  1893. Vector values = new Vector();
  1894. s.defaultWriteObject();
  1895. // Save the cellRenderer, if its Serializable.
  1896. if(cellRenderer != null && cellRenderer instanceof Serializable) {
  1897. values.addElement("cellRenderer");
  1898. values.addElement(cellRenderer);
  1899. }
  1900. // Save the cellEditor, if its Serializable.
  1901. if(cellEditor != null && cellEditor instanceof Serializable) {
  1902. values.addElement("cellEditor");
  1903. values.addElement(cellEditor);
  1904. }
  1905. // Save the treeModel, if its Serializable.
  1906. if(treeModel != null && treeModel instanceof Serializable) {
  1907. values.addElement("treeModel");
  1908. values.addElement(treeModel);
  1909. }
  1910. // Save the selectionModel, if its Serializable.
  1911. if(selectionModel != null && selectionModel instanceof Serializable) {
  1912. values.addElement("selectionModel");
  1913. values.addElement(selectionModel);
  1914. }
  1915. Object expandedData = getArchivableExpandedState();
  1916. if(expandedData != null) {
  1917. values.addElement("expandedState");
  1918. values.addElement(expandedData);
  1919. }
  1920. s.writeObject(values);
  1921. if ((ui != null) && (getUIClassID().equals(uiClassID))) {
  1922. ui.installUI(this);
  1923. }
  1924. }
  1925. private void readObject(ObjectInputStream s)
  1926. throws IOException, ClassNotFoundException {
  1927. s.defaultReadObject();
  1928. // Create an instance of expanded state.
  1929. expandedState = new Hashtable();
  1930. expandedStack = new Stack();
  1931. Vector values = (Vector)s.readObject();
  1932. int indexCounter = 0;
  1933. int maxCounter = values.size();
  1934. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1935. equals("cellRenderer")) {
  1936. cellRenderer = (TreeCellRenderer)values.elementAt(++indexCounter);
  1937. indexCounter++;
  1938. }
  1939. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1940. equals("cellEditor")) {
  1941. cellEditor = (TreeCellEditor)values.elementAt(++indexCounter);
  1942. indexCounter++;
  1943. }
  1944. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1945. equals("treeModel")) {
  1946. treeModel = (TreeModel)values.elementAt(++indexCounter);
  1947. indexCounter++;
  1948. }
  1949. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1950. equals("selectionModel")) {
  1951. selectionModel = (TreeSelectionModel)values.elementAt(++indexCounter);
  1952. indexCounter++;
  1953. }
  1954. if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1955. equals("expandedState")) {
  1956. unarchiveExpandedState(values.elementAt(++indexCounter));
  1957. indexCounter++;
  1958. }
  1959. // Reinstall the redirector.
  1960. if(listenerList.getListenerCount(TreeSelectionListener.class) != 0) {
  1961. selectionRedirector = new TreeSelectionRedirector();
  1962. selectionModel.addTreeSelectionListener(selectionRedirector);
  1963. }
  1964. // Listener to TreeModel.
  1965. if(treeModel != null) {
  1966. treeModelListener = createTreeModelListener();
  1967. if(treeModelListener != null)
  1968. treeModel.addTreeModelListener(treeModelListener);
  1969. }
  1970. }
  1971. /**
  1972. * Returns an object that can be archived indicating what nodes are
  1973. * expanded and what aren't. The objects from the model are NOT
  1974. * written out.
  1975. */
  1976. private Object getArchivableExpandedState() {
  1977. TreeModel model = getModel();
  1978. if(model != null) {
  1979. Enumeration paths = expandedState.keys();
  1980. if(paths != null) {
  1981. Vector state = new Vector();
  1982. while(paths.hasMoreElements()) {
  1983. TreePath path = (TreePath)paths.nextElement();
  1984. Object archivePath;
  1985. try {
  1986. archivePath = getModelIndexsForPath(path);
  1987. } catch (Error error) {
  1988. archivePath = null;
  1989. }
  1990. if(archivePath != null) {
  1991. state.addElement(archivePath);
  1992. state.addElement(expandedState.get(path));
  1993. }
  1994. }
  1995. return state;
  1996. }
  1997. }
  1998. return null;
  1999. }
  2000. /**
  2001. * Updates the expanded state of nodes in the tree based on the
  2002. * previously archived state <code>state</code>.
  2003. */
  2004. private void unarchiveExpandedState(Object state) {
  2005. if(state instanceof Vector) {
  2006. Vector paths = (Vector)state;
  2007. for(int counter = paths.size() - 1; counter >= 0; counter--) {
  2008. Boolean eState = (Boolean)paths.elementAt(counter--);
  2009. TreePath path;
  2010. try {
  2011. path = getPathForIndexs((int[])paths.elementAt(counter));
  2012. if(path != null)
  2013. expandedState.put(path, eState);
  2014. } catch (Error error) {}
  2015. }
  2016. }
  2017. }
  2018. /**
  2019. * Returns an array of integers specifying the indexs of the
  2020. * components in the <code>path</code>. If <code>path</code> is
  2021. * the root, this will return an empty array.
  2022. */
  2023. private int[] getModelIndexsForPath(TreePath path) {
  2024. if(path != null) {
  2025. TreeModel model = getModel();
  2026. int count = path.getPathCount();
  2027. int[] indexs = new int[count - 1];
  2028. Object parent = model.getRoot();
  2029. for(int counter = 1; counter < count; counter++) {
  2030. indexs[counter - 1] = model.getIndexOfChild
  2031. (parent, path.getPathComponent(counter));
  2032. parent = path.getPathComponent(counter);
  2033. if(indexs[counter - 1] < 0)
  2034. return null;
  2035. }
  2036. return indexs;
  2037. }
  2038. return null;
  2039. }
  2040. /**
  2041. * Returns a TreePath created by obtaining the children for each of
  2042. * the indices in <code>indexs</code>.
  2043. */
  2044. private TreePath getPathForIndexs(int[] indexs) {
  2045. if(indexs == null)
  2046. return null;
  2047. TreeModel model = getModel();
  2048. if(model == null)
  2049. return null;
  2050. int count = indexs.length;
  2051. Object parent = model.getRoot();
  2052. TreePath parentPath = new TreePath(parent);
  2053. for(int counter = 0; counter < count; counter++) {
  2054. parent = model.getChild(parent, indexs[counter]);
  2055. if(parent == null)
  2056. return null;
  2057. parentPath = parentPath.pathByAddingChild(parent);
  2058. }
  2059. return parentPath;
  2060. }
  2061. /**
  2062. * EmptySelectionModel is a TreeSelectionModel that does not allow
  2063. * anything to be selected.
  2064. * <p>
  2065. * <strong>Warning:</strong>
  2066. * Serialized objects of this class will not be compatible with
  2067. * future Swing releases. The current serialization support is appropriate
  2068. * for short term storage or RMI between applications running the same
  2069. * version of Swing. A future release of Swing will provide support for
  2070. * long term persistence.
  2071. */
  2072. protected static class EmptySelectionModel extends
  2073. DefaultTreeSelectionModel
  2074. {
  2075. /** Unique shared instance. */
  2076. protected static final EmptySelectionModel sharedInstance =
  2077. new EmptySelectionModel();
  2078. /** Returns a shared instance of an empty selection model */
  2079. static public EmptySelectionModel sharedInstance() {
  2080. return sharedInstance;
  2081. }
  2082. /** A null implementation that selects nothing */
  2083. public void setSelectionPaths(TreePath[] pPaths) {}
  2084. /** A null implementation that adds nothing */
  2085. public void addSelectionPaths(TreePath[] paths) {}
  2086. /** A null implementation that removes nothing */
  2087. public void removeSelectionPaths(TreePath[] paths) {}
  2088. }
  2089. /**
  2090. * Handles creating a new TreeSelectionEvent with the JTree as the
  2091. * source and passing it off to all the listeners.
  2092. * <p>
  2093. * <strong>Warning:</strong>
  2094. * Serialized objects of this class will not be compatible with
  2095. * future Swing releases. The current serialization support is appropriate
  2096. * for short term storage or RMI between applications running the same
  2097. * version of Swing. A future release of Swing will provide support for
  2098. * long term persistence.
  2099. */
  2100. protected class TreeSelectionRedirector implements Serializable,
  2101. TreeSelectionListener
  2102. {
  2103. /**
  2104. * Invoked by the TreeSelectionModel when the selection changes.
  2105. *
  2106. * @param e the TreeSelectionEvent generated by the TreeSelectionModel
  2107. */
  2108. public void valueChanged(TreeSelectionEvent e) {
  2109. TreeSelectionEvent newE;
  2110. newE = (TreeSelectionEvent)e.cloneWithSource(JTree.this);
  2111. fireValueChanged(newE);
  2112. }
  2113. } // End of class JTree.TreeSelectionRedirector
  2114. //
  2115. // Scrollable interface
  2116. //
  2117. /**
  2118. * Returns the preferred display size of a JTree. The height is
  2119. * determined from <code>getVisibleRowCount</code> and the width
  2120. * is the current preferred width.
  2121. *
  2122. * @return a Dimension object containing the preferred size
  2123. */
  2124. public Dimension getPreferredScrollableViewportSize() {
  2125. int width = getPreferredSize().width;
  2126. int visRows = getVisibleRowCount();
  2127. int height;
  2128. if(isFixedRowHeight())
  2129. height = visRows * getRowHeight();
  2130. else {
  2131. TreeUI ui = getUI();
  2132. if(ui != null && ui.getRowCount(this) > 0)
  2133. height = getRowBounds(0).height * visRows;
  2134. else
  2135. height = 16 * visRows;
  2136. }
  2137. return new Dimension(width, height);
  2138. }
  2139. /**
  2140. * Returns the amount to increment when scrolling. The amount is
  2141. * the height of the first displayed row that isn't completely in view
  2142. * or, if it is totally displayed, the height of the next row in the
  2143. * scrolling direction.
  2144. *
  2145. * @param visibleRect The view area visible within the viewport
  2146. * @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
  2147. * @param direction Less than zero to scroll up/left, greater than zero for down/right.
  2148. * @return The "unit" increment for scrolling in the specified direction
  2149. * @see JScrollBar#setUnitIncrement
  2150. */
  2151. public int getScrollableUnitIncrement(Rectangle visibleRect,
  2152. int orientation, int direction) {
  2153. if(orientation == SwingConstants.VERTICAL) {
  2154. Rectangle rowBounds;
  2155. int firstIndex = getClosestRowForLocation
  2156. (0, visibleRect.y);
  2157. if(firstIndex != -1) {
  2158. rowBounds = getRowBounds(firstIndex);
  2159. if(rowBounds.y != visibleRect.y) {
  2160. if(direction < 0) // UP
  2161. return (visibleRect.y - rowBounds.y);
  2162. return (rowBounds.y + rowBounds.height - visibleRect.y);
  2163. }
  2164. if(direction < 0) { // UP
  2165. if(firstIndex != 0) {
  2166. rowBounds = getRowBounds(firstIndex - 1);
  2167. return rowBounds.height;
  2168. }
  2169. }
  2170. else {
  2171. return rowBounds.height;
  2172. }
  2173. }
  2174. return 0;
  2175. }
  2176. return 4;
  2177. }
  2178. /**
  2179. * Returns the amount for a block inrecment, which is the height or
  2180. * width of <code>visibleRect</code>, based on <code>orientation</code>.
  2181. *
  2182. * @param visibleRect The view area visible within the viewport
  2183. * @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
  2184. * @param direction Less than zero to scroll up/left, greater than zero for down/right.
  2185. * @return The "block" increment for scrolling in the specified direction.
  2186. * @see JScrollBar#setBlockIncrement
  2187. */
  2188. public int getScrollableBlockIncrement(Rectangle visibleRect,
  2189. int orientation, int direction) {
  2190. return (orientation == SwingConstants.VERTICAL) ? visibleRect.height :
  2191. visibleRect.width;
  2192. }
  2193. /**
  2194. * Returns false to indicate that the width of the viewport does not
  2195. * determine the width of the table, unless the preferred width of
  2196. * the tree is smaller than the viewports width. In other words:
  2197. * ensure that the tree is never smaller than its viewport.
  2198. *
  2199. * @return false
  2200. * @see Scrollable#getScrollableTracksViewportWidth
  2201. */
  2202. public boolean getScrollableTracksViewportWidth() {
  2203. if (getParent() instanceof JViewport) {
  2204. return (((JViewport)getParent()).getWidth() > getPreferredSize().width);
  2205. }
  2206. return false;
  2207. }
  2208. /**
  2209. * Returns false to indicate that the height of the viewport does not
  2210. * determine the height of the table, unless the preferred height
  2211. * of the tree is smaller than the viewports height. In other words:
  2212. * ensure that the tree is never smaller than its viewport.
  2213. *
  2214. * @return false
  2215. * @see Scrollable#getScrollableTracksViewportHeight
  2216. */
  2217. public boolean getScrollableTracksViewportHeight() {
  2218. if (getParent() instanceof JViewport) {
  2219. return (((JViewport)getParent()).getHeight() > getPreferredSize().height);
  2220. }
  2221. return false;
  2222. }
  2223. /**
  2224. * Sets the expanded state of the receiver. If <code>state</code> is
  2225. * true, all parents of <code>path</code> and path are marked as
  2226. * expanded. If <code>state</code> is false, all parents of
  2227. * <code>path</code> are marked EXPANDED, but <code>path</code> itself
  2228. * is marked collapsed.<p>
  2229. * This will fail if a TreeWillExpandListener vetos it.
  2230. */
  2231. protected void setExpandedState(TreePath path, boolean state) {
  2232. if(path != null) {
  2233. // Make sure all parents of path are expanded.
  2234. Stack stack;
  2235. TreePath parentPath = path.getParentPath();
  2236. if (expandedStack.size() == 0) {
  2237. stack = new Stack();
  2238. }
  2239. else {
  2240. stack = (Stack)expandedStack.pop();
  2241. }
  2242. try {
  2243. while(parentPath != null) {
  2244. if(isExpanded(parentPath)) {
  2245. parentPath = null;
  2246. }
  2247. else {
  2248. stack.push(parentPath);
  2249. parentPath = parentPath.getParentPath();
  2250. }
  2251. }
  2252. for(int counter = stack.size() - 1; counter >= 0; counter--) {
  2253. parentPath = (TreePath)stack.pop();
  2254. if(!isExpanded(parentPath)) {
  2255. try {
  2256. fireTreeWillExpand(parentPath);
  2257. } catch (ExpandVetoException eve) {
  2258. // Expand vetoed!
  2259. return;
  2260. }
  2261. expandedState.put(parentPath, Boolean.TRUE);
  2262. fireTreeExpanded(parentPath);
  2263. if (accessibleContext != null) {
  2264. ((AccessibleJTree)accessibleContext).
  2265. fireVisibleDataPropertyChange();
  2266. }
  2267. }
  2268. }
  2269. }
  2270. finally {
  2271. if (expandedStack.size() < TEMP_STACK_SIZE) {
  2272. stack.removeAllElements();
  2273. expandedStack.push(stack);
  2274. }
  2275. }
  2276. if(!state) {
  2277. // collapse last path.
  2278. Object cValue = expandedState.get(path);
  2279. if(cValue != null && ((Boolean)cValue).booleanValue()) {
  2280. try {
  2281. fireTreeWillCollapse(path);
  2282. }
  2283. catch (ExpandVetoException eve) {
  2284. return;
  2285. }
  2286. expandedState.put(path, Boolean.FALSE);
  2287. fireTreeCollapsed(path);
  2288. if (accessibleContext != null) {
  2289. ((AccessibleJTree)accessibleContext).
  2290. fireVisibleDataPropertyChange();
  2291. }
  2292. }
  2293. }
  2294. else {
  2295. // Expand last path.
  2296. Object cValue = expandedState.get(path);
  2297. if(cValue == null || !((Boolean)cValue).booleanValue()) {
  2298. try {
  2299. fireTreeWillExpand(path);
  2300. }
  2301. catch (ExpandVetoException eve) {
  2302. return;
  2303. }
  2304. expandedState.put(path, Boolean.TRUE);
  2305. fireTreeExpanded(path);
  2306. if (accessibleContext != null) {
  2307. ((AccessibleJTree)accessibleContext).
  2308. fireVisibleDataPropertyChange();
  2309. }
  2310. }
  2311. }
  2312. }
  2313. }
  2314. /**
  2315. * Returns an Enumeration of TreePaths that have been expanded that
  2316. * are descendants of <code>parent</code>.
  2317. */
  2318. protected Enumeration getDescendantToggledPaths(TreePath parent) {
  2319. if(parent == null)
  2320. return null;
  2321. Vector descendants = new Vector();
  2322. Enumeration nodes = expandedState.keys();
  2323. TreePath path;
  2324. while(nodes.hasMoreElements()) {
  2325. path = (TreePath)nodes.nextElement();
  2326. if(parent.isDescendant(path))
  2327. descendants.addElement(path);
  2328. }
  2329. return descendants.elements();
  2330. }
  2331. /**
  2332. * Removes any descendants of the TreePaths in <code>toRemove</code>
  2333. * that have been expanded.
  2334. */
  2335. protected void removeDescendantToggledPaths(Enumeration toRemove) {
  2336. if(toRemove != null) {
  2337. while(toRemove.hasMoreElements()) {
  2338. Enumeration descendants = getDescendantToggledPaths
  2339. ((TreePath)toRemove.nextElement());
  2340. if(descendants != null) {
  2341. while(descendants.hasMoreElements()) {
  2342. expandedState.remove(descendants.nextElement());
  2343. }
  2344. }
  2345. }
  2346. }
  2347. }
  2348. /**
  2349. * Clears the cache of toggled tree paths. This does NOT send out
  2350. * any TreeExpansionListener events.
  2351. */
  2352. protected void clearToggledPaths() {
  2353. expandedState.clear();
  2354. }
  2355. /**
  2356. * Creates and returns an instance of TreeModelHandler. The returned
  2357. * object is responsible for updating the expanded state when the
  2358. * TreeModel changes.
  2359. */
  2360. protected TreeModelListener createTreeModelListener() {
  2361. return new TreeModelHandler();
  2362. }
  2363. /**
  2364. * Listens to the model and updates the expandedState accordingly
  2365. * when nodes are removed, or changed.
  2366. */
  2367. protected class TreeModelHandler implements TreeModelListener {
  2368. public void treeNodesChanged(TreeModelEvent e) { }
  2369. public void treeNodesInserted(TreeModelEvent e) { }
  2370. public void treeStructureChanged(TreeModelEvent e) {
  2371. if(e == null)
  2372. return;
  2373. // NOTE: If I change this to NOT remove the descendants
  2374. // and update BasicTreeUIs treeStructureChanged method
  2375. // to update descendants in response to a treeStructureChanged
  2376. // event, all the children of the event won't collapse!
  2377. TreePath parent = e.getTreePath();
  2378. if(parent == null)
  2379. return;
  2380. if (parent.getPathCount() == 1) {
  2381. // New root, remove everything!
  2382. clearToggledPaths();
  2383. if(!treeModel.isLeaf(treeModel.getRoot())) {
  2384. // Mark the root as expanded, if it isn't a leaf.
  2385. expandedState.put(parent, Boolean.TRUE);
  2386. }
  2387. }
  2388. else if(expandedState.get(parent) != null) {
  2389. Vector toRemove = new Vector(1);
  2390. boolean isExpanded = isExpanded(parent);
  2391. toRemove.addElement(parent);
  2392. removeDescendantToggledPaths(toRemove.elements());
  2393. if(isExpanded) {
  2394. TreeModel model = getModel();
  2395. if(model == null || model.isLeaf
  2396. (parent.getLastPathComponent()))
  2397. collapsePath(parent);
  2398. else
  2399. expandedState.put(parent, Boolean.TRUE);
  2400. }
  2401. }
  2402. }
  2403. public void treeNodesRemoved(TreeModelEvent e) {
  2404. if(e == null)
  2405. return;
  2406. TreePath parent = e.getTreePath();
  2407. Object[] children = e.getChildren();
  2408. if(children == null)
  2409. return;
  2410. TreePath rPath;
  2411. Vector toRemove = new Vector(Math.max
  2412. (1, children.length));
  2413. for(int counter = children.length - 1; counter >= 0; counter--) {
  2414. rPath = parent.pathByAddingChild(children[counter]);
  2415. if(expandedState.get(rPath) != null)
  2416. toRemove.addElement(rPath);
  2417. }
  2418. if(toRemove.size() > 0)
  2419. removeDescendantToggledPaths(toRemove.elements());
  2420. TreeModel model = getModel();
  2421. if(model == null || model.isLeaf(parent.getLastPathComponent()))
  2422. expandedState.remove(parent);
  2423. }
  2424. }
  2425. /**
  2426. * DynamicUtilTreeNode can wrap vectors/hashtables/arrays/strings and
  2427. * create the appropriate children tree nodes as necessary. It is
  2428. * dynamic in that it'll only create the children as necessary.
  2429. * <p>
  2430. * <strong>Warning:</strong>
  2431. * Serialized objects of this class will not be compatible with
  2432. * future Swing releases. The current serialization support is appropriate
  2433. * for short term storage or RMI between applications running the same
  2434. * version of Swing. A future release of Swing will provide support for
  2435. * long term persistence.
  2436. */
  2437. public static class DynamicUtilTreeNode extends DefaultMutableTreeNode {
  2438. /* Does the receiver have children? */
  2439. protected boolean hasChildren;
  2440. /** Value to create children with. */
  2441. protected Object childValue;
  2442. /* Have the children been loaded yet? */
  2443. protected boolean loadedChildren;
  2444. /**
  2445. * Adds to parent all the children in <code>children</code>.
  2446. * If <code>children</code> is an array or Vector all of its
  2447. * elements are added is children, otherwise if <code>children</code>
  2448. * is a Hashtable all the key/value pairs are added in the order
  2449. * Enumeration returns them.
  2450. */
  2451. public static void createChildren(DefaultMutableTreeNode parent,
  2452. Object children) {
  2453. if(children instanceof Vector) {
  2454. Vector childVector = (Vector)children;
  2455. for(int counter = 0, maxCounter = childVector.size();
  2456. counter < maxCounter; counter++)
  2457. parent.add(new DynamicUtilTreeNode
  2458. (childVector.elementAt(counter),
  2459. childVector.elementAt(counter)));
  2460. }
  2461. else if(children instanceof Hashtable) {
  2462. Hashtable childHT = (Hashtable)children;
  2463. Enumeration keys = childHT.keys();
  2464. Object aKey;
  2465. while(keys.hasMoreElements()) {
  2466. aKey = keys.nextElement();
  2467. parent.add(new DynamicUtilTreeNode(aKey,
  2468. childHT.get(aKey)));
  2469. }
  2470. }
  2471. else if(children instanceof Object[]) {
  2472. Object[] childArray = (Object[])children;
  2473. for(int counter = 0, maxCounter = childArray.length;
  2474. counter < maxCounter; counter++)
  2475. parent.add(new DynamicUtilTreeNode(childArray[counter],
  2476. childArray[counter]));
  2477. }
  2478. }
  2479. /**
  2480. * Creates a node with the specified object as its value and
  2481. * with the specified children. For the node to allow children,
  2482. * the children-object must be an array of objects, a Vector,
  2483. * or a Hashtable -- even if empty. Otherwise, the node is not
  2484. * allowed to have children.
  2485. *
  2486. * @param value the Object that is the value for the new node
  2487. * @param children an array of Objects, a Vector, or a Hashtable
  2488. * used to create the child nodes. If any other
  2489. * object is specified, or if the value is null,
  2490. * then the node is not allowed to have children.
  2491. */
  2492. public DynamicUtilTreeNode(Object value, Object children) {
  2493. super(value);
  2494. loadedChildren = false;
  2495. childValue = children;
  2496. if(children != null) {
  2497. if(children instanceof Vector)
  2498. setAllowsChildren(true);
  2499. else if(children instanceof Hashtable)
  2500. setAllowsChildren(true);
  2501. else if(children instanceof Object[])
  2502. setAllowsChildren(true);
  2503. else
  2504. setAllowsChildren(false);
  2505. }
  2506. else
  2507. setAllowsChildren(false);
  2508. }
  2509. /**
  2510. * Returns true if this node allows children. Whether the node
  2511. * allows children depends on how it was created.
  2512. *
  2513. * @return true if this node allows children, false otherwise.
  2514. * @see JTree.DynamicUtilTreeNode#DynamicUtilTreeNode(Object, Object)
  2515. */
  2516. public boolean isLeaf() {
  2517. return !getAllowsChildren();
  2518. }
  2519. /**
  2520. * Returns the number of child nodes.
  2521. *
  2522. * @return the number of child nodes
  2523. */
  2524. public int getChildCount() {
  2525. if(!loadedChildren)
  2526. loadChildren();
  2527. return super.getChildCount();
  2528. }
  2529. /**
  2530. * Loads the children based on childValue. If childValue is
  2531. * a Vector orarray each element added as a child, if childValue
  2532. * is a Hashtable each key/value pair is added in the order that
  2533. * Enumeration returns the keys.
  2534. */
  2535. protected void loadChildren() {
  2536. loadedChildren = true;
  2537. createChildren(this, childValue);
  2538. }
  2539. /**
  2540. * Subclassed to load the children, if necessary.
  2541. */
  2542. public TreeNode getChildAt(int index) {
  2543. if(!loadedChildren)
  2544. loadChildren();
  2545. return super.getChildAt(index);
  2546. }
  2547. /**
  2548. * Subclassed to load the children, if necessary.
  2549. */
  2550. public Enumeration children() {
  2551. if(!loadedChildren)
  2552. loadChildren();
  2553. return super.children();
  2554. }
  2555. }
  2556. /**
  2557. * Returns a string representation of this JTree. This method
  2558. * is intended to be used only for debugging purposes, and the
  2559. * content and format of the returned string may vary between
  2560. * implementations. The returned string may be empty but may not
  2561. * be <code>null</code>.
  2562. *
  2563. * @return a string representation of this JTree.
  2564. */
  2565. protected String paramString() {
  2566. String rootVisibleString = (rootVisible ?
  2567. "true" : "false");
  2568. String showsRootHandlesString = (showsRootHandles ?
  2569. "true" : "false");
  2570. String editableString = (editable ?
  2571. "true" : "false");
  2572. String largeModelString = (largeModel ?
  2573. "true" : "false");
  2574. String invokesStopCellEditingString = (invokesStopCellEditing ?
  2575. "true" : "false");
  2576. String scrollsOnExpandString = (scrollsOnExpand ?
  2577. "true" : "false");
  2578. return super.paramString() +
  2579. ",editable=" + editableString +
  2580. ",invokesStopCellEditing=" + invokesStopCellEditingString +
  2581. ",largeModel=" + largeModelString +
  2582. ",rootVisible=" + rootVisibleString +
  2583. ",rowHeight=" + rowHeight +
  2584. ",scrollsOnExpand=" + scrollsOnExpandString +
  2585. ",showsRootHandles=" + showsRootHandlesString +
  2586. ",toggleClickCount=" + toggleClickCount +
  2587. ",visibleRowCount=" + visibleRowCount;
  2588. }
  2589. /////////////////
  2590. // Accessibility support
  2591. ////////////////
  2592. /**
  2593. * Get the AccessibleContext associated with this JComponent
  2594. *
  2595. * @return the AccessibleContext of this JComponent
  2596. */
  2597. public AccessibleContext getAccessibleContext() {
  2598. if (accessibleContext == null) {
  2599. accessibleContext = new AccessibleJTree();
  2600. }
  2601. return accessibleContext;
  2602. }
  2603. /**
  2604. * The class used to obtain the accessible role for this object.
  2605. * <p>
  2606. * <strong>Warning:</strong>
  2607. * Serialized objects of this class will not be compatible with
  2608. * future Swing releases. The current serialization support is appropriate
  2609. * for short term storage or RMI between applications running the same
  2610. * version of Swing. A future release of Swing will provide support for
  2611. * long term persistence.
  2612. */
  2613. protected class AccessibleJTree extends AccessibleJComponent
  2614. implements AccessibleSelection, TreeSelectionListener,
  2615. TreeModelListener, TreeExpansionListener {
  2616. TreePath leadSelectionPath;
  2617. Accessible leadSelectionAccessible;
  2618. public AccessibleJTree() {
  2619. // Add a tree model listener for JTree
  2620. JTree.this.getModel().addTreeModelListener(this);
  2621. JTree.this.addTreeExpansionListener(this);
  2622. JTree.this.addTreeSelectionListener(this);
  2623. leadSelectionPath = JTree.this.getLeadSelectionPath();
  2624. leadSelectionAccessible = (leadSelectionPath != null)
  2625. ? new AccessibleJTreeNode(JTree.this,
  2626. leadSelectionPath,
  2627. JTree.this)
  2628. : null;
  2629. }
  2630. /**
  2631. * Tree Selection Listener value change method. Used to fire the
  2632. * property change
  2633. *
  2634. * @param e ListSelectionEvent
  2635. *
  2636. */
  2637. public void valueChanged(TreeSelectionEvent e) {
  2638. TreePath oldLeadSelectionPath = leadSelectionPath;
  2639. leadSelectionPath = JTree.this.getLeadSelectionPath();
  2640. if (oldLeadSelectionPath != leadSelectionPath) {
  2641. Accessible oldLSA = leadSelectionAccessible;
  2642. leadSelectionAccessible = (leadSelectionPath != null)
  2643. ? new AccessibleJTreeNode(JTree.this,
  2644. leadSelectionPath,
  2645. JTree.this)
  2646. : null;
  2647. firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
  2648. oldLSA, leadSelectionAccessible);
  2649. }
  2650. firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
  2651. new Boolean(false), new Boolean(true));
  2652. }
  2653. /**
  2654. * Fire a visible data property change notification.
  2655. * A 'visible' data property is that it represents
  2656. * something about the way the component appears on the
  2657. * display, where that appearance isn't bound to any other
  2658. * property. It notifies screen readers that the visual
  2659. * appearance of the component has changed, so they can
  2660. * notify the user.
  2661. */
  2662. public void fireVisibleDataPropertyChange() {
  2663. firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  2664. new Boolean(false), new Boolean(true));
  2665. }
  2666. // Fire the visible data changes for the model changes.
  2667. /**
  2668. * Tree Model Node change notification.
  2669. *
  2670. * @param e a Tree Model event
  2671. */
  2672. public void treeNodesChanged(TreeModelEvent e) {
  2673. fireVisibleDataPropertyChange();
  2674. }
  2675. /**
  2676. * Tree Model Node change notification.
  2677. *
  2678. * @param e a Tree node insertion event
  2679. */
  2680. public void treeNodesInserted(TreeModelEvent e) {
  2681. fireVisibleDataPropertyChange();
  2682. }
  2683. /**
  2684. * Tree Model Node change notification.
  2685. *
  2686. * @param e a Tree node(s) removal event
  2687. */
  2688. public void treeNodesRemoved(TreeModelEvent e) {
  2689. fireVisibleDataPropertyChange();
  2690. }
  2691. /**
  2692. * Tree Model structure change change notification.
  2693. *
  2694. * @param e a Tree Model event
  2695. */
  2696. public void treeStructureChanged(TreeModelEvent e) {
  2697. fireVisibleDataPropertyChange();
  2698. }
  2699. /**
  2700. * Tree Collapsed notification.
  2701. *
  2702. * @param e a TreeExpansionEvent
  2703. */
  2704. public void treeCollapsed(TreeExpansionEvent e) {
  2705. fireVisibleDataPropertyChange();
  2706. }
  2707. /**
  2708. * Tree Model Expansion notification.
  2709. *
  2710. * @param e a Tree node insertion event
  2711. */
  2712. public void treeExpanded(TreeExpansionEvent e) {
  2713. fireVisibleDataPropertyChange();
  2714. }
  2715. private AccessibleContext getCurrentAccessibleContext() {
  2716. Component c = getCurrentComponent();
  2717. if (c instanceof Accessible) {
  2718. return (((Accessible) c).getAccessibleContext());
  2719. } else {
  2720. return null;
  2721. }
  2722. }
  2723. private Component getCurrentComponent() {
  2724. // is the object visible?
  2725. // if so, get row, selected, focus & leaf state,
  2726. // and then get the renderer component and return it
  2727. TreeModel model = JTree.this.getModel();
  2728. TreePath path = new TreePath(model.getRoot());
  2729. if (JTree.this.isVisible(path)) {
  2730. TreeCellRenderer r = JTree.this.getCellRenderer();
  2731. TreeUI ui = JTree.this.getUI();
  2732. if (ui != null) {
  2733. int row = ui.getRowForPath(JTree.this, path);
  2734. int lsr = JTree.this.getLeadSelectionRow();
  2735. boolean hasFocus = JTree.this.hasFocus()
  2736. && (lsr == row);
  2737. boolean selected = JTree.this.isPathSelected(path);
  2738. boolean expanded = JTree.this.isExpanded(path);
  2739. return r.getTreeCellRendererComponent(JTree.this,
  2740. model.getRoot(), selected, expanded,
  2741. model.isLeaf(model.getRoot()), row, hasFocus);
  2742. }
  2743. }
  2744. return null;
  2745. }
  2746. // Overridden methods from AccessibleJComponent
  2747. /**
  2748. * Get the role of this object.
  2749. *
  2750. * @return an instance of AccessibleRole describing the role of the
  2751. * object
  2752. * @see AccessibleRole
  2753. */
  2754. public AccessibleRole getAccessibleRole() {
  2755. return AccessibleRole.TREE;
  2756. }
  2757. /**
  2758. * Returns the Accessible child, if one exists, contained at the local
  2759. * coordinate Point.
  2760. *
  2761. * @param p point in local coordinates of the this Accessible
  2762. * @return the Accessible, if it exists, at the specified location;
  2763. * else null
  2764. */
  2765. public Accessible getAccessibleAt(Point p) {
  2766. TreePath path = getClosestPathForLocation(p.x, p.y);
  2767. if (path != null) {
  2768. // JTree.this is NOT the parent; parent will get computed later
  2769. return new AccessibleJTreeNode(JTree.this, path, null);
  2770. } else {
  2771. return null;
  2772. }
  2773. }
  2774. /**
  2775. * Returns the number of top-level children nodes of this
  2776. * JTree. Each of these nodes may in turn have children nodes.
  2777. *
  2778. * @return the number of accessible children nodes in the tree.
  2779. */
  2780. public int getAccessibleChildrenCount() {
  2781. TreeModel model = JTree.this.getModel();
  2782. if (model != null) {
  2783. return 1;
  2784. } else {
  2785. return 0;
  2786. }
  2787. }
  2788. /**
  2789. * Return the nth Accessible child of the object.
  2790. *
  2791. * @param i zero-based index of child
  2792. * @return the nth Accessible child of the object
  2793. */
  2794. public Accessible getAccessibleChild(int i) {
  2795. TreeModel model = JTree.this.getModel();
  2796. if (model != null) {
  2797. if (i != 0) {
  2798. return null;
  2799. } else {
  2800. Object[] objPath = {model.getRoot()};
  2801. TreePath path = new TreePath(objPath);
  2802. return new AccessibleJTreeNode(JTree.this, path,
  2803. JTree.this);
  2804. }
  2805. }
  2806. return null;
  2807. }
  2808. /**
  2809. * Get the index of this object in its accessible parent.
  2810. *
  2811. * @return the index of this object in its parent. Since a JTree
  2812. * top-level object does not have an accessible parent.
  2813. * @see #getAccessibleParent
  2814. */
  2815. public int getAccessibleIndexInParent() {
  2816. return 0;
  2817. }
  2818. // AccessibleSelection methods
  2819. public AccessibleSelection getAccessibleSelection() {
  2820. return this;
  2821. }
  2822. /**
  2823. * Returns the number of items currently selected.
  2824. * If no items are selected, the return value will be 0.
  2825. *
  2826. * @return the number of items currently selected.
  2827. */
  2828. public int getAccessibleSelectionCount() {
  2829. return JTree.this.getSelectionCount();
  2830. }
  2831. /**
  2832. * Returns an Accessible representing the specified selected item
  2833. * in the object. If there isn't a selection, or there are
  2834. * fewer items selcted than the integer passed in, the return
  2835. * value will be null.
  2836. *
  2837. * @param i the zero-based index of selected items
  2838. * @return an Accessible containing the selected item
  2839. */
  2840. public Accessible getAccessibleSelection(int i) {
  2841. TreePath[] paths = JTree.this.getSelectionPaths();
  2842. if (i < 0 || i >= paths.length) {
  2843. return null;
  2844. } else {
  2845. return new AccessibleJTreeNode(JTree.this, paths[i], JTree.this);
  2846. }
  2847. }
  2848. /**
  2849. * Returns true if the current child of this object is selected.
  2850. *
  2851. * @param i the zero-based index of the child in this Accessible object.
  2852. * @see AccessibleContext#getAccessibleChild
  2853. */
  2854. public boolean isAccessibleChildSelected(int i) {
  2855. TreePath[] paths = JTree.this.getSelectionPaths();
  2856. TreeModel treeModel = JTree.this.getModel();
  2857. Object o;
  2858. for (int j = 0; j < paths.length; j++) {
  2859. o = paths[j].getLastPathComponent();
  2860. if (i == treeModel.getIndexOfChild(treeModel.getRoot(), o)) {
  2861. return true;
  2862. }
  2863. }
  2864. return false;
  2865. }
  2866. /**
  2867. * Adds the specified selected item in the object to the object's
  2868. * selection. If the object supports multiple selections,
  2869. * the specified item is added to any existing selection, otherwise
  2870. * it replaces any existing selection in the object. If the
  2871. * specified item is already selected, this method has no effect.
  2872. *
  2873. * @param i the zero-based index of selectable items
  2874. */
  2875. public void addAccessibleSelection(int i) {
  2876. TreeModel model = JTree.this.getModel();
  2877. if (model != null) {
  2878. if (i == 0) {
  2879. Object[] objPath = {model.getRoot()};
  2880. TreePath path = new TreePath(objPath);
  2881. JTree.this.addSelectionPath(path);
  2882. }
  2883. }
  2884. }
  2885. /**
  2886. * Removes the specified selected item in the object from the object's
  2887. * selection. If the specified item isn't currently selected, this
  2888. * method has no effect.
  2889. *
  2890. * @param i the zero-based index of selectable items
  2891. */
  2892. public void removeAccessibleSelection(int i) {
  2893. TreeModel model = JTree.this.getModel();
  2894. if (model != null) {
  2895. if (i == 0) {
  2896. Object[] objPath = {model.getRoot()};
  2897. TreePath path = new TreePath(objPath);
  2898. JTree.this.removeSelectionPath(path);
  2899. }
  2900. }
  2901. }
  2902. /**
  2903. * Clears the selection in the object, so that nothing in the
  2904. * object is selected.
  2905. */
  2906. public void clearAccessibleSelection() {
  2907. int childCount = getAccessibleChildrenCount();
  2908. for (int i = 0; i < childCount; i++) {
  2909. removeAccessibleSelection(i);
  2910. }
  2911. }
  2912. /**
  2913. * Causes every selected item in the object to be selected
  2914. * if the object supports multiple selections.
  2915. */
  2916. public void selectAllAccessibleSelection() {
  2917. TreeModel model = JTree.this.getModel();
  2918. if (model != null) {
  2919. Object[] objPath = {model.getRoot()};
  2920. TreePath path = new TreePath(objPath);
  2921. JTree.this.addSelectionPath(path);
  2922. }
  2923. }
  2924. /**
  2925. *
  2926. */
  2927. protected class AccessibleJTreeNode extends AccessibleContext
  2928. implements Accessible, AccessibleComponent, AccessibleSelection,
  2929. AccessibleAction {
  2930. private JTree tree = null;
  2931. private TreeModel treeModel = null;
  2932. private Object obj = null;
  2933. private TreePath path = null;
  2934. private Accessible accessibleParent = null;
  2935. private int index = 0;
  2936. private boolean isLeaf = false;
  2937. /**
  2938. * Constructs an AccessibleJTreeNode
  2939. */
  2940. public AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) {
  2941. tree = t;
  2942. path = p;
  2943. accessibleParent = ap;
  2944. treeModel = t.getModel();
  2945. obj = p.getLastPathComponent();
  2946. if (treeModel != null) {
  2947. isLeaf = treeModel.isLeaf(obj);
  2948. }
  2949. }
  2950. private TreePath getChildTreePath(int i) {
  2951. // Tree nodes can't be so complex that they have
  2952. // two sets of children -> we're ignoring that case
  2953. if (i < 0 || i >= getAccessibleChildrenCount()) {
  2954. return null;
  2955. } else {
  2956. Object childObj = treeModel.getChild(obj, i);
  2957. Object[] objPath = path.getPath();
  2958. Object[] objChildPath = new Object[objPath.length+1];
  2959. java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
  2960. objChildPath[objChildPath.length-1] = childObj;
  2961. return new TreePath(objChildPath);
  2962. }
  2963. }
  2964. /**
  2965. * Get the AccessibleContext associated with this tree node
  2966. *
  2967. * @return the AccessibleContext of this JComponent
  2968. */
  2969. public AccessibleContext getAccessibleContext() {
  2970. return this;
  2971. }
  2972. private AccessibleContext getCurrentAccessibleContext() {
  2973. Component c = getCurrentComponent();
  2974. if (c instanceof Accessible) {
  2975. return (((Accessible) c).getAccessibleContext());
  2976. } else {
  2977. return null;
  2978. }
  2979. }
  2980. private Component getCurrentComponent() {
  2981. // is the object visible?
  2982. // if so, get row, selected, focus & leaf state,
  2983. // and then get the renderer component and return it
  2984. if (tree.isVisible(path)) {
  2985. TreeCellRenderer r = tree.getCellRenderer();
  2986. if (r == null) {
  2987. return null;
  2988. }
  2989. TreeUI ui = tree.getUI();
  2990. if (ui != null) {
  2991. int row = ui.getRowForPath(JTree.this, path);
  2992. boolean selected = tree.isPathSelected(path);
  2993. boolean expanded = tree.isExpanded(path);
  2994. boolean hasFocus = false; // how to tell?? -PK
  2995. return r.getTreeCellRendererComponent(tree, obj,
  2996. selected, expanded, isLeaf, row, hasFocus);
  2997. }
  2998. }
  2999. return null;
  3000. }
  3001. // AccessibleContext methods
  3002. /**
  3003. * Get the accessible name of this object.
  3004. *
  3005. * @return the localized name of the object; null if this
  3006. * object does not have a name
  3007. */
  3008. public String getAccessibleName() {
  3009. AccessibleContext ac = getCurrentAccessibleContext();
  3010. if (ac != null) {
  3011. String name = ac.getAccessibleName();
  3012. if ((name != null) && (name != "")) {
  3013. return ac.getAccessibleName();
  3014. } else {
  3015. return null;
  3016. }
  3017. }
  3018. if ((accessibleName != null) && (accessibleName != "")) {
  3019. return accessibleName;
  3020. } else {
  3021. return null;
  3022. }
  3023. }
  3024. /**
  3025. * Set the localized accessible name of this object.
  3026. *
  3027. * @param s the new localized name of the object.
  3028. */
  3029. public void setAccessibleName(String s) {
  3030. AccessibleContext ac = getCurrentAccessibleContext();
  3031. if (ac != null) {
  3032. ac.setAccessibleName(s);
  3033. } else {
  3034. super.setAccessibleName(s);
  3035. }
  3036. }
  3037. //
  3038. // *** should check toolip text for desc. (needs MouseEvent)
  3039. //
  3040. /**
  3041. * Get the accessible description of this object.
  3042. *
  3043. * @return the localized description of the object; null if
  3044. * this object does not have a description
  3045. */
  3046. public String getAccessibleDescription() {
  3047. AccessibleContext ac = getCurrentAccessibleContext();
  3048. if (ac != null) {
  3049. return ac.getAccessibleDescription();
  3050. } else {
  3051. return super.getAccessibleDescription();
  3052. }
  3053. }
  3054. /**
  3055. * Set the accessible description of this object.
  3056. *
  3057. * @param s the new localized description of the object
  3058. */
  3059. public void setAccessibleDescription(String s) {
  3060. AccessibleContext ac = getCurrentAccessibleContext();
  3061. if (ac != null) {
  3062. ac.setAccessibleDescription(s);
  3063. } else {
  3064. super.setAccessibleDescription(s);
  3065. }
  3066. }
  3067. /**
  3068. * Get the role of this object.
  3069. *
  3070. * @return an instance of AccessibleRole describing the role of the object
  3071. * @see AccessibleRole
  3072. */
  3073. public AccessibleRole getAccessibleRole() {
  3074. AccessibleContext ac = getCurrentAccessibleContext();
  3075. if (ac != null) {
  3076. return ac.getAccessibleRole();
  3077. } else {
  3078. return AccessibleRole.UNKNOWN;
  3079. }
  3080. }
  3081. /**
  3082. * Get the state set of this object.
  3083. *
  3084. * @return an instance of AccessibleStateSet containing the
  3085. * current state set of the object
  3086. * @see AccessibleState
  3087. */
  3088. public AccessibleStateSet getAccessibleStateSet() {
  3089. AccessibleContext ac = getCurrentAccessibleContext();
  3090. AccessibleStateSet states;
  3091. int row = tree.getUI().getRowForPath(tree,path);
  3092. int lsr = tree.getLeadSelectionRow();
  3093. if (ac != null) {
  3094. states = ac.getAccessibleStateSet();
  3095. } else {
  3096. states = new AccessibleStateSet();
  3097. }
  3098. // need to test here, 'cause the underlying component
  3099. // is a cellRenderer, which is never showing...
  3100. if (isShowing()) {
  3101. states.add(AccessibleState.SHOWING);
  3102. } else if (states.contains(AccessibleState.SHOWING)) {
  3103. states.remove(AccessibleState.SHOWING);
  3104. }
  3105. if (isVisible()) {
  3106. states.add(AccessibleState.VISIBLE);
  3107. } else if (states.contains(AccessibleState.VISIBLE)) {
  3108. states.remove(AccessibleState.VISIBLE);
  3109. }
  3110. if (tree.isPathSelected(path)){
  3111. states.add(AccessibleState.SELECTED);
  3112. }
  3113. if (lsr == row) {
  3114. states.add(AccessibleState.ACTIVE);
  3115. }
  3116. if (!isLeaf) {
  3117. states.add(AccessibleState.EXPANDABLE);
  3118. }
  3119. if (tree.isExpanded(path)) {
  3120. states.add(AccessibleState.EXPANDED);
  3121. } else {
  3122. states.add(AccessibleState.COLLAPSED);
  3123. }
  3124. if (tree.isEditable()) {
  3125. states.add(AccessibleState.EDITABLE);
  3126. }
  3127. return states;
  3128. }
  3129. /**
  3130. * Get the Accessible parent of this object.
  3131. *
  3132. * @return the Accessible parent of this object; null if this
  3133. * object does not have an Accessible parent
  3134. */
  3135. public Accessible getAccessibleParent() {
  3136. // someone wants to know, so we need to create our parent
  3137. // if we don't have one (hey, we're a talented kid!)
  3138. if (accessibleParent == null) {
  3139. Object[] objPath = path.getPath();
  3140. if (objPath.length > 1) {
  3141. Object objParent = objPath[objPath.length-2];
  3142. if (treeModel != null) {
  3143. index = treeModel.getIndexOfChild(objParent, obj);
  3144. }
  3145. Object[] objParentPath = new Object[objPath.length-1];
  3146. java.lang.System.arraycopy(objPath, 0, objParentPath,
  3147. 0, objPath.length-1);
  3148. TreePath parentPath = new TreePath(objParentPath);
  3149. accessibleParent = new AccessibleJTreeNode(tree,
  3150. parentPath,
  3151. null);
  3152. this.setAccessibleParent(accessibleParent);
  3153. } else if (treeModel != null) {
  3154. accessibleParent = tree; // we're the top!
  3155. index = 0; // we're an only child!
  3156. // TreePath parentPath = new TreePath(treeModel.getRoot());
  3157. // accessibleParent = new AccessibleJTreeNode(tree,
  3158. // parentPath,
  3159. // null);
  3160. this.setAccessibleParent(accessibleParent);
  3161. }
  3162. }
  3163. return accessibleParent;
  3164. }
  3165. /**
  3166. * Get the index of this object in its accessible parent.
  3167. *
  3168. * @return the index of this object in its parent; -1 if this
  3169. * object does not have an accessible parent.
  3170. * @see #getAccessibleParent
  3171. */
  3172. public int getAccessibleIndexInParent() {
  3173. // index is invalid 'till we have an accessibleParent...
  3174. if (accessibleParent == null) {
  3175. getAccessibleParent();
  3176. }
  3177. return index;
  3178. }
  3179. /**
  3180. * Returns the number of accessible children in the object.
  3181. *
  3182. * @return the number of accessible children in the object.
  3183. */
  3184. public int getAccessibleChildrenCount() {
  3185. // Tree nodes can't be so complex that they have
  3186. // two sets of children -> we're ignoring that case
  3187. return treeModel.getChildCount(obj);
  3188. }
  3189. /**
  3190. * Return the specified Accessible child of the object.
  3191. *
  3192. * @param i zero-based index of child
  3193. * @return the Accessible child of the object
  3194. */
  3195. public Accessible getAccessibleChild(int i) {
  3196. // Tree nodes can't be so complex that they have
  3197. // two sets of children -> we're ignoring that case
  3198. if (i < 0 || i >= getAccessibleChildrenCount()) {
  3199. return null;
  3200. } else {
  3201. Object childObj = treeModel.getChild(obj, i);
  3202. Object[] objPath = path.getPath();
  3203. Object[] objChildPath = new Object[objPath.length+1];
  3204. java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
  3205. objChildPath[objChildPath.length-1] = childObj;
  3206. TreePath childPath = new TreePath(objChildPath);
  3207. return new AccessibleJTreeNode(JTree.this, childPath, this);
  3208. }
  3209. }
  3210. /**
  3211. * Gets the locale of the component. If the component does not have
  3212. * a locale, then the locale of its parent is returned.
  3213. *
  3214. * @return This component's locale. If this component does not have
  3215. * a locale, the locale of its parent is returned.
  3216. * @exception IllegalComponentStateException
  3217. * If the Component does not have its own locale and has not yet
  3218. * been added to a containment hierarchy such that the locale can be
  3219. * determined from the containing parent.
  3220. * @see #setLocale
  3221. */
  3222. public Locale getLocale() {
  3223. AccessibleContext ac = getCurrentAccessibleContext();
  3224. if (ac != null) {
  3225. return ac.getLocale();
  3226. } else {
  3227. return tree.getLocale();
  3228. }
  3229. }
  3230. /**
  3231. * Add a PropertyChangeListener to the listener list.
  3232. * The listener is registered for all properties.
  3233. *
  3234. * @param listener The PropertyChangeListener to be added
  3235. */
  3236. public void addPropertyChangeListener(PropertyChangeListener l) {
  3237. AccessibleContext ac = getCurrentAccessibleContext();
  3238. if (ac != null) {
  3239. ac.addPropertyChangeListener(l);
  3240. } else {
  3241. super.addPropertyChangeListener(l);
  3242. }
  3243. }
  3244. /**
  3245. * Remove a PropertyChangeListener from the listener list.
  3246. * This removes a PropertyChangeListener that was registered
  3247. * for all properties.
  3248. *
  3249. * @param listener The PropertyChangeListener to be removed
  3250. */
  3251. public void removePropertyChangeListener(PropertyChangeListener l) {
  3252. AccessibleContext ac = getCurrentAccessibleContext();
  3253. if (ac != null) {
  3254. ac.removePropertyChangeListener(l);
  3255. } else {
  3256. super.removePropertyChangeListener(l);
  3257. }
  3258. }
  3259. /**
  3260. * Get the AccessibleAction associated with this object if one
  3261. * exists. Otherwise return null.
  3262. *
  3263. * @return the AccessibleAction, or null
  3264. */
  3265. public AccessibleAction getAccessibleAction() {
  3266. return this;
  3267. }
  3268. /**
  3269. * Get the AccessibleComponent associated with this tree node
  3270. * NOTE: if the node is not displayed (either scrolled off of
  3271. * the screen, or not expanded), this will return null
  3272. *
  3273. * @return the AccessibleComponent of this tree node
  3274. */
  3275. public AccessibleComponent getAccessibleComponent() {
  3276. return this; // to override getBounds()
  3277. }
  3278. /**
  3279. * Get the AccessibleSelection associated with this object if one
  3280. * exists. Otherwise return null.
  3281. *
  3282. * @return the AccessibleSelection, or null
  3283. */
  3284. public AccessibleSelection getAccessibleSelection() {
  3285. AccessibleContext ac = getCurrentAccessibleContext();
  3286. if (ac != null && isLeaf) {
  3287. return getCurrentAccessibleContext().getAccessibleSelection();
  3288. } else {
  3289. return this;
  3290. }
  3291. }
  3292. /**
  3293. * Get the AccessibleText associated with this object if one
  3294. * exists. Otherwise return null.
  3295. *
  3296. * @return the AccessibleText, or null
  3297. */
  3298. public AccessibleText getAccessibleText() {
  3299. AccessibleContext ac = getCurrentAccessibleContext();
  3300. if (ac != null) {
  3301. return getCurrentAccessibleContext().getAccessibleText();
  3302. } else {
  3303. return null;
  3304. }
  3305. }
  3306. /**
  3307. * Get the AccessibleValue associated with this object if one
  3308. * exists. Otherwise return null.
  3309. *
  3310. * @return the AccessibleValue, or null
  3311. */
  3312. public AccessibleValue getAccessibleValue() {
  3313. AccessibleContext ac = getCurrentAccessibleContext();
  3314. if (ac != null) {
  3315. return getCurrentAccessibleContext().getAccessibleValue();
  3316. } else {
  3317. return null;
  3318. }
  3319. }
  3320. // AccessibleComponent methods
  3321. /**
  3322. * Get the background color of this object.
  3323. *
  3324. * @return the background color, if supported, of the object;
  3325. * otherwise, null
  3326. */
  3327. public Color getBackground() {
  3328. AccessibleContext ac = getCurrentAccessibleContext();
  3329. if (ac instanceof AccessibleComponent) {
  3330. return ((AccessibleComponent) ac).getBackground();
  3331. } else {
  3332. Component c = getCurrentComponent();
  3333. if (c != null) {
  3334. return c.getBackground();
  3335. } else {
  3336. return null;
  3337. }
  3338. }
  3339. }
  3340. /**
  3341. * Set the background color of this object.
  3342. *
  3343. * @param c the new Color for the background
  3344. */
  3345. public void setBackground(Color c) {
  3346. AccessibleContext ac = getCurrentAccessibleContext();
  3347. if (ac instanceof AccessibleComponent) {
  3348. ((AccessibleComponent) ac).setBackground(c);
  3349. } else {
  3350. Component cp = getCurrentComponent();
  3351. if (cp != null) {
  3352. cp.setBackground(c);
  3353. }
  3354. }
  3355. }
  3356. /**
  3357. * Get the foreground color of this object.
  3358. *
  3359. * @return the foreground color, if supported, of the object;
  3360. * otherwise, null
  3361. */
  3362. public Color getForeground() {
  3363. AccessibleContext ac = getCurrentAccessibleContext();
  3364. if (ac instanceof AccessibleComponent) {
  3365. return ((AccessibleComponent) ac).getForeground();
  3366. } else {
  3367. Component c = getCurrentComponent();
  3368. if (c != null) {
  3369. return c.getForeground();
  3370. } else {
  3371. return null;
  3372. }
  3373. }
  3374. }
  3375. public void setForeground(Color c) {
  3376. AccessibleContext ac = getCurrentAccessibleContext();
  3377. if (ac instanceof AccessibleComponent) {
  3378. ((AccessibleComponent) ac).setForeground(c);
  3379. } else {
  3380. Component cp = getCurrentComponent();
  3381. if (cp != null) {
  3382. cp.setForeground(c);
  3383. }
  3384. }
  3385. }
  3386. public Cursor getCursor() {
  3387. AccessibleContext ac = getCurrentAccessibleContext();
  3388. if (ac instanceof AccessibleComponent) {
  3389. return ((AccessibleComponent) ac).getCursor();
  3390. } else {
  3391. Component c = getCurrentComponent();
  3392. if (c != null) {
  3393. return c.getCursor();
  3394. } else {
  3395. Accessible ap = getAccessibleParent();
  3396. if (ap instanceof AccessibleComponent) {
  3397. return ((AccessibleComponent) ap).getCursor();
  3398. } else {
  3399. return null;
  3400. }
  3401. }
  3402. }
  3403. }
  3404. public void setCursor(Cursor c) {
  3405. AccessibleContext ac = getCurrentAccessibleContext();
  3406. if (ac instanceof AccessibleComponent) {
  3407. ((AccessibleComponent) ac).setCursor(c);
  3408. } else {
  3409. Component cp = getCurrentComponent();
  3410. if (cp != null) {
  3411. cp.setCursor(c);
  3412. }
  3413. }
  3414. }
  3415. public Font getFont() {
  3416. AccessibleContext ac = getCurrentAccessibleContext();
  3417. if (ac instanceof AccessibleComponent) {
  3418. return ((AccessibleComponent) ac).getFont();
  3419. } else {
  3420. Component c = getCurrentComponent();
  3421. if (c != null) {
  3422. return c.getFont();
  3423. } else {
  3424. return null;
  3425. }
  3426. }
  3427. }
  3428. public void setFont(Font f) {
  3429. AccessibleContext ac = getCurrentAccessibleContext();
  3430. if (ac instanceof AccessibleComponent) {
  3431. ((AccessibleComponent) ac).setFont(f);
  3432. } else {
  3433. Component c = getCurrentComponent();
  3434. if (c != null) {
  3435. c.setFont(f);
  3436. }
  3437. }
  3438. }
  3439. public FontMetrics getFontMetrics(Font f) {
  3440. AccessibleContext ac = getCurrentAccessibleContext();
  3441. if (ac instanceof AccessibleComponent) {
  3442. return ((AccessibleComponent) ac).getFontMetrics(f);
  3443. } else {
  3444. Component c = getCurrentComponent();
  3445. if (c != null) {
  3446. return c.getFontMetrics(f);
  3447. } else {
  3448. return null;
  3449. }
  3450. }
  3451. }
  3452. public boolean isEnabled() {
  3453. AccessibleContext ac = getCurrentAccessibleContext();
  3454. if (ac instanceof AccessibleComponent) {
  3455. return ((AccessibleComponent) ac).isEnabled();
  3456. } else {
  3457. Component c = getCurrentComponent();
  3458. if (c != null) {
  3459. return c.isEnabled();
  3460. } else {
  3461. return false;
  3462. }
  3463. }
  3464. }
  3465. public void setEnabled(boolean b) {
  3466. AccessibleContext ac = getCurrentAccessibleContext();
  3467. if (ac instanceof AccessibleComponent) {
  3468. ((AccessibleComponent) ac).setEnabled(b);
  3469. } else {
  3470. Component c = getCurrentComponent();
  3471. if (c != null) {
  3472. c.setEnabled(b);
  3473. }
  3474. }
  3475. }
  3476. public boolean isVisible() {
  3477. Rectangle pathBounds = tree.getPathBounds(path);
  3478. Rectangle parentBounds = tree.getVisibleRect();
  3479. if (pathBounds != null && parentBounds != null &&
  3480. parentBounds.intersects(pathBounds)) {
  3481. return true;
  3482. } else {
  3483. return false;
  3484. }
  3485. }
  3486. public void setVisible(boolean b) {
  3487. }
  3488. public boolean isShowing() {
  3489. return (tree.isShowing() && isVisible());
  3490. }
  3491. public boolean contains(Point p) {
  3492. AccessibleContext ac = getCurrentAccessibleContext();
  3493. if (ac instanceof AccessibleComponent) {
  3494. Rectangle r = ((AccessibleComponent) ac).getBounds();
  3495. return r.contains(p);
  3496. } else {
  3497. Component c = getCurrentComponent();
  3498. if (c != null) {
  3499. Rectangle r = c.getBounds();
  3500. return r.contains(p);
  3501. } else {
  3502. return getBounds().contains(p);
  3503. }
  3504. }
  3505. }
  3506. public Point getLocationOnScreen() {
  3507. if (tree != null) {
  3508. Point parentLocation = tree.getLocationOnScreen();
  3509. Point componentLocation = getLocation();
  3510. componentLocation.translate(parentLocation.x, parentLocation.y);
  3511. return componentLocation;
  3512. } else {
  3513. return null;
  3514. }
  3515. }
  3516. protected Point getLocationInJTree() {
  3517. Rectangle r = tree.getPathBounds(path);
  3518. if (r != null) {
  3519. return r.getLocation();
  3520. } else {
  3521. return null;
  3522. }
  3523. }
  3524. public Point getLocation() {
  3525. Rectangle r = getBounds();
  3526. if (r != null) {
  3527. return r.getLocation();
  3528. } else {
  3529. return null;
  3530. }
  3531. }
  3532. public void setLocation(Point p) {
  3533. }
  3534. public Rectangle getBounds() {
  3535. Rectangle r = tree.getPathBounds(path);
  3536. Accessible parent = getAccessibleParent();
  3537. if (parent != null) {
  3538. if (parent instanceof AccessibleJTreeNode) {
  3539. Point parentLoc = ((AccessibleJTreeNode) parent).getLocationInJTree();
  3540. if (parentLoc != null && r != null) {
  3541. r.translate(-parentLoc.x, -parentLoc.y);
  3542. } else {
  3543. return null; // not visible!
  3544. }
  3545. }
  3546. }
  3547. return r;
  3548. }
  3549. public void setBounds(Rectangle r) {
  3550. AccessibleContext ac = getCurrentAccessibleContext();
  3551. if (ac instanceof AccessibleComponent) {
  3552. ((AccessibleComponent) ac).setBounds(r);
  3553. } else {
  3554. Component c = getCurrentComponent();
  3555. if (c != null) {
  3556. c.setBounds(r);
  3557. }
  3558. }
  3559. }
  3560. public Dimension getSize() {
  3561. return getBounds().getSize();
  3562. }
  3563. public void setSize (Dimension d) {
  3564. AccessibleContext ac = getCurrentAccessibleContext();
  3565. if (ac instanceof AccessibleComponent) {
  3566. ((AccessibleComponent) ac).setSize(d);
  3567. } else {
  3568. Component c = getCurrentComponent();
  3569. if (c != null) {
  3570. c.setSize(d);
  3571. }
  3572. }
  3573. }
  3574. public Accessible getAccessibleAt(Point p) {
  3575. AccessibleContext ac = getCurrentAccessibleContext();
  3576. if (ac instanceof AccessibleComponent) {
  3577. return ((AccessibleComponent) ac).getAccessibleAt(p);
  3578. } else {
  3579. return null;
  3580. }
  3581. }
  3582. public boolean isFocusTraversable() {
  3583. AccessibleContext ac = getCurrentAccessibleContext();
  3584. if (ac instanceof AccessibleComponent) {
  3585. return ((AccessibleComponent) ac).isFocusTraversable();
  3586. } else {
  3587. Component c = getCurrentComponent();
  3588. if (c != null) {
  3589. return c.isFocusTraversable();
  3590. } else {
  3591. return false;
  3592. }
  3593. }
  3594. }
  3595. public void requestFocus() {
  3596. AccessibleContext ac = getCurrentAccessibleContext();
  3597. if (ac instanceof AccessibleComponent) {
  3598. ((AccessibleComponent) ac).requestFocus();
  3599. } else {
  3600. Component c = getCurrentComponent();
  3601. if (c != null) {
  3602. c.requestFocus();
  3603. }
  3604. }
  3605. }
  3606. public void addFocusListener(FocusListener l) {
  3607. AccessibleContext ac = getCurrentAccessibleContext();
  3608. if (ac instanceof AccessibleComponent) {
  3609. ((AccessibleComponent) ac).addFocusListener(l);
  3610. } else {
  3611. Component c = getCurrentComponent();
  3612. if (c != null) {
  3613. c.addFocusListener(l);
  3614. }
  3615. }
  3616. }
  3617. public void removeFocusListener(FocusListener l) {
  3618. AccessibleContext ac = getCurrentAccessibleContext();
  3619. if (ac instanceof AccessibleComponent) {
  3620. ((AccessibleComponent) ac).removeFocusListener(l);
  3621. } else {
  3622. Component c = getCurrentComponent();
  3623. if (c != null) {
  3624. c.removeFocusListener(l);
  3625. }
  3626. }
  3627. }
  3628. // AccessibleSelection methods
  3629. /**
  3630. * Returns the number of items currently selected.
  3631. * If no items are selected, the return value will be 0.
  3632. *
  3633. * @return the number of items currently selected.
  3634. */
  3635. public int getAccessibleSelectionCount() {
  3636. int count = 0;
  3637. int childCount = getAccessibleChildrenCount();
  3638. for (int i = 0; i < childCount; i++) {
  3639. TreePath childPath = getChildTreePath(i);
  3640. if (tree.isPathSelected(childPath)) {
  3641. count++;
  3642. }
  3643. }
  3644. return count;
  3645. }
  3646. /**
  3647. * Returns an Accessible representing the specified selected item
  3648. * in the object. If there isn't a selection, or there are
  3649. * fewer items selcted than the integer passed in, the return
  3650. * value will be null.
  3651. *
  3652. * @param i the zero-based index of selected items
  3653. * @return an Accessible containing the selected item
  3654. */
  3655. public Accessible getAccessibleSelection(int i) {
  3656. int childCount = getAccessibleChildrenCount();
  3657. if (i < 0 || i >= childCount) {
  3658. return null; // out of range
  3659. }
  3660. int count = 0;
  3661. for (int j = 0; j < childCount && i >= count; j++) {
  3662. TreePath childPath = getChildTreePath(j);
  3663. if (tree.isPathSelected(childPath)) {
  3664. if (count == i) {
  3665. return new AccessibleJTreeNode(tree, childPath, this);
  3666. } else {
  3667. count++;
  3668. }
  3669. }
  3670. }
  3671. return null;
  3672. }
  3673. /**
  3674. * Returns true if the current child of this object is selected.
  3675. *
  3676. * @param i the zero-based index of the child in this Accessible
  3677. * object.
  3678. * @see AccessibleContext#getAccessibleChild
  3679. */
  3680. public boolean isAccessibleChildSelected(int i) {
  3681. int childCount = getAccessibleChildrenCount();
  3682. if (i < 0 || i >= childCount) {
  3683. return false; // out of range
  3684. } else {
  3685. TreePath childPath = getChildTreePath(i);
  3686. return tree.isPathSelected(childPath);
  3687. }
  3688. }
  3689. /**
  3690. * Adds the specified selected item in the object to the object's
  3691. * selection. If the object supports multiple selections,
  3692. * the specified item is added to any existing selection, otherwise
  3693. * it replaces any existing selection in the object. If the
  3694. * specified item is already selected, this method has no effect.
  3695. *
  3696. * @param i the zero-based index of selectable items
  3697. */
  3698. public void addAccessibleSelection(int i) {
  3699. TreeModel model = JTree.this.getModel();
  3700. if (model != null) {
  3701. if (i >= 0 && i < getAccessibleChildrenCount()) {
  3702. TreePath path = getChildTreePath(i);
  3703. JTree.this.addSelectionPath(path);
  3704. }
  3705. }
  3706. }
  3707. /**
  3708. * Removes the specified selected item in the object from the
  3709. * object's
  3710. * selection. If the specified item isn't currently selected, this
  3711. * method has no effect.
  3712. *
  3713. * @param i the zero-based index of selectable items
  3714. */
  3715. public void removeAccessibleSelection(int i) {
  3716. TreeModel model = JTree.this.getModel();
  3717. if (model != null) {
  3718. if (i >= 0 && i < getAccessibleChildrenCount()) {
  3719. TreePath path = getChildTreePath(i);
  3720. JTree.this.removeSelectionPath(path);
  3721. }
  3722. }
  3723. }
  3724. /**
  3725. * Clears the selection in the object, so that nothing in the
  3726. * object is selected.
  3727. */
  3728. public void clearAccessibleSelection() {
  3729. int childCount = getAccessibleChildrenCount();
  3730. for (int i = 0; i < childCount; i++) {
  3731. removeAccessibleSelection(i);
  3732. }
  3733. }
  3734. /**
  3735. * Causes every selected item in the object to be selected
  3736. * if the object supports multiple selections.
  3737. */
  3738. public void selectAllAccessibleSelection() {
  3739. TreeModel model = JTree.this.getModel();
  3740. if (model != null) {
  3741. int childCount = getAccessibleChildrenCount();
  3742. TreePath path;
  3743. for (int i = 0; i < childCount; i++) {
  3744. path = getChildTreePath(i);
  3745. JTree.this.addSelectionPath(path);
  3746. }
  3747. }
  3748. }
  3749. // AccessibleAction methods
  3750. /**
  3751. * Returns the number of accessible actions available in this
  3752. * tree node. If this node is not a leaf, there is at least
  3753. * one action (toggle expand), in addition to any available
  3754. * on the object behind the TreeCellRenderer.
  3755. *
  3756. * @return the number of Actions in this object
  3757. */
  3758. public int getAccessibleActionCount() {
  3759. AccessibleContext ac = getCurrentAccessibleContext();
  3760. if (ac != null) {
  3761. AccessibleAction aa = ac.getAccessibleAction();
  3762. if (aa != null) {
  3763. return (aa.getAccessibleActionCount() + (isLeaf ? 0 : 1));
  3764. }
  3765. }
  3766. return isLeaf ? 0 : 1;
  3767. }
  3768. /**
  3769. * Return a description of the specified action of the tree node.
  3770. * If this node is not a leaf, there is at least one action
  3771. * description (toggle expand), in addition to any available
  3772. * on the object behind the TreeCellRenderer.
  3773. *
  3774. * @param i zero-based index of the actions
  3775. * @return a description of the action
  3776. */
  3777. public String getAccessibleActionDescription(int i) {
  3778. if (i < 0 || i >= getAccessibleActionCount()) {
  3779. return null;
  3780. }
  3781. AccessibleContext ac = getCurrentAccessibleContext();
  3782. if (i == 0) {
  3783. return "toggle expand";
  3784. } else if (ac != null) {
  3785. AccessibleAction aa = ac.getAccessibleAction();
  3786. if (aa != null) {
  3787. return aa.getAccessibleActionDescription(i - 1);
  3788. }
  3789. }
  3790. return null;
  3791. }
  3792. /**
  3793. * Perform the specified Action on the tree node. If this node
  3794. * is not a leaf, there is at least one action which can be
  3795. * done (toggle expand), in addition to any available on the
  3796. * object behind the TreeCellRenderer.
  3797. *
  3798. * @param i zero-based index of actions
  3799. * @return true if the the action was performed; else false.
  3800. */
  3801. public boolean doAccessibleAction(int i) {
  3802. if (i < 0 || i >= getAccessibleActionCount()) {
  3803. return false;
  3804. }
  3805. AccessibleContext ac = getCurrentAccessibleContext();
  3806. if (i == 0) {
  3807. if (JTree.this.isExpanded(path)) {
  3808. JTree.this.collapsePath(path);
  3809. } else {
  3810. JTree.this.expandPath(path);
  3811. }
  3812. return true;
  3813. } else if (ac != null) {
  3814. AccessibleAction aa = ac.getAccessibleAction();
  3815. if (aa != null) {
  3816. return aa.doAccessibleAction(i - 1);
  3817. }
  3818. }
  3819. return false;
  3820. }
  3821. } // inner class AccessibleJTreeNode
  3822. } // inner class AccessibleJTree
  3823. } // End of class JTree