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