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