KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > JTree


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
8 package javax.swing;
9
10 import java.awt.*;
11 import java.awt.event.*;
12 import java.beans.*;
13 import java.io.*;
14 import java.util.*;
15 import javax.swing.event.*;
16 import javax.swing.plaf.TreeUI JavaDoc;
17 import javax.swing.tree.*;
18 import javax.swing.text.Position JavaDoc;
19 import javax.accessibility.*;
20
21
22 /**
23  * <a name="jtree_description">
24  * A control that displays a set of hierarchical data as an outline.
25  * You can find task-oriented documentation and examples of using trees in
26  * <a HREF="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>,
27  * a section in <em>The Java Tutorial.</em>
28  * <p>
29  * A specific node in a tree can be identified either by a
30  * <code>TreePath</code> (an object
31  * that encapsulates a node and all of its ancestors), or by its
32  * display row, where each row in the display area displays one node.
33  * An <i>expanded</i> node is a non-leaf node (as identified by
34  * <code>TreeModel.isLeaf(node)</code> returning false) that will displays
35  * its children when all its ancestors are <i>expanded</i>.
36  * A <i>collapsed</i>
37  * node is one which hides them. A <i>hidden</i> node is one which is
38  * under a collapsed ancestor. All of a <i>viewable</i> nodes parents
39  * are expanded, but may or may not be displayed. A <i>displayed</i> node
40  * is both viewable and in the display area, where it can be seen.
41  * <p>
42  * The following <code>JTree</code> methods use "visible" to mean "displayed":
43  * <ul>
44  * <li><code>isRootVisible()</code>
45  * <li><code>setRootVisible()</code>
46  * <li><code>scrollPathToVisible()</code>
47  * <li><code>scrollRowToVisible()</code>
48  * <li><code>getVisibleRowCount()</code>
49  * <li><code>setVisibleRowCount()</code>
50  * </ul>
51  * <p>
52  * The next group of <code>JTree</code> methods use "visible" to mean
53  * "viewable" (under an expanded parent):
54  * <ul>
55  * <li><code>isVisible()</code>
56  * <li><code>makeVisible()</code>
57  * </ul>
58  * <p>
59  * If you are interested in knowing when the selection changes implement
60  * the <code>TreeSelectionListener</code> interface and add the instance
61  * using the method <code>addTreeSelectionListener</code>.
62  * <code>valueChanged</code> will be invoked when the
63  * selection changes, that is if the user clicks twice on the same
64  * node <code>valueChanged</code> will only be invoked once.
65  * <p>
66  * If you are interested in detecting either double-click events or when
67  * a user clicks on a node, regardless of whether or not it was selected,
68  * we recommend you do the following:
69  * <pre>
70  * final JTree tree = ...;
71  *
72  * MouseListener ml = new MouseAdapter() {
73  * public void <b>mousePressed</b>(MouseEvent e) {
74  * int selRow = tree.getRowForLocation(e.getX(), e.getY());
75  * TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
76  * if(selRow != -1) {
77  * if(e.getClickCount() == 1) {
78  * mySingleClick(selRow, selPath);
79  * }
80  * else if(e.getClickCount() == 2) {
81  * myDoubleClick(selRow, selPath);
82  * }
83  * }
84  * }
85  * };
86  * tree.addMouseListener(ml);
87  * </pre>
88  * NOTE: This example obtains both the path and row, but you only need to
89  * get the one you're interested in.
90  * <p>
91  * To use <code>JTree</code> to display compound nodes
92  * (for example, nodes containing both
93  * a graphic icon and text), subclass {@link TreeCellRenderer} and use
94  * {@link #setCellRenderer} to tell the tree to use it. To edit such nodes,
95  * subclass {@link TreeCellEditor} and use {@link #setCellEditor}.
96  * <p>
97  * Like all <code>JComponent</code> classes, you can use {@link InputMap} and
98  * {@link ActionMap}
99  * to associate an {@link Action} object with a {@link KeyStroke}
100  * and execute the action under specified conditions.
101  * <p>
102  * <strong>Warning:</strong>
103  * Serialized objects of this class will not be compatible with
104  * future Swing releases. The current serialization support is
105  * appropriate for short term storage or RMI between applications running
106  * the same version of Swing. As of 1.4, support for long term storage
107  * of all JavaBeans<sup><font size="-2">TM</font></sup>
108  * has been added to the <code>java.beans</code> package.
109  * Please see {@link java.beans.XMLEncoder}.
110  *
111  * @beaninfo
112  * attribute: isContainer false
113  * description: A component that displays a set of hierarchical data as an outline.
114  *
115  * @version 1.176, 07/13/04
116  * @author Rob Davis
117  * @author Ray Ryan
118  * @author Scott Violet
119  */

120 public class JTree extends JComponent JavaDoc implements Scrollable JavaDoc, Accessible
121 {
122     /**
123      * @see #getUIClassID
124      * @see #readObject
125      */

126     private static final String JavaDoc uiClassID = "TreeUI";
127
128     /**
129      * The model that defines the tree displayed by this object.
130      */

131     transient protected TreeModel treeModel;
132
133     /**
134      * Models the set of selected nodes in this tree.
135      */

136     transient protected TreeSelectionModel selectionModel;
137
138     /**
139      * True if the root node is displayed, false if its children are
140      * the highest visible nodes.
141      */

142     protected boolean rootVisible;
143
144     /**
145      * The cell used to draw nodes. If <code>null</code>, the UI uses a default
146      * <code>cellRenderer</code>.
147      */

148     transient protected TreeCellRenderer cellRenderer;
149
150     /**
151      * Height to use for each display row. If this is <= 0 the renderer
152      * determines the height for each row.
153      */

154     protected int rowHeight;
155     private boolean rowHeightSet = false;
156
157     /**
158      * Maps from <code>TreePath</code> to <code>Boolean</code>
159      * indicating whether or not the
160      * particular path is expanded. This ONLY indicates whether a
161      * given path is expanded, and NOT if it is visible or not. That
162      * information must be determined by visiting all the parent
163      * paths and seeing if they are visible.
164      */

165     transient private Hashtable expandedState;
166
167
168     /**
169      * True if handles are displayed at the topmost level of the tree.
170      * <p>
171      * A handle is a small icon that displays adjacent to the node which
172      * allows the user to click once to expand or collapse the node. A
173      * common interface shows a plus sign (+) for a node which can be
174      * expanded and a minus sign (-) for a node which can be collapsed.
175      * Handles are always shown for nodes below the topmost level.
176      * <p>
177      * If the <code>rootVisible</code> setting specifies that the root
178      * node is to be displayed, then that is the only node at the topmost
179      * level. If the root node is not displayed, then all of its
180      * children are at the topmost level of the tree. Handles are
181      * always displayed for nodes other than the topmost.
182      * <p>
183      * If the root node isn't visible, it is generally a good to make
184      * this value true. Otherwise, the tree looks exactly like a list,
185      * and users may not know that the "list entries" are actually
186      * tree nodes.
187      *
188      * @see #rootVisible
189      */

190     protected boolean showsRootHandles;
191     private boolean showsRootHandlesSet = false;
192
193     /**
194      * Creates a new event and passed it off the
195      * <code>selectionListeners</code>.
196      */

197     protected transient TreeSelectionRedirector selectionRedirector;
198
199     /**
200      * Editor for the entries. Default is <code>null</code>
201      * (tree is not editable).
202      */

203     transient protected TreeCellEditor cellEditor;
204
205     /**
206      * Is the tree editable? Default is false.
207      */

208     protected boolean editable;
209
210     /**
211      * Is this tree a large model? This is a code-optimization setting.
212      * A large model can be used when the cell height is the same for all
213      * nodes. The UI will then cache very little information and instead
214      * continually message the model. Without a large model the UI caches
215      * most of the information, resulting in fewer method calls to the model.
216      * <p>
217      * This value is only a suggestion to the UI. Not all UIs will
218      * take advantage of it. Default value is false.
219      */

220     protected boolean largeModel;
221
222     /**
223      * Number of rows to make visible at one time. This value is used for
224      * the <code>Scrollable</code> interface. It determines the preferred
225      * size of the display area.
226      */

227     protected int visibleRowCount;
228
229     /**
230      * If true, when editing is to be stopped by way of selection changing,
231      * data in tree changing or other means <code>stopCellEditing</code>
232      * is invoked, and changes are saved. If false,
233      * <code>cancelCellEditing</code> is invoked, and changes
234      * are discarded. Default is false.
235      */

236     protected boolean invokesStopCellEditing;
237
238     /**
239      * If true, when a node is expanded, as many of the descendants are
240      * scrolled to be visible.
241      */

242     protected boolean scrollsOnExpand;
243     private boolean scrollsOnExpandSet = false;
244
245     /**
246      * Number of mouse clicks before a node is expanded.
247      */

248     protected int toggleClickCount;
249
250     /**
251      * Updates the <code>expandedState</code>.
252      */

253     transient protected TreeModelListener treeModelListener;
254
255     /**
256      * Used when <code>setExpandedState</code> is invoked,
257      * will be a <code>Stack</code> of <code>Stack</code>s.
258      */

259     transient private Stack expandedStack;
260
261     /**
262      * Lead selection path, may not be <code>null</code>.
263      */

264     private TreePath leadPath;
265
266     /**
267      * Anchor path.
268      */

269     private TreePath anchorPath;
270
271     /**
272      * True if paths in the selection should be expanded.
273      */

274     private boolean expandsSelectedPaths;
275
276     /**
277      * This is set to true for the life of the <code>setUI</code> call.
278      */

279     private boolean settingUI;
280
281     /** If true, mouse presses on selections initiate a drag operation. */
282     private boolean dragEnabled;
283
284     /**
285      * When <code>addTreeExpansionListener</code> is invoked,
286      * and <code>settingUI</code> is true, this ivar gets set to the passed in
287      * <code>Listener</code>. This listener is then notified first in
288      * <code>fireTreeCollapsed</code> and <code>fireTreeExpanded</code>.
289      * <p>This is an ugly workaround for a way to have the UI listener
290      * get notified before other listeners.
291      */

292     private transient TreeExpansionListener uiTreeExpansionListener;
293
294     /**
295      * Max number of stacks to keep around.
296      */

297     private static int TEMP_STACK_SIZE = 11;
298
299     //
300
// Bound property names
301
//
302
/** Bound property name for <code>cellRenderer</code>. */
303     public final static String JavaDoc CELL_RENDERER_PROPERTY = "cellRenderer";
304     /** Bound property name for <code>treeModel</code>. */
305     public final static String JavaDoc TREE_MODEL_PROPERTY = "model";
306     /** Bound property name for <code>rootVisible</code>. */
307     public final static String JavaDoc ROOT_VISIBLE_PROPERTY = "rootVisible";
308     /** Bound property name for <code>showsRootHandles</code>. */
309     public final static String JavaDoc SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
310     /** Bound property name for <code>rowHeight</code>. */
311     public final static String JavaDoc ROW_HEIGHT_PROPERTY = "rowHeight";
312     /** Bound property name for <code>cellEditor</code>. */
313     public final static String JavaDoc CELL_EDITOR_PROPERTY = "cellEditor";
314     /** Bound property name for <code>editable</code>. */
315     public final static String JavaDoc EDITABLE_PROPERTY = "editable";
316     /** Bound property name for <code>largeModel</code>. */
317     public final static String JavaDoc LARGE_MODEL_PROPERTY = "largeModel";
318     /** Bound property name for selectionModel. */
319     public final static String JavaDoc SELECTION_MODEL_PROPERTY = "selectionModel";
320     /** Bound property name for <code>visibleRowCount</code>. */
321     public final static String JavaDoc VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
322     /** Bound property name for <code>messagesStopCellEditing</code>. */
323     public final static String JavaDoc INVOKES_STOP_CELL_EDITING_PROPERTY = "invokesStopCellEditing";
324     /** Bound property name for <code>scrollsOnExpand</code>. */
325     public final static String JavaDoc SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
326     /** Bound property name for <code>toggleClickCount</code>. */
327     public final static String JavaDoc TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
328     /** Bound property name for <code>leadSelectionPath</code>.
329      * @since 1.3 */

330     public final static String JavaDoc LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
331     /** Bound property name for anchor selection path.
332      * @since 1.3 */

333     public final static String JavaDoc ANCHOR_SELECTION_PATH_PROPERTY = "anchorSelectionPath";
334     /** Bound property name for expands selected paths property
335      * @since 1.3 */

336     public final static String JavaDoc EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths";
337
338
339     /**
340      * Creates and returns a sample <code>TreeModel</code>.
341      * Used primarily for beanbuilders to show something interesting.
342      *
343      * @return the default <code>TreeModel</code>
344      */

345     protected static TreeModel getDefaultTreeModel() {
346         DefaultMutableTreeNode root = new DefaultMutableTreeNode("JTree");
347     DefaultMutableTreeNode parent;
348
349     parent = new DefaultMutableTreeNode("colors");
350     root.add(parent);
351     parent.add(new DefaultMutableTreeNode("blue"));
352     parent.add(new DefaultMutableTreeNode("violet"));
353     parent.add(new DefaultMutableTreeNode("red"));
354     parent.add(new DefaultMutableTreeNode("yellow"));
355
356     parent = new DefaultMutableTreeNode("sports");
357     root.add(parent);
358     parent.add(new DefaultMutableTreeNode("basketball"));
359     parent.add(new DefaultMutableTreeNode("soccer"));
360     parent.add(new DefaultMutableTreeNode("football"));
361     parent.add(new DefaultMutableTreeNode("hockey"));
362
363     parent = new DefaultMutableTreeNode("food");
364     root.add(parent);
365     parent.add(new DefaultMutableTreeNode("hot dogs"));
366     parent.add(new DefaultMutableTreeNode("pizza"));
367     parent.add(new DefaultMutableTreeNode("ravioli"));
368     parent.add(new DefaultMutableTreeNode("bananas"));
369         return new DefaultTreeModel(root);
370     }
371
372     /**
373      * Returns a <code>TreeModel</code> wrapping the specified object.
374      * If the object is:<ul>
375      * <li>an array of <code>Object</code>s,
376      * <li>a <code>Hashtable</code>, or
377      * <li>a <code>Vector</code>
378      * </ul>then a new root node is created with each of the incoming
379      * objects as children. Otherwise, a new root is created with the
380      * specified object as its value.
381      *
382      * @param value the <code>Object</code> used as the foundation for
383      * the <code>TreeModel</code>
384      * @return a <code>TreeModel</code> wrapping the specified object
385      */

386     protected static TreeModel createTreeModel(Object JavaDoc value) {
387         DefaultMutableTreeNode root;
388
389         if((value instanceof Object JavaDoc[]) || (value instanceof Hashtable) ||
390            (value instanceof Vector)) {
391             root = new DefaultMutableTreeNode("root");
392             DynamicUtilTreeNode.createChildren(root, value);
393         }
394         else {
395             root = new DynamicUtilTreeNode("root", value);
396         }
397         return new DefaultTreeModel(root, false);
398     }
399
400     /**
401      * Returns a <code>JTree</code> with a sample model.
402      * The default model used by the tree defines a leaf node as any node
403      * without children.
404      *
405      * @see DefaultTreeModel#asksAllowsChildren
406      */

407     public JTree() {
408         this(getDefaultTreeModel());
409     }
410
411     /**
412      * Returns a <code>JTree</code> with each element of the
413      * specified array as the
414      * child of a new root node which is not displayed.
415      * By default, the tree defines a leaf node as any node without
416      * children.
417      *
418      * @param value an array of <code>Object</code>s
419      * @see DefaultTreeModel#asksAllowsChildren
420      */

421     public JTree(Object JavaDoc[] value) {
422         this(createTreeModel(value));
423         this.setRootVisible(false);
424         this.setShowsRootHandles(true);
425         expandRoot();
426     }
427
428     /**
429      * Returns a <code>JTree</code> with each element of the specified
430      * <code>Vector</code> as the
431      * child of a new root node which is not displayed. By default, the
432      * tree defines a leaf node as any node without children.
433      *
434      * @param value a <code>Vector</code>
435      * @see DefaultTreeModel#asksAllowsChildren
436      */

437     public JTree(Vector<?> value) {
438         this(createTreeModel(value));
439         this.setRootVisible(false);
440         this.setShowsRootHandles(true);
441         expandRoot();
442     }
443
444     /**
445      * Returns a <code>JTree</code> created from a <code>Hashtable</code>
446      * which does not display with root.
447      * Each value-half of the key/value pairs in the <code>HashTable</code>
448      * becomes a child of the new root node. By default, the tree defines
449      * a leaf node as any node without children.
450      *
451      * @param value a <code>Hashtable</code>
452      * @see DefaultTreeModel#asksAllowsChildren
453      */

454     public JTree(Hashtable<?,?> value) {
455         this(createTreeModel(value));
456         this.setRootVisible(false);
457         this.setShowsRootHandles(true);
458         expandRoot();
459     }
460
461     /**
462      * Returns a <code>JTree</code> with the specified
463      * <code>TreeNode</code> as its root,
464      * which displays the root node.
465      * By default, the tree defines a leaf node as any node without children.
466      *
467      * @param root a <code>TreeNode</code> object
468      * @see DefaultTreeModel#asksAllowsChildren
469      */

470     public JTree(TreeNode root) {
471         this(root, false);
472     }
473
474     /**
475      * Returns a <code>JTree</code> with the specified <code>TreeNode</code>
476      * as its root, which
477      * displays the root node and which decides whether a node is a
478      * leaf node in the specified manner.
479      *
480      * @param root a <code>TreeNode</code> object
481      * @param asksAllowsChildren if false, any node without children is a
482      * leaf node; if true, only nodes that do not allow
483      * children are leaf nodes
484      * @see DefaultTreeModel#asksAllowsChildren
485      */

486     public JTree(TreeNode root, boolean asksAllowsChildren) {
487         this(new DefaultTreeModel(root, asksAllowsChildren));
488     }
489
490     /**
491      * Returns an instance of <code>JTree</code> which displays the root node
492      * -- the tree is created using the specified data model.
493      *
494      * @param newModel the <code>TreeModel</code> to use as the data model
495      */

496     public JTree(TreeModel newModel) {
497         super();
498     expandedStack = new Stack();
499     toggleClickCount = 2;
500     expandedState = new Hashtable();
501         setLayout(null);
502         rowHeight = 16;
503         visibleRowCount = 20;
504         rootVisible = true;
505         selectionModel = new DefaultTreeSelectionModel();
506         cellRenderer = null;
507     scrollsOnExpand = true;
508         setOpaque(true);
509     expandsSelectedPaths = true;
510         updateUI();
511         setModel(newModel);
512     }
513
514     /**
515      * Returns the L&F object that renders this component.
516      *
517      * @return the <code>TreeUI</code> object that renders this component
518      */

519     public TreeUI JavaDoc getUI() {
520         return (TreeUI JavaDoc)ui;
521     }
522
523     /**
524      * Sets the L&F object that renders this component.
525      *
526      * @param ui the <code>TreeUI</code> L&F object
527      * @see UIDefaults#getUI
528      * @beaninfo
529      * bound: true
530      * hidden: true
531      * attribute: visualUpdate true
532      * description: The UI object that implements the Component's LookAndFeel.
533      */

534     public void setUI(TreeUI JavaDoc ui) {
535         if ((TreeUI JavaDoc)this.ui != ui) {
536         settingUI = true;
537         uiTreeExpansionListener = null;
538         try {
539         super.setUI(ui);
540         }
541         finally {
542         settingUI = false;
543         }
544         }
545     }
546
547     /**
548      * Notification from the <code>UIManager</code> that the L&F has changed.
549      * Replaces the current UI object with the latest version from the
550      * <code>UIManager</code>.
551      *
552      * @see JComponent#updateUI
553      */

554     public void updateUI() {
555         setUI((TreeUI JavaDoc)UIManager.getUI(this));
556         invalidate();
557     }
558
559
560     /**
561      * Returns the name of the L&F class that renders this component.
562      *
563      * @return the string "TreeUI"
564      * @see JComponent#getUIClassID
565      * @see UIDefaults#getUI
566      */

567     public String JavaDoc getUIClassID() {
568         return uiClassID;
569     }
570
571
572     /**
573      * Returns the current <code>TreeCellRenderer</code>
574      * that is rendering each cell.
575      *
576      * @return the <code>TreeCellRenderer</code> that is rendering each cell
577      */

578     public TreeCellRenderer getCellRenderer() {
579         return cellRenderer;
580     }
581
582     /**
583      * Sets the <code>TreeCellRenderer</code> that will be used to
584      * draw each cell.
585      *
586      * @param x the <code>TreeCellRenderer</code> that is to render each cell
587      * @beaninfo
588      * bound: true
589      * description: The TreeCellRenderer that will be used to draw
590      * each cell.
591      */

592     public void setCellRenderer(TreeCellRenderer x) {
593         TreeCellRenderer oldValue = cellRenderer;
594
595         cellRenderer = x;
596         firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, cellRenderer);
597         invalidate();
598     }
599
600     /**
601       * Determines whether the tree is editable. Fires a property
602       * change event if the new setting is different from the existing
603       * setting.
604       *
605       * @param flag a boolean value, true if the tree is editable
606       * @beaninfo
607       * bound: true
608       * description: Whether the tree is editable.
609       */

610     public void setEditable(boolean flag) {
611         boolean oldValue = this.editable;
612
613         this.editable = flag;
614         firePropertyChange(EDITABLE_PROPERTY, oldValue, flag);
615         if (accessibleContext != null) {
616             accessibleContext.firePropertyChange(
617                 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
618                 (oldValue ? AccessibleState.EDITABLE : null),
619                 (flag ? AccessibleState.EDITABLE : null));
620         }
621     }
622
623     /**
624      * Returns true if the tree is editable.
625      *
626      * @return true if the tree is editable
627      */

628     public boolean isEditable() {
629         return editable;
630     }
631
632     /**
633      * Sets the cell editor. A <code>null</code> value implies that the
634      * tree cannot be edited. If this represents a change in the
635      * <code>cellEditor</code>, the <code>propertyChange</code>
636      * method is invoked on all listeners.
637      *
638      * @param cellEditor the <code>TreeCellEditor</code> to use
639      * @beaninfo
640      * bound: true
641      * description: The cell editor. A null value implies the tree
642      * cannot be edited.
643      */

644     public void setCellEditor(TreeCellEditor cellEditor) {
645         TreeCellEditor oldEditor = this.cellEditor;
646
647         this.cellEditor = cellEditor;
648         firePropertyChange(CELL_EDITOR_PROPERTY, oldEditor, cellEditor);
649         invalidate();
650     }
651
652     /**
653      * Returns the editor used to edit entries in the tree.
654      *
655      * @return the <code>TreeCellEditor</code> in use,
656      * or <code>null</code> if the tree cannot be edited
657      */

658     public TreeCellEditor getCellEditor() {
659         return cellEditor;
660     }
661
662     /**
663      * Returns the <code>TreeModel</code> that is providing the data.
664      *
665      * @return the <code>TreeModel</code> that is providing the data
666      */

667     public TreeModel getModel() {
668         return treeModel;
669     }
670
671     /**
672      * Sets the <code>TreeModel</code> that will provide the data.
673      *
674      * @param newModel the <code>TreeModel</code> that is to provide the data
675      * @beaninfo
676      * bound: true
677      * description: The TreeModel that will provide the data.
678      */

679     public void setModel(TreeModel newModel) {
680         clearSelection();
681
682         TreeModel oldModel = treeModel;
683
684     if(treeModel != null && treeModelListener != null)
685         treeModel.removeTreeModelListener(treeModelListener);
686
687         if (accessibleContext != null) {
688         if (treeModel != null) {
689                 treeModel.removeTreeModelListener((TreeModelListener)accessibleContext);
690         }
691             if (newModel != null) {
692             newModel.addTreeModelListener((TreeModelListener)accessibleContext);
693         }
694         }
695
696         treeModel = newModel;
697     clearToggledPaths();
698     if(treeModel != null) {
699         if(treeModelListener == null)
700         treeModelListener = createTreeModelListener();
701         if(treeModelListener != null)
702         treeModel.addTreeModelListener(treeModelListener);
703         // Mark the root as expanded, if it isn't a leaf.
704
if(treeModel.getRoot() != null &&
705                !treeModel.isLeaf(treeModel.getRoot())) {
706         expandedState.put(new TreePath(treeModel.getRoot()),
707                   Boolean.TRUE);
708             }
709     }
710         firePropertyChange(TREE_MODEL_PROPERTY, oldModel, treeModel);
711         invalidate();
712     }
713
714     /**
715      * Returns true if the root node of the tree is displayed.
716      *
717      * @return true if the root node of the tree is displayed
718      * @see #rootVisible
719      */

720     public boolean isRootVisible() {
721         return rootVisible;
722     }
723
724     /**
725      * Determines whether or not the root node from
726      * the <code>TreeModel</code> is visible.
727      *
728      * @param rootVisible true if the root node of the tree is to be displayed
729      * @see #rootVisible
730      * @beaninfo
731      * bound: true
732      * description: Whether or not the root node
733      * from the TreeModel is visible.
734      */

735     public void setRootVisible(boolean rootVisible) {
736         boolean oldValue = this.rootVisible;
737
738         this.rootVisible = rootVisible;
739         firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, this.rootVisible);
740         if (accessibleContext != null) {
741             ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
742         }
743     }
744
745     /**
746      * Sets the value of the <code>showsRootHandles</code> property,
747      * which specifies whether the node handles should be displayed.
748      * The default value of this property depends on the constructor
749      * used to create the <code>JTree</code>.
750      * Some look and feels might not support handles;
751      * they will ignore this property.
752      *
753      * @param newValue <code>true</code> if root handles should be displayed;
754      * otherwise, <code>false</code>
755      * @see #showsRootHandles
756      * @see #getShowsRootHandles
757      * @beaninfo
758      * bound: true
759      * description: Whether the node handles are to be
760      * displayed.
761      */

762     public void setShowsRootHandles(boolean newValue) {
763         boolean oldValue = showsRootHandles;
764     TreeModel model = getModel();
765
766         showsRootHandles = newValue;
767         showsRootHandlesSet = true;
768         firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue,
769                            showsRootHandles);
770         if (accessibleContext != null) {
771             ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
772         }
773         invalidate();
774     }
775
776     /**
777      * Returns the value of the <code>showsRootHandles</code> property.
778      *
779      * @return the value of the <code>showsRootHandles</code> property
780      * @see #showsRootHandles
781      */

782     public boolean getShowsRootHandles()
783     {
784         return showsRootHandles;
785     }
786
787     /**
788      * Sets the height of each cell, in pixels. If the specified value
789      * is less than or equal to zero the current cell renderer is
790      * queried for each row's height.
791      *
792      * @param rowHeight the height of each cell, in pixels
793      * @beaninfo
794      * bound: true
795      * description: The height of each cell.
796      */

797     public void setRowHeight(int rowHeight)
798     {
799         int oldValue = this.rowHeight;
800
801         this.rowHeight = rowHeight;
802     rowHeightSet = true;
803         firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, this.rowHeight);
804         invalidate();
805     }
806
807     /**
808      * Returns the height of each row. If the returned value is less than
809      * or equal to 0 the height for each row is determined by the
810      * renderer.
811      *
812      */

813     public int getRowHeight()
814     {
815         return rowHeight;
816     }
817
818     /**
819      * Returns true if the height of each display row is a fixed size.
820      *
821      * @return true if the height of each row is a fixed size
822      */

823     public boolean isFixedRowHeight()
824     {
825         return (rowHeight > 0);
826     }
827
828     /**
829      * Specifies whether the UI should use a large model.
830      * (Not all UIs will implement this.) Fires a property change
831      * for the LARGE_MODEL_PROPERTY.
832      *
833      * @param newValue true to suggest a large model to the UI
834      * @see #largeModel
835      * @beaninfo
836      * bound: true
837      * description: Whether the UI should use a
838      * large model.
839      */

840     public void setLargeModel(boolean newValue) {
841         boolean oldValue = largeModel;
842
843         largeModel = newValue;
844         firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, newValue);
845     }
846
847     /**
848      * Returns true if the tree is configured for a large model.
849      *
850      * @return true if a large model is suggested
851      * @see #largeModel
852      */

853     public boolean isLargeModel() {
854         return largeModel;
855     }
856
857     /**
858      * Determines what happens when editing is interrupted by selecting
859      * another node in the tree, a change in the tree's data, or by some
860      * other means. Setting this property to <code>true</code> causes the
861      * changes to be automatically saved when editing is interrupted.
862      * <p>
863      * Fires a property change for the INVOKES_STOP_CELL_EDITING_PROPERTY.
864      *
865      * @param newValue true means that <code>stopCellEditing</code> is invoked
866      * when editing is interrupted, and data is saved; false means that
867      * <code>cancelCellEditing</code> is invoked, and changes are lost
868      * @beaninfo
869      * bound: true
870      * description: Determines what happens when editing is interrupted,
871      * selecting another node in the tree, a change in the
872      * tree's data, or some other means.
873      */

874     public void setInvokesStopCellEditing(boolean newValue) {
875         boolean oldValue = invokesStopCellEditing;
876
877         invokesStopCellEditing = newValue;
878         firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, oldValue,
879                            newValue);
880     }
881
882     /**
883      * Returns the indicator that tells what happens when editing is
884      * interrupted.
885      *
886      * @return the indicator that tells what happens when editing is
887      * interrupted
888      * @see #setInvokesStopCellEditing
889      */

890     public boolean getInvokesStopCellEditing() {
891         return invokesStopCellEditing;
892     }
893
894     /**
895      * Sets the <code>scrollsOnExpand</code> property,
896      * which determines whether the
897      * tree might scroll to show previously hidden children.
898      * If this property is <code>true</code> (the default),
899      * when a node expands
900      * the tree can use scrolling to make
901      * the maximum possible number of the node's descendants visible.
902      * In some look and feels, trees might not need to scroll when expanded;
903      * those look and feels will ignore this property.
904      *
905      * @param newValue <code>false</code> to disable scrolling on expansion;
906      * <code>true</code> to enable it
907      * @see #getScrollsOnExpand
908      *
909      * @beaninfo
910      * bound: true
911      * description: Indicates if a node descendant should be scrolled when expanded.
912      */

913     public void setScrollsOnExpand(boolean newValue) {
914     boolean oldValue = scrollsOnExpand;
915
916     scrollsOnExpand = newValue;
917     scrollsOnExpandSet = true;
918         firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue,
919                            newValue);
920     }
921
922     /**
923      * Returns the value of the <code>scrollsOnExpand</code> property.
924      *
925      * @return the value of the <code>scrollsOnExpand</code> property
926      */

927     public boolean getScrollsOnExpand() {
928     return scrollsOnExpand;
929     }
930
931     /**
932      * Sets the number of mouse clicks before a node will expand or close.
933      * The default is two.
934      *
935      * @since 1.3
936      * @beaninfo
937      * bound: true
938      * description: Number of clicks before a node will expand/collapse.
939      */

940     public void setToggleClickCount(int clickCount) {
941     int oldCount = toggleClickCount;
942
943     toggleClickCount = clickCount;
944     firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldCount,
945                clickCount);
946     }
947
948     /**
949      * Returns the number of mouse clicks needed to expand or close a node.
950      *
951      * @return number of mouse clicks before node is expanded
952      * @since 1.3
953      */

954     public int getToggleClickCount() {
955     return toggleClickCount;
956     }
957
958     /**
959      * Configures the <code>expandsSelectedPaths</code> property. If
960      * true, any time the selection is changed, either via the
961      * <code>TreeSelectionModel</code>, or the cover methods provided by
962      * <code>JTree</code>, the <code>TreePath</code>s parents will be
963      * expanded to make them visible (visible meaning the parent path is
964      * expanded, not necessarily in the visible rectangle of the
965      * <code>JTree</code>). If false, when the selection
966      * changes the nodes parent is not made visible (all its parents expanded).
967      * This is useful if you wish to have your selection model maintain paths
968      * that are not always visible (all parents expanded).
969      *
970      * @param newValue the new value for <code>expandsSelectedPaths</code>
971      *
972      * @since 1.3
973      * @beaninfo
974      * bound: true
975      * description: Indicates whether changes to the selection should make
976      * the parent of the path visible.
977      */

978     public void setExpandsSelectedPaths(boolean newValue) {
979     boolean oldValue = expandsSelectedPaths;
980
981     expandsSelectedPaths = newValue;
982     firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue,
983                newValue);
984     }
985
986     /**
987      * Returns the <code>expandsSelectedPaths</code> property.
988      * @return true if selection changes result in the parent path being
989      * expanded
990      * @since 1.3
991      * @see #setExpandsSelectedPaths
992      */

993     public boolean getExpandsSelectedPaths() {
994     return expandsSelectedPaths;
995     }
996
997     /**
998      * Sets the <code>dragEnabled</code> property,
999      * which must be <code>true</code> to enable
1000     * automatic drag handling (the first part of drag and drop)
1001     * on this component.
1002     * The <code>transferHandler</code> property needs to be set
1003     * to a non-<code>null</code> value for the drag to do
1004     * anything. The default value of the <code>dragEnabled</code>
1005     * property
1006     * is <code>false</code>.
1007     *
1008     * <p>
1009     *
1010     * When automatic drag handling is enabled,
1011     * most look and feels begin a drag-and-drop operation
1012     * whenever the user presses the mouse button over a selection
1013     * and then moves the mouse a few pixels.
1014     * Setting this property to <code>true</code>
1015     * can therefore have a subtle effect on
1016     * how selections behave.
1017     *
1018     * <p>
1019     *
1020     * Some look and feels might not support automatic drag and drop;
1021     * they will ignore this property. You can work around such
1022     * look and feels by modifying the component
1023     * to directly call the <code>exportAsDrag</code> method of a
1024     * <code>TransferHandler</code>.
1025     *
1026     * @param b the value to set the <code>dragEnabled</code> property to
1027     * @exception HeadlessException if
1028     * <code>b</code> is <code>true</code> and
1029     * <code>GraphicsEnvironment.isHeadless()</code>
1030     * returns <code>true</code>
1031     * @see java.awt.GraphicsEnvironment#isHeadless
1032     * @see #getDragEnabled
1033     * @see #setTransferHandler
1034     * @see TransferHandler
1035     * @since 1.4
1036     *
1037     * @beaninfo
1038     * description: determines whether automatic drag handling is enabled
1039     * bound: false
1040     */

1041    public void setDragEnabled(boolean b) {
1042        if (b && GraphicsEnvironment.isHeadless()) {
1043            throw new HeadlessException();
1044        }
1045    dragEnabled = b;
1046    }
1047
1048    /**
1049     * Gets the value of the <code>dragEnabled</code> property.
1050     *
1051     * @return the value of the <code>dragEnabled</code> property
1052     * @see #setDragEnabled
1053     * @since 1.4
1054     */

1055    public boolean getDragEnabled() {
1056    return dragEnabled;
1057    }
1058
1059    /**
1060     * Returns <code>isEditable</code>. This is invoked from the UI before
1061     * editing begins to insure that the given path can be edited. This
1062     * is provided as an entry point for subclassers to add filtered
1063     * editing without having to resort to creating a new editor.
1064     *
1065     * @return true if every parent node and the node itself is editable
1066     * @see #isEditable
1067     */

1068    public boolean isPathEditable(TreePath path) {
1069        return isEditable();
1070    }
1071
1072    /**
1073     * Overrides <code>JComponent</code>'s <code>getToolTipText</code>
1074     * method in order to allow
1075     * renderer's tips to be used if it has text set.
1076     * <p>
1077     * NOTE: For <code>JTree</code> to properly display tooltips of its
1078     * renderers, <code>JTree</code> must be a registered component with the
1079     * <code>ToolTipManager</code>. This can be done by invoking
1080     * <code>ToolTipManager.sharedInstance().registerComponent(tree)</code>.
1081     * This is not done automatically!
1082     *
1083     * @param event the <code>MouseEvent</code> that initiated the
1084     * <code>ToolTip</code> display
1085     * @return a string containing the tooltip or <code>null</code>
1086     * if <code>event</code> is null
1087     */

1088    public String JavaDoc getToolTipText(MouseEvent event) {
1089        if(event != null) {
1090            Point p = event.getPoint();
1091            int selRow = getRowForLocation(p.x, p.y);
1092            TreeCellRenderer r = getCellRenderer();
1093
1094            if(selRow != -1 && r != null) {
1095                TreePath path = getPathForRow(selRow);
1096                Object JavaDoc lastPath = path.getLastPathComponent();
1097                Component rComponent = r.getTreeCellRendererComponent
1098                    (this, lastPath, isRowSelected(selRow),
1099                     isExpanded(selRow), getModel().isLeaf(lastPath), selRow,
1100                     true);
1101
1102                if(rComponent instanceof JComponent JavaDoc) {
1103                    MouseEvent newEvent;
1104                    Rectangle pathBounds = getPathBounds(path);
1105
1106                    p.translate(-pathBounds.x, -pathBounds.y);
1107                    newEvent = new MouseEvent(rComponent, event.getID(),
1108                                          event.getWhen(),
1109                                              event.getModifiers(),
1110                                              p.x, p.y, event.getClickCount(),
1111                                              event.isPopupTrigger());
1112                    
1113                    return ((JComponent JavaDoc)rComponent).getToolTipText(newEvent);
1114                }
1115            }
1116        }
1117        return null;
1118    }
1119    
1120    /**
1121     * Called by the renderers to convert the specified value to
1122     * text. This implementation returns <code>value.toString</code>, ignoring
1123     * all other arguments. To control the conversion, subclass this
1124     * method and use any of the arguments you need.
1125     *
1126     * @param value the <code>Object</code> to convert to text
1127     * @param selected true if the node is selected
1128     * @param expanded true if the node is expanded
1129     * @param leaf true if the node is a leaf node
1130     * @param row an integer specifying the node's display row, where 0 is
1131     * the first row in the display
1132     * @param hasFocus true if the node has the focus
1133     * @return the <code>String</code> representation of the node's value
1134     */

1135    public String JavaDoc convertValueToText(Object JavaDoc value, boolean selected,
1136                                     boolean expanded, boolean leaf, int row,
1137                                     boolean hasFocus) {
1138        if(value != null) {
1139            String JavaDoc sValue = value.toString();
1140            if (sValue != null) {
1141                return sValue;
1142            }
1143        }
1144        return "";
1145    }
1146
1147    //
1148
// The following are convenience methods that get forwarded to the
1149
// current TreeUI.
1150
//
1151

1152    /**
1153     * Returns the number of rows that are currently being displayed.
1154     *
1155     * @return the number of rows that are being displayed
1156     */

1157    public int getRowCount() {
1158        TreeUI JavaDoc tree = getUI();
1159
1160        if(tree != null)
1161            return tree.getRowCount(this);
1162        return 0;
1163    }
1164
1165    /**
1166     * Selects the node identified by the specified path. If any
1167     * component of the path is hidden (under a collapsed node), and
1168     * <code>getExpandsSelectedPaths</code> is true it is
1169     * exposed (made viewable).
1170     *
1171     * @param path the <code>TreePath</code> specifying the node to select
1172     */

1173    public void setSelectionPath(TreePath path) {
1174        getSelectionModel().setSelectionPath(path);
1175    }
1176
1177    /**
1178     * Selects the nodes identified by the specified array of paths.
1179     * If any component in any of the paths is hidden (under a collapsed
1180     * node), and <code>getExpandsSelectedPaths</code> is true
1181     * it is exposed (made viewable).
1182     *
1183     * @param paths an array of <code>TreePath</code> objects that specifies
1184     * the nodes to select
1185     */

1186    public void setSelectionPaths(TreePath[] paths) {
1187        getSelectionModel().setSelectionPaths(paths);
1188    }
1189
1190    /**
1191     * Sets the path identifies as the lead. The lead may not be selected.
1192     * The lead is not maintained by <code>JTree</code>,
1193     * rather the UI will update it.
1194     *
1195     * @param newPath the new lead path
1196     * @since 1.3
1197     * @beaninfo
1198     * bound: true
1199     * description: Lead selection path
1200     */

1201    public void setLeadSelectionPath(TreePath newPath) {
1202    TreePath oldValue = leadPath;
1203
1204    leadPath = newPath;
1205    firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, newPath);
1206    }
1207
1208    /**
1209     * Sets the path identified as the anchor.
1210     * The anchor is not maintained by <code>JTree</code>, rather the UI will
1211     * update it.
1212     *
1213     * @param newPath the new anchor path
1214     * @since 1.3
1215     * @beaninfo
1216     * bound: true
1217     * description: Anchor selection path
1218     */

1219    public void setAnchorSelectionPath(TreePath newPath) {
1220    TreePath oldValue = anchorPath;
1221
1222    anchorPath = newPath;
1223    firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, newPath);
1224    }
1225
1226    /**
1227     * Selects the node at the specified row in the display.
1228     *
1229     * @param row the row to select, where 0 is the first row in
1230     * the display
1231     */

1232    public void setSelectionRow(int row) {
1233        int[] rows = { row };
1234
1235        setSelectionRows(rows);
1236    }
1237
1238    /**
1239     * Selects the nodes corresponding to each of the specified rows
1240     * in the display. If a particular element of <code>rows</code> is
1241     * < 0 or >= <code>getRowCount</code>, it will be ignored.
1242     * If none of the elements
1243     * in <code>rows</code> are valid rows, the selection will
1244     * be cleared. That is it will be as if <code>clearSelection</code>
1245     * was invoked.
1246     *
1247     * @param rows an array of ints specifying the rows to select,
1248     * where 0 indicates the first row in the display
1249     */

1250    public void setSelectionRows(int[] rows) {
1251        TreeUI JavaDoc ui = getUI();
1252
1253        if(ui != null && rows != null) {
1254            int numRows = rows.length;
1255            TreePath[] paths = new TreePath[numRows];
1256
1257            for(int counter = 0; counter < numRows; counter++) {
1258                paths[counter] = ui.getPathForRow(this, rows[counter]);
1259        }
1260            setSelectionPaths(paths);
1261        }
1262    }
1263
1264    /**
1265     * Adds the node identified by the specified <code>TreePath</code>
1266     * to the current selection. If any component of the path isn't
1267     * viewable, and <code>getExpandsSelectedPaths</code> is true it is
1268     * made viewable.
1269     * <p>
1270     * Note that <code>JTree</code> does not allow duplicate nodes to
1271     * exist as children under the same parent -- each sibling must be
1272     * a unique object.
1273     *
1274     * @param path the <code>TreePath</code> to add
1275     */

1276    public void addSelectionPath(TreePath path) {
1277        getSelectionModel().addSelectionPath(path);
1278    }
1279
1280    /**
1281     * Adds each path in the array of paths to the current selection. If
1282     * any component of any of the paths isn't viewable and
1283     * <code>getExpandsSelectedPaths</code> is true, it is
1284     * made viewable.
1285     * <p>
1286     * Note that <code>JTree</code> does not allow duplicate nodes to
1287     * exist as children under the same parent -- each sibling must be
1288     * a unique object.
1289     *
1290     * @param paths an array of <code>TreePath</code> objects that specifies
1291     * the nodes to add
1292     */

1293    public void addSelectionPaths(TreePath[] paths) {
1294    getSelectionModel().addSelectionPaths(paths);
1295    }
1296
1297    /**
1298     * Adds the path at the specified row to the current selection.
1299     *
1300     * @param row an integer specifying the row of the node to add,
1301     * where 0 is the first row in the display
1302     */

1303    public void addSelectionRow(int row) {
1304        int[] rows = { row };
1305
1306        addSelectionRows(rows);
1307    }
1308
1309    /**
1310     * Adds the paths at each of the specified rows to the current selection.
1311     *
1312     * @param rows an array of ints specifying the rows to add,
1313     * where 0 indicates the first row in the display
1314     */

1315    public void addSelectionRows(int[] rows) {
1316        TreeUI JavaDoc ui = getUI();
1317
1318        if(ui != null && rows != null) {
1319            int numRows = rows.length;
1320            TreePath[] paths = new TreePath[numRows];
1321
1322            for(int counter = 0; counter < numRows; counter++)
1323                paths[counter] = ui.getPathForRow(this, rows[counter]);
1324            addSelectionPaths(paths);
1325        }
1326    }
1327
1328    /**
1329     * Returns the last path component in the first node of the current
1330     * selection.
1331     *
1332     * @return the last <code>Object</code> in the first selected node's
1333     * <code>TreePath</code>,
1334     * or <code>null</code> if nothing is selected
1335     * @see TreePath#getLastPathComponent
1336     */

1337    public Object JavaDoc getLastSelectedPathComponent() {
1338        TreePath selPath = getSelectionModel().getSelectionPath();
1339
1340        if(selPath != null)
1341            return selPath.getLastPathComponent();
1342        return null;
1343    }
1344
1345    /**
1346     * Returns the path identified as the lead.
1347     * @return path identified as the lead
1348     */

1349    public TreePath getLeadSelectionPath() {
1350    return leadPath;
1351    }
1352
1353    /**
1354     * Returns the path identified as the anchor.
1355     * @return path identified as the anchor
1356     * @since 1.3
1357     */

1358    public TreePath getAnchorSelectionPath() {
1359    return anchorPath;
1360    }
1361
1362    /**
1363     * Returns the path to the first selected node.
1364     *
1365     * @return the <code>TreePath</code> for the first selected node,
1366     * or <code>null</code> if nothing is currently selected
1367     */

1368    public TreePath getSelectionPath() {
1369        return getSelectionModel().getSelectionPath();
1370    }
1371
1372    /**
1373     * Returns the paths of all selected values.
1374     *
1375     * @return an array of <code>TreePath</code> objects indicating the selected
1376     * nodes, or <code>null</code> if nothing is currently selected
1377     */

1378    public TreePath[] getSelectionPaths() {
1379        return getSelectionModel().getSelectionPaths();
1380    }
1381
1382    /**
1383     * Returns all of the currently selected rows. This method is simply
1384     * forwarded to the <code>TreeSelectionModel</code>.
1385     * If nothing is selected <code>null</code> or an empty array will
1386     * be returned, based on the <code>TreeSelectionModel</code>
1387     * implementation.
1388     *
1389     * @return an array of integers that identifies all currently selected rows
1390     * where 0 is the first row in the display
1391     */

1392    public int[] getSelectionRows() {
1393        return getSelectionModel().getSelectionRows();
1394    }
1395
1396    /**
1397     * Returns the number of nodes selected.
1398     *
1399     * @return the number of nodes selected
1400     */

1401    public int getSelectionCount() {
1402        return selectionModel.getSelectionCount();
1403    }
1404
1405    /**
1406     * Gets the first selected row.
1407     *
1408     * @return an integer designating the first selected row, where 0 is the
1409     * first row in the display
1410     */

1411    public int getMinSelectionRow() {
1412        return getSelectionModel().getMinSelectionRow();
1413    }
1414
1415    /**
1416     * Returns the last selected row.
1417     *
1418     * @return an integer designating the last selected row, where 0 is the
1419     * first row in the display
1420     */

1421    public int getMaxSelectionRow() {
1422        return getSelectionModel().getMaxSelectionRow();
1423    }
1424
1425    /**
1426     * Returns the row index corresponding to the lead path.
1427     *
1428     * @return an integer giving the row index of the lead path,
1429     * where 0 is the first row in the display; or -1
1430     * if <code>leadPath</code> is <code>null</code>
1431     */

1432    public int getLeadSelectionRow() {
1433    TreePath leadPath = getLeadSelectionPath();
1434
1435    if (leadPath != null) {
1436        return getRowForPath(leadPath);
1437    }
1438        return -1;
1439    }
1440
1441    /**
1442     * Returns true if the item identified by the path is currently selected.
1443     *
1444     * @param path a <code>TreePath</code> identifying a node
1445     * @return true if the node is selected
1446     */

1447    public boolean isPathSelected(TreePath path) {
1448        return getSelectionModel().isPathSelected(path);
1449    }
1450
1451    /**
1452     * Returns true if the node identified by row is selected.
1453     *
1454     * @param row an integer specifying a display row, where 0 is the first
1455     * row in the display
1456     * @return true if the node is selected
1457     */

1458    public boolean isRowSelected(int row) {
1459        return getSelectionModel().isRowSelected(row);
1460    }
1461
1462    /**
1463     * Returns an <code>Enumeration</code> of the descendants of the
1464     * path <code>parent</code> that
1465     * are currently expanded. If <code>parent</code> is not currently
1466     * expanded, this will return <code>null</code>.
1467     * If you expand/collapse nodes while
1468     * iterating over the returned <code>Enumeration</code>
1469     * this may not return all
1470     * the expanded paths, or may return paths that are no longer expanded.
1471     *
1472     * @param parent the path which is to be examined
1473     * @return an <code>Enumeration</code> of the descendents of
1474     * <code>parent</code>, or <code>null</code> if
1475     * <code>parent</code> is not currently expanded
1476     */

1477    public Enumeration<TreePath> getExpandedDescendants(TreePath parent) {
1478    if(!isExpanded(parent))
1479        return null;
1480
1481    Enumeration toggledPaths = expandedState.keys();
1482    Vector elements = null;
1483    TreePath path;
1484    Object JavaDoc value;
1485
1486    if(toggledPaths != null) {
1487        while(toggledPaths.hasMoreElements()) {
1488        path = (TreePath)toggledPaths.nextElement();
1489        value = expandedState.get(path);
1490        // Add the path if it is expanded, a descendant of parent,
1491
// and it is visible (all parents expanded). This is rather
1492
// expensive!
1493
if(path != parent && value != null &&
1494           ((Boolean JavaDoc)value).booleanValue() &&
1495           parent.isDescendant(path) && isVisible(path)) {
1496            if (elements == null) {
1497            elements = new Vector();
1498            }
1499            elements.addElement(path);
1500        }
1501        }
1502    }
1503    if (elements == null) {
1504        Set<TreePath> empty = Collections.emptySet();
1505        return Collections.enumeration(empty);
1506    }
1507    return elements.elements();
1508    }
1509
1510    /**
1511     * Returns true if the node identified by the path has ever been
1512     * expanded.
1513     * @return true if the <code>path</code> has ever been expanded
1514     */

1515    public boolean hasBeenExpanded(TreePath path) {
1516    return (path != null && expandedState.get(path) != null);
1517    }
1518
1519    /**
1520     * Returns true if the node identified by the path is currently expanded,
1521     *
1522     * @param path the <code>TreePath</code> specifying the node to check
1523     * @return false if any of the nodes in the node's path are collapsed,
1524     * true if all nodes in the path are expanded
1525     */

1526    public boolean isExpanded(TreePath path) {
1527    if(path == null)
1528        return false;
1529
1530    // Is this node expanded?
1531
Object JavaDoc value = expandedState.get(path);
1532
1533    if(value == null || !((Boolean JavaDoc)value).booleanValue())
1534        return false;
1535
1536    // It is, make sure its parent is also expanded.
1537
TreePath parentPath = path.getParentPath();
1538
1539    if(parentPath != null)
1540        return isExpanded(parentPath);
1541        return true;
1542    }
1543
1544    /**
1545     * Returns true if the node at the specified display row is currently
1546     * expanded.
1547     *
1548     * @param row the row to check, where 0 is the first row in the
1549     * display
1550     * @return true if the node is currently expanded, otherwise false
1551     */

1552    public boolean isExpanded(int row) {
1553        TreeUI JavaDoc tree = getUI();
1554
1555        if(tree != null) {
1556        TreePath path = tree.getPathForRow(this, row);
1557
1558        if(path != null) {
1559                Boolean JavaDoc value = (Boolean JavaDoc)expandedState.get(path);
1560
1561                return (value != null && value.booleanValue());
1562            }
1563    }
1564        return false;
1565    }
1566
1567    /**
1568     * Returns true if the value identified by path is currently collapsed,
1569     * this will return false if any of the values in path are currently
1570     * not being displayed.
1571     *
1572     * @param path the <code>TreePath</code> to check
1573     * @return true if any of the nodes in the node's path are collapsed,
1574     * false if all nodes in the path are expanded
1575     */

1576    public boolean isCollapsed(TreePath path) {
1577    return !isExpanded(path);
1578    }
1579
1580    /**
1581     * Returns true if the node at the specified display row is collapsed.
1582     *
1583     * @param row the row to check, where 0 is the first row in the
1584     * display
1585     * @return true if the node is currently collapsed, otherwise false
1586     */

1587    public boolean isCollapsed(int row) {
1588    return !isExpanded(row);
1589    }
1590
1591    /**
1592     * Ensures that the node identified by path is currently viewable.
1593     *
1594     * @param path the <code>TreePath</code> to make visible
1595     */

1596    public void makeVisible(TreePath path) {
1597        if(path != null) {
1598        TreePath parentPath = path.getParentPath();
1599
1600        if(parentPath != null) {
1601        expandPath(parentPath);
1602        }
1603        }
1604    }
1605
1606    /**
1607     * Returns true if the value identified by path is currently viewable,
1608     * which means it is either the root or all of its parents are expanded.
1609     * Otherwise, this method returns false.
1610     *
1611     * @return true if the node is viewable, otherwise false
1612     */

1613    public boolean isVisible(TreePath path) {
1614        if(path != null) {
1615        TreePath parentPath = path.getParentPath();
1616
1617        if(parentPath != null)
1618        return isExpanded(parentPath);
1619        // Root.
1620
return true;
1621    }
1622        return false;
1623    }
1624
1625    /**
1626     * Returns the <code>Rectangle</code> that the specified node will be drawn
1627     * into. Returns <code>null</code> if any component in the path is hidden
1628     * (under a collapsed parent).
1629     * <p>
1630     * Note:<br>
1631     * This method returns a valid rectangle, even if the specified
1632     * node is not currently displayed.
1633     *
1634     * @param path the <code>TreePath</code> identifying the node
1635     * @return the <code>Rectangle</code> the node is drawn in,
1636     * or <code>null</code>
1637     */

1638    public Rectangle getPathBounds(TreePath path) {
1639        TreeUI JavaDoc tree = getUI();
1640
1641        if(tree != null)
1642            return tree.getPathBounds(this, path);
1643        return null;
1644    }
1645
1646    /**
1647     * Returns the <code>Rectangle</code> that the node at the specified row is
1648     * drawn in.
1649     *
1650     * @param row the row to be drawn, where 0 is the first row in the
1651     * display
1652     * @return the <code>Rectangle</code> the node is drawn in
1653     */

1654    public Rectangle getRowBounds(int row) {
1655    return getPathBounds(getPathForRow(row));
1656    }
1657
1658    /**
1659     * Makes sure all the path components in path are expanded (except
1660     * for the last path component) and scrolls so that the
1661     * node identified by the path is displayed. Only works when this
1662     * <code>JTree</code> is contained in a <code>JScrollPane</code>.
1663     *
1664     * @param path the <code>TreePath</code> identifying the node to
1665     * bring into view
1666     */

1667    public void scrollPathToVisible(TreePath path) {
1668    if(path != null) {
1669        makeVisible(path);
1670
1671        Rectangle bounds = getPathBounds(path);
1672
1673        if(bounds != null) {
1674        scrollRectToVisible(bounds);
1675        if (accessibleContext != null) {
1676            ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
1677        }
1678        }
1679    }
1680    }
1681
1682    /**
1683     * Scrolls the item identified by row until it is displayed. The minimum
1684     * of amount of scrolling necessary to bring the row into view
1685     * is performed. Only works when this <code>JTree</code> is contained in a
1686     * <code>JScrollPane</code>.
1687     *
1688     * @param row an integer specifying the row to scroll, where 0 is the
1689     * first row in the display
1690     */

1691    public void scrollRowToVisible(int row) {
1692    scrollPathToVisible(getPathForRow(row));
1693    }
1694
1695    /**
1696     * Returns the path for the specified row. If <code>row</code> is
1697     * not visible, <code>null</code> is returned.
1698     *
1699     * @param row an integer specifying a row
1700     * @return the <code>TreePath</code> to the specified node,
1701     * <code>null</code> if <code>row < 0</code>
1702     * or <code>row > getRowCount()</code>
1703     */

1704    public TreePath getPathForRow(int row) {
1705        TreeUI JavaDoc tree = getUI();
1706
1707        if(tree != null)
1708            return tree.getPathForRow(this, row);
1709        return null;
1710    }
1711
1712    /**
1713     * Returns the row that displays the node identified by the specified
1714     * path.
1715     *
1716     * @param path the <code>TreePath</code> identifying a node
1717     * @return an integer specifying the display row, where 0 is the first
1718     * row in the display, or -1 if any of the elements in path
1719     * are hidden under a collapsed parent.
1720     */

1721    public int getRowForPath(TreePath path) {
1722        TreeUI JavaDoc tree = getUI();
1723
1724        if(tree != null)
1725            return tree.getRowForPath(this, path);
1726        return -1;
1727    }
1728
1729    /**
1730     * Ensures that the node identified by the specified path is
1731     * expanded and viewable. If the last item in the path is a
1732     * leaf, this will have no effect.
1733     *
1734     * @param path the <code>TreePath</code> identifying a node
1735     */

1736    public void expandPath(TreePath path) {
1737    // Only expand if not leaf!
1738
TreeModel model = getModel();
1739
1740    if(path != null && model != null &&
1741       !model.isLeaf(path.getLastPathComponent())) {
1742        setExpandedState(path, true);
1743    }
1744    }
1745
1746    /**
1747     * Ensures that the node in the specified row is expanded and
1748     * viewable.
1749     * <p>
1750     * If <code>row</code> is < 0 or >= <code>getRowCount</code> this
1751     * will have no effect.
1752     *
1753     * @param row an integer specifying a display row, where 0 is the
1754     * first row in the display
1755     */

1756    public void expandRow(int row) {
1757    expandPath(getPathForRow(row));
1758    }
1759
1760    /**
1761     * Ensures that the node identified by the specified path is
1762     * collapsed and viewable.
1763     *
1764     * @param path the <code>TreePath</code> identifying a node
1765      */

1766    public void collapsePath(TreePath path) {
1767    setExpandedState(path, false);
1768    }
1769
1770    /**
1771     * Ensures that the node in the specified row is collapsed.
1772     * <p>
1773     * If <code>row</code> is < 0 or >= <code>getRowCount</code> this
1774     * will have no effect.
1775     *
1776     * @param row an integer specifying a display row, where 0 is the
1777     * first row in the display
1778      */

1779    public void collapseRow(int row) {
1780    collapsePath(getPathForRow(row));
1781    }
1782
1783    /**
1784     * Returns the path for the node at the specified location.
1785     *
1786     * @param x an integer giving the number of pixels horizontally from
1787     * the left edge of the display area, minus any left margin
1788     * @param y an integer giving the number of pixels vertically from
1789     * the top of the display area, minus any top margin
1790     * @return the <code>TreePath</code> for the node at that location
1791     */

1792    public TreePath getPathForLocation(int x, int y) {
1793        TreePath closestPath = getClosestPathForLocation(x, y);
1794
1795        if(closestPath != null) {
1796            Rectangle pathBounds = getPathBounds(closestPath);
1797
1798            if(pathBounds != null &&
1799               x >= pathBounds.x && x < (pathBounds.x + pathBounds.width) &&
1800               y >= pathBounds.y && y < (pathBounds.y + pathBounds.height))
1801                return closestPath;
1802        }
1803        return null;
1804    }
1805
1806    /**
1807     * Returns the row for the specified location.
1808     *
1809     * @param x an integer giving the number of pixels horizontally from
1810     * the left edge of the display area, minus any left margin
1811     * @param y an integer giving the number of pixels vertically from
1812     * the top of the display area, minus any top margin
1813     * @return the row corresponding to the location, or -1 if the
1814     * location is not within the bounds of a displayed cell
1815     * @see #getClosestRowForLocation
1816     */

1817    public int getRowForLocation(int x, int y) {
1818    return getRowForPath(getPathForLocation(x, y));
1819    }
1820
1821    /**
1822     * Returns the path to the node that is closest to x,y. If
1823     * no nodes are currently viewable, or there is no model, returns
1824     * <code>null</code>, otherwise it always returns a valid path. To test if
1825     * the node is exactly at x, y, get the node's bounds and
1826     * test x, y against that.
1827     *
1828     * @param x an integer giving the number of pixels horizontally from
1829     * the left edge of the display area, minus any left margin
1830     * @param y an integer giving the number of pixels vertically from
1831     * the top of the display area, minus any top margin
1832     * @return the <code>TreePath</code> for the node closest to that location,
1833     * <code>null</code> if nothing is viewable or there is no model
1834     *
1835     * @see #getPathForLocation
1836     * @see #getPathBounds
1837     */

1838    public TreePath getClosestPathForLocation(int x, int y) {
1839        TreeUI JavaDoc tree = getUI();
1840
1841        if(tree != null)
1842            return tree.getClosestPathForLocation(this, x, y);
1843        return null;
1844    }
1845
1846    /**
1847     * Returns the row to the node that is closest to x,y. If no nodes
1848     * are viewable or there is no model, returns -1. Otherwise,
1849     * it always returns a valid row. To test if the returned object is
1850     * exactly at x, y, get the bounds for the node at the returned
1851     * row and test x, y against that.
1852     *
1853     * @param x an integer giving the number of pixels horizontally from
1854     * the left edge of the display area, minus any left margin
1855     * @param y an integer giving the number of pixels vertically from
1856     * the top of the display area, minus any top margin
1857     * @return the row closest to the location, -1 if nothing is
1858     * viewable or there is no model
1859     *
1860     * @see #getRowForLocation
1861     * @see #getRowBounds
1862     */

1863    public int getClosestRowForLocation(int x, int y) {
1864    return getRowForPath(getClosestPathForLocation(x, y));
1865    }
1866
1867    /**
1868     * Returns true if the tree is being edited. The item that is being
1869     * edited can be obtained using <code>getSelectionPath</code>.
1870     *
1871     * @return true if the user is currently editing a node
1872     * @see #getSelectionPath
1873     */

1874    public boolean isEditing() {
1875        TreeUI JavaDoc tree = getUI();
1876
1877        if(tree != null)
1878            return tree.isEditing(this);
1879        return false;
1880    }
1881
1882    /**
1883     * Ends the current editing session.
1884     * (The <code>DefaultTreeCellEditor</code>
1885     * object saves any edits that are currently in progress on a cell.
1886     * Other implementations may operate differently.)
1887     * Has no effect if the tree isn't being edited.
1888     * <blockquote>
1889     * <b>Note:</b><br>
1890     * To make edit-saves automatic whenever the user changes
1891     * their position in the tree, use {@link #setInvokesStopCellEditing}.
1892     * </blockquote>
1893     *
1894     * @return true if editing was in progress and is now stopped,
1895     * false if editing was not in progress
1896     */

1897    public boolean stopEditing() {
1898        TreeUI JavaDoc tree = getUI();
1899
1900        if(tree != null)
1901            return tree.stopEditing(this);
1902        return false;
1903    }
1904
1905    /**
1906     * Cancels the current editing session. Has no effect if the
1907     * tree isn't being edited.
1908     */

1909    public void cancelEditing() {
1910        TreeUI JavaDoc tree = getUI();
1911
1912        if(tree != null)
1913        tree.cancelEditing(this);
1914    }
1915
1916    /**
1917     * Selects the node identified by the specified path and initiates
1918     * editing. The edit-attempt fails if the <code>CellEditor</code>
1919     * does not allow
1920     * editing for the specified item.
1921     *
1922     * @param path the <code>TreePath</code> identifying a node
1923     */

1924    public void startEditingAtPath(TreePath path) {
1925        TreeUI JavaDoc tree = getUI();
1926
1927        if(tree != null)
1928            tree.startEditingAtPath(this, path);
1929    }
1930
1931    /**
1932     * Returns the path to the element that is currently being edited.
1933     *
1934     * @return the <code>TreePath</code> for the node being edited
1935     */

1936    public TreePath getEditingPath() {
1937        TreeUI JavaDoc tree = getUI();
1938
1939        if(tree != null)
1940            return tree.getEditingPath(this);
1941        return null;
1942    }
1943
1944    //
1945
// Following are primarily convenience methods for mapping from
1946
// row based selections to path selections. Sometimes it is
1947
// easier to deal with these than paths (mouse downs, key downs
1948
// usually just deal with index based selections).
1949
// Since row based selections require a UI many of these won't work
1950
// without one.
1951
//
1952

1953    /**
1954     * Sets the tree's selection model. When a <code>null</code> value is
1955     * specified an emtpy
1956     * <code>selectionModel</code> is used, which does not allow selections.
1957     *
1958     * @param selectionModel the <code>TreeSelectionModel</code> to use,
1959     * or <code>null</code> to disable selections
1960     * @see TreeSelectionModel
1961     * @beaninfo
1962     * bound: true
1963     * description: The tree's selection model.
1964     */

1965    public void setSelectionModel(TreeSelectionModel selectionModel) {
1966        if(selectionModel == null)
1967            selectionModel = EmptySelectionModel.sharedInstance();
1968
1969        TreeSelectionModel oldValue = this.selectionModel;
1970
1971    if (this.selectionModel != null && selectionRedirector != null) {
1972            this.selectionModel.removeTreeSelectionListener
1973                        (selectionRedirector);
1974    }
1975        if (accessibleContext != null) {
1976           this.selectionModel.removeTreeSelectionListener((TreeSelectionListener)accessibleContext);
1977           selectionModel.addTreeSelectionListener((TreeSelectionListener)accessibleContext);
1978        }
1979
1980        this.selectionModel = selectionModel;
1981    if (selectionRedirector != null) {
1982            this.selectionModel.addTreeSelectionListener(selectionRedirector);
1983    }
1984        firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue,
1985                           this.selectionModel);
1986
1987        if (accessibleContext != null) {
1988            accessibleContext.firePropertyChange(
1989                    AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
1990                    Boolean.valueOf(false), Boolean.valueOf(true));
1991        }
1992    }
1993
1994    /**
1995     * Returns the model for selections. This should always return a
1996     * non-<code>null</code> value. If you don't want to allow anything
1997     * to be selected
1998     * set the selection model to <code>null</code>, which forces an empty
1999     * selection model to be used.
2000     *
2001     * @see #setSelectionModel
2002     */

2003    public TreeSelectionModel getSelectionModel() {
2004        return selectionModel;
2005    }
2006
2007    /**
2008     * Returns <code>JTreePath</code> instances representing the path
2009     * between index0 and index1 (including index1).
2010     * Returns <code>null</code> if there is no tree.
2011     *
2012     * @param index0 an integer specifying a display row, where 0 is the
2013     * first row in the display
2014     * @param index1 an integer specifying a second display row
2015     * @return an array of <code>TreePath</code> objects, one for each
2016     * node between index0 and index1, inclusive; or <code>null</code>
2017     * if there is no tree
2018     */

2019    protected TreePath[] getPathBetweenRows(int index0, int index1) {
2020        int newMinIndex, newMaxIndex;
2021        TreeUI JavaDoc tree = getUI();
2022
2023        newMinIndex = Math.min(index0, index1);
2024        newMaxIndex = Math.max(index0, index1);
2025
2026        if(tree != null) {
2027            TreePath[] selection = new TreePath[newMaxIndex -
2028                                                            newMinIndex + 1];
2029
2030            for(int counter = newMinIndex; counter <= newMaxIndex; counter++)
2031                selection[counter - newMinIndex] = tree.getPathForRow(this,
2032                                      counter);
2033            return selection;
2034        }
2035        return null;
2036    }
2037
2038    /**
2039     * Selects the nodes between index0 and index1, inclusive.
2040     *
2041     * @param index0 an integer specifying a display row, where 0 is the
2042     * first row in the display
2043     * @param index1 an integer specifying a second display row
2044    */

2045    public void setSelectionInterval(int index0, int index1) {
2046        TreePath[] paths = getPathBetweenRows(index0, index1);
2047
2048        this.getSelectionModel().setSelectionPaths(paths);
2049    }
2050
2051    /**
2052     * Adds the paths between index0 and index1, inclusive, to the
2053     * selection.
2054     *
2055     * @param index0 an integer specifying a display row, where 0 is the
2056     * first row in the display
2057     * @param index1 an integer specifying a second display row
2058     */

2059    public void addSelectionInterval(int index0, int index1) {
2060        TreePath[] paths = getPathBetweenRows(index0, index1);
2061
2062        this.getSelectionModel().addSelectionPaths(paths);
2063    }
2064
2065    /**
2066     * Removes the nodes between index0 and index1, inclusive, from the
2067     * selection.
2068     *
2069     * @param index0 an integer specifying a display row, where 0 is the
2070     * first row in the display
2071     * @param index1 an integer specifying a second display row
2072     */

2073    public void removeSelectionInterval(int index0, int index1) {
2074        TreePath[] paths = getPathBetweenRows(index0, index1);
2075
2076        this.getSelectionModel().removeSelectionPaths(paths);
2077    }
2078
2079    /**
2080     * Removes the node identified by the specified path from the current
2081     * selection.
2082     *
2083     * @param path the <code>TreePath</code> identifying a node
2084     */

2085    public void removeSelectionPath(TreePath path) {
2086        this.getSelectionModel().removeSelectionPath(path);
2087    }
2088
2089    /**
2090     * Removes the nodes identified by the specified paths from the
2091     * current selection.
2092     *
2093     * @param paths an array of <code>TreePath</code> objects that
2094     * specifies the nodes to remove
2095     */

2096    public void removeSelectionPaths(TreePath[] paths) {
2097        this.getSelectionModel().removeSelectionPaths(paths);
2098    }
2099
2100    /**
2101     * Removes the row at the index <code>row</code> from the current
2102     * selection.
2103     *
2104     * @param row the row to remove
2105     */

2106    public void removeSelectionRow(int row) {
2107        int[] rows = { row };
2108
2109        removeSelectionRows(rows);
2110    }
2111
2112    /**
2113     * Removes the rows that are selected at each of the specified
2114     * rows.
2115     *
2116     * @param rows an array of ints specifying display rows, where 0 is
2117     * the first row in the display
2118     */

2119    public void removeSelectionRows(int[] rows) {
2120        TreeUI JavaDoc ui = getUI();
2121
2122        if(ui != null && rows != null) {
2123            int numRows = rows.length;
2124            TreePath[] paths = new TreePath[numRows];
2125
2126            for(int counter = 0; counter < numRows; counter++)
2127                paths[counter] = ui.getPathForRow(this, rows[counter]);
2128            removeSelectionPaths(paths);
2129        }
2130    }
2131
2132    /**
2133     * Clears the selection.
2134     */

2135    public void clearSelection() {
2136        getSelectionModel().clearSelection();
2137    }
2138
2139    /**
2140     * Returns true if the selection is currently empty.
2141     *
2142     * @return true if the selection is currently empty
2143     */

2144    public boolean isSelectionEmpty() {
2145        return getSelectionModel().isSelectionEmpty();
2146    }
2147
2148    /**
2149     * Adds a listener for <code>TreeExpansion</code> events.
2150     *
2151     * @param tel a TreeExpansionListener that will be notified when
2152     * a tree node is expanded or collapsed (a "negative
2153     * expansion")
2154     */

2155    public void addTreeExpansionListener(TreeExpansionListener tel) {
2156    if (settingUI) {
2157        uiTreeExpansionListener = tel;
2158    }
2159        listenerList.add(TreeExpansionListener.class, tel);
2160    }
2161
2162    /**
2163     * Removes a listener for <code>TreeExpansion</code> events.
2164     *
2165     * @param tel the <code>TreeExpansionListener</code> to remove
2166     */

2167    public void removeTreeExpansionListener(TreeExpansionListener tel) {
2168        listenerList.remove(TreeExpansionListener.class, tel);
2169    if (uiTreeExpansionListener == tel) {
2170        uiTreeExpansionListener = null;
2171    }
2172    }
2173
2174    /**
2175     * Returns an array of all the <code>TreeExpansionListener</code>s added
2176     * to this JTree with addTreeExpansionListener().
2177     *
2178     * @return all of the <code>TreeExpansionListener</code>s added or an empty
2179     * array if no listeners have been added
2180     * @since 1.4
2181     */

2182    public TreeExpansionListener[] getTreeExpansionListeners() {
2183        return (TreeExpansionListener[])listenerList.getListeners(
2184                TreeExpansionListener.class);
2185    }
2186
2187    /**
2188     * Adds a listener for <code>TreeWillExpand</code> events.
2189     *
2190     * @param tel a <code>TreeWillExpandListener</code> that will be notified
2191     * when a tree node will be expanded or collapsed (a "negative
2192     * expansion")
2193     */

2194    public void addTreeWillExpandListener(TreeWillExpandListener tel) {
2195        listenerList.add(TreeWillExpandListener.class, tel);
2196    }
2197
2198    /**
2199     * Removes a listener for <code>TreeWillExpand</code> events.
2200     *
2201     * @param tel the <code>TreeWillExpandListener</code> to remove
2202     */

2203    public void removeTreeWillExpandListener(TreeWillExpandListener tel) {
2204        listenerList.remove(TreeWillExpandListener.class, tel);
2205    }
2206
2207    /**
2208     * Returns an array of all the <code>TreeWillExpandListener</code>s added
2209     * to this JTree with addTreeWillExpandListener().
2210     *
2211     * @return all of the <code>TreeWillExpandListener</code>s added or an empty
2212     * array if no listeners have been added
2213     * @since 1.4
2214     */

2215    public TreeWillExpandListener[] getTreeWillExpandListeners() {
2216        return (TreeWillExpandListener[])listenerList.getListeners(
2217                TreeWillExpandListener.class);
2218    }
2219
2220    /**
2221     * Notifies all listeners that have registered interest for
2222     * notification on this event type. The event instance
2223     * is lazily created using the <code>path</code> parameter.
2224     *
2225     * @param path the <code>TreePath</code> indicating the node that was
2226     * expanded
2227     * @see EventListenerList
2228     */

2229     public void fireTreeExpanded(TreePath path) {
2230        // Guaranteed to return a non-null array
2231
Object JavaDoc[] listeners = listenerList.getListenerList();
2232        TreeExpansionEvent e = null;
2233    if (uiTreeExpansionListener != null) {
2234        e = new TreeExpansionEvent(this, path);
2235        uiTreeExpansionListener.treeExpanded(e);
2236    }
2237        // Process the listeners last to first, notifying
2238
// those that are interested in this event
2239
for (int i = listeners.length-2; i>=0; i-=2) {
2240            if (listeners[i]==TreeExpansionListener.class &&
2241        listeners[i + 1] != uiTreeExpansionListener) {
2242                // Lazily create the event:
2243
if (e == null)
2244                    e = new TreeExpansionEvent(this, path);
2245                ((TreeExpansionListener)listeners[i+1]).
2246                    treeExpanded(e);
2247            }
2248        }
2249    }
2250
2251    /**
2252     * Notifies all listeners that have registered interest for
2253     * notification on this event type. The event instance
2254     * is lazily created using the <code>path</code> parameter.
2255     *
2256     * @param path the <code>TreePath</code> indicating the node that was
2257     * collapsed
2258     * @see EventListenerList
2259     */

2260    public void fireTreeCollapsed(TreePath path) {
2261        // Guaranteed to return a non-null array
2262
Object JavaDoc[] listeners = listenerList.getListenerList();
2263        TreeExpansionEvent e = null;
2264    if (uiTreeExpansionListener != null) {
2265        e = new TreeExpansionEvent(this, path);
2266        uiTreeExpansionListener.treeCollapsed(e);
2267    }
2268        // Process the listeners last to first, notifying
2269
// those that are interested in this event
2270
for (int i = listeners.length-2; i>=0; i-=2) {
2271            if (listeners[i]==TreeExpansionListener.class &&
2272        listeners[i + 1] != uiTreeExpansionListener) {
2273                // Lazily create the event:
2274
if (e == null)
2275                    e = new TreeExpansionEvent(this, path);
2276                ((TreeExpansionListener)listeners[i+1]).
2277                    treeCollapsed(e);
2278            }
2279        }
2280    }
2281
2282    /**
2283     * Notifies all listeners that have registered interest for
2284     * notification on this event type. The event instance
2285     * is lazily created using the <code>path</code> parameter.
2286     *
2287     * @param path the <code>TreePath</code> indicating the node that was
2288     * expanded
2289     * @see EventListenerList
2290     */

2291     public void fireTreeWillExpand(TreePath path) throws ExpandVetoException {
2292        // Guaranteed to return a non-null array
2293
Object JavaDoc[] listeners = listenerList.getListenerList();
2294        TreeExpansionEvent e = null;
2295        // Process the listeners last to first, notifying
2296
// those that are interested in this event
2297
for (int i = listeners.length-2; i>=0; i-=2) {
2298            if (listeners[i]==TreeWillExpandListener.class) {
2299                // Lazily create the event:
2300
if (e == null)
2301                    e = new TreeExpansionEvent(this, path);
2302                ((TreeWillExpandListener)listeners[i+1]).
2303                    treeWillExpand(e);
2304            }
2305        }
2306    }
2307
2308    /**
2309     * Notifies all listeners that have registered interest for
2310     * notification on this event type. The event instance
2311     * is lazily created using the <code>path</code> parameter.
2312     *
2313     * @param path the <code>TreePath</code> indicating the node that was
2314     * expanded
2315     * @see EventListenerList
2316     */

2317     public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException {
2318        // Guaranteed to return a non-null array
2319
Object JavaDoc[] listeners = listenerList.getListenerList();
2320        TreeExpansionEvent e = null;
2321        // Process the listeners last to first, notifying
2322
// those that are interested in this event
2323
for (int i = listeners.length-2; i>=0; i-=2) {
2324            if (listeners[i]==TreeWillExpandListener.class) {
2325                // Lazily create the event:
2326
if (e == null)
2327                    e = new TreeExpansionEvent(this, path);
2328                ((TreeWillExpandListener)listeners[i+1]).
2329                    treeWillCollapse(e);
2330            }
2331        }
2332    }
2333
2334    /**
2335     * Adds a listener for <code>TreeSelection</code> events.
2336     *
2337     * @param tsl the <code>TreeSelectionListener</code> that will be notified
2338     * when a node is selected or deselected (a "negative
2339     * selection")
2340     */

2341    public void addTreeSelectionListener(TreeSelectionListener tsl) {
2342        listenerList.add(TreeSelectionListener.class,tsl);
2343        if(listenerList.getListenerCount(TreeSelectionListener.class) != 0
2344           && selectionRedirector == null) {
2345            selectionRedirector = new TreeSelectionRedirector();
2346            selectionModel.addTreeSelectionListener(selectionRedirector);
2347        }
2348    }
2349
2350    /**
2351     * Removes a <code>TreeSelection</code> listener.
2352     *
2353     * @param tsl the <code>TreeSelectionListener</code> to remove
2354     */

2355    public void removeTreeSelectionListener(TreeSelectionListener tsl) {
2356        listenerList.remove(TreeSelectionListener.class,tsl);
2357        if(listenerList.getListenerCount(TreeSelectionListener.class) == 0
2358           && selectionRedirector != null) {
2359            selectionModel.removeTreeSelectionListener
2360                (selectionRedirector);
2361            selectionRedirector = null;
2362        }
2363    }
2364
2365    /**
2366     * Returns an array of all the <code>TreeSelectionListener</code>s added
2367     * to this JTree with addTreeSelectionListener().
2368     *
2369     * @return all of the <code>TreeSelectionListener</code>s added or an empty
2370     * array if no listeners have been added
2371     * @since 1.4
2372     */

2373    public TreeSelectionListener[] getTreeSelectionListeners() {
2374        return (TreeSelectionListener[])listenerList.getListeners(
2375                TreeSelectionListener.class);
2376    }
2377
2378    /**
2379     * Notifies all listeners that have registered interest for
2380     * notification on this event type.
2381     *
2382     * @param e the <code>TreeSelectionEvent</code> to be fired;
2383     * generated by the
2384     * <code>TreeSelectionModel</code>
2385     * when a node is selected or deselected
2386     * @see EventListenerList
2387     */

2388    protected void fireValueChanged(TreeSelectionEvent e) {
2389        // Guaranteed to return a non-null array
2390
Object JavaDoc[] listeners = listenerList.getListenerList();
2391        // Process the listeners last to first, notifying
2392
// those that are interested in this event
2393
for (int i = listeners.length-2; i>=0; i-=2) {
2394            // TreeSelectionEvent e = null;
2395
if (listeners[i]==TreeSelectionListener.class) {
2396                // Lazily create the event:
2397
// if (e == null)
2398
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
2399
((TreeSelectionListener)listeners[i+1]).valueChanged(e);
2400            }
2401        }
2402    }
2403
2404    /**
2405     * Sent when the tree has changed enough that we need to resize
2406     * the bounds, but not enough that we need to remove the
2407     * expanded node set (e.g nodes were expanded or collapsed, or
2408     * nodes were inserted into the tree). You should never have to
2409     * invoke this, the UI will invoke this as it needs to.
2410     */

2411    public void treeDidChange() {
2412        revalidate();
2413        repaint();
2414    }
2415
2416    /**
2417     * Sets the number of rows that are to be displayed.
2418     * This will only work if the tree is contained in a
2419     * <code>JScrollPane</code>,
2420     * and will adjust the preferred size and size of that scrollpane.
2421     *
2422     * @param newCount the number of rows to display
2423     * @beaninfo
2424     * bound: true
2425     * description: The number of rows that are to be displayed.
2426     */

2427    public void setVisibleRowCount(int newCount) {
2428        int oldCount = visibleRowCount;
2429
2430        visibleRowCount = newCount;
2431        firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldCount,
2432                           visibleRowCount);
2433        invalidate();
2434        if (accessibleContext != null) {
2435            ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
2436        }
2437    }
2438
2439    /**
2440     * Returns the number of rows that are displayed in the display area.
2441     *
2442     * @return the number of rows displayed
2443     */

2444    public int getVisibleRowCount() {
2445        return visibleRowCount;
2446    }
2447
2448    /**
2449     * Expands the root path, assuming the current TreeModel has been set.
2450     */

2451    private void expandRoot() {
2452    TreeModel model = getModel();
2453
2454    if(model != null && model.getRoot() != null) {
2455        expandPath(new TreePath(model.getRoot()));
2456    }
2457    }
2458
2459    /**
2460     * Returns the TreePath to the next tree element that
2461     * begins with a prefix. To handle the conversion of a
2462     * <code>TreePath</code> into a String, <code>convertValueToText</code>
2463     * is used.
2464     *
2465     * @param prefix the string to test for a match
2466     * @param startingRow the row for starting the search
2467     * @param bias the search direction, either
2468     * Position.Bias.Forward or Position.Bias.Backward.
2469     * @return the TreePath of the next tree element that
2470     * starts with the prefix; otherwise null
2471     * @exception IllegalArgumentException if prefix is null
2472     * or startingRow is out of bounds
2473     * @since 1.4
2474     */

2475    public TreePath getNextMatch(String JavaDoc prefix, int startingRow,
2476                 Position.Bias JavaDoc bias) {
2477
2478        int max = getRowCount();
2479    if (prefix == null) {
2480        throw new IllegalArgumentException JavaDoc();
2481    }
2482    if (startingRow < 0 || startingRow >= max) {
2483        throw new IllegalArgumentException JavaDoc();
2484    }
2485    prefix = prefix.toUpperCase();
2486
2487    // start search from the next/previous element froom the
2488
// selected element
2489
int increment = (bias == Position.Bias.Forward) ? 1 : -1;
2490    int row = startingRow;
2491    do {
2492        TreePath path = getPathForRow(row);
2493        String JavaDoc text = convertValueToText(
2494            path.getLastPathComponent(), isRowSelected(row),
2495        isExpanded(row), true, row, false);
2496        
2497        if (text.toUpperCase().startsWith(prefix)) {
2498        return path;
2499        }
2500        row = (row + increment + max) % max;
2501    } while (row != startingRow);
2502    return null;
2503    }
2504
2505    // Serialization support.
2506
private void writeObject(ObjectOutputStream s) throws IOException {
2507        Vector values = new Vector();
2508
2509        s.defaultWriteObject();
2510        // Save the cellRenderer, if its Serializable.
2511
if(cellRenderer != null && cellRenderer instanceof Serializable) {
2512            values.addElement("cellRenderer");
2513            values.addElement(cellRenderer);
2514        }
2515        // Save the cellEditor, if its Serializable.
2516
if(cellEditor != null && cellEditor instanceof Serializable) {
2517            values.addElement("cellEditor");
2518            values.addElement(cellEditor);
2519        }
2520        // Save the treeModel, if its Serializable.
2521
if(treeModel != null && treeModel instanceof Serializable) {
2522            values.addElement("treeModel");
2523            values.addElement(treeModel);
2524        }
2525        // Save the selectionModel, if its Serializable.
2526
if(selectionModel != null && selectionModel instanceof Serializable) {
2527            values.addElement("selectionModel");
2528            values.addElement(selectionModel);
2529        }
2530
2531    Object JavaDoc expandedData = getArchivableExpandedState();
2532
2533    if(expandedData != null) {
2534            values.addElement("expandedState");
2535            values.addElement(expandedData);
2536    }
2537
2538        s.writeObject(values);
2539        if (getUIClassID().equals(uiClassID)) {
2540            byte count = JComponent.getWriteObjCounter(this);
2541            JComponent.setWriteObjCounter(this, --count);
2542            if (count == 0 && ui != null) {
2543                ui.installUI(this);
2544            }
2545        }
2546    }
2547
2548    private void readObject(ObjectInputStream s)
2549        throws IOException, ClassNotFoundException JavaDoc {
2550        s.defaultReadObject();
2551
2552    // Create an instance of expanded state.
2553

2554    expandedState = new Hashtable();
2555
2556    expandedStack = new Stack();
2557
2558        Vector values = (Vector)s.readObject();
2559        int indexCounter = 0;
2560        int maxCounter = values.size();
2561
2562        if(indexCounter < maxCounter && values.elementAt(indexCounter).
2563           equals("cellRenderer")) {
2564            cellRenderer = (TreeCellRenderer)values.elementAt(++indexCounter);
2565            indexCounter++;
2566        }
2567        if(indexCounter < maxCounter && values.elementAt(indexCounter).
2568           equals("cellEditor")) {
2569            cellEditor = (TreeCellEditor)values.elementAt(++indexCounter);
2570            indexCounter++;
2571        }
2572        if(indexCounter < maxCounter && values.elementAt(indexCounter).
2573           equals("treeModel")) {
2574            treeModel = (TreeModel)values.elementAt(++indexCounter);
2575            indexCounter++;
2576        }
2577        if(indexCounter < maxCounter && values.elementAt(indexCounter).
2578           equals("selectionModel")) {
2579            selectionModel = (TreeSelectionModel)values.elementAt(++indexCounter);
2580            indexCounter++;
2581        }
2582        if(indexCounter < maxCounter && values.elementAt(indexCounter).
2583           equals("expandedState")) {
2584        unarchiveExpandedState(values.elementAt(++indexCounter));
2585            indexCounter++;
2586        }
2587    // Reinstall the redirector.
2588
if(listenerList.getListenerCount(TreeSelectionListener.class) != 0) {
2589            selectionRedirector = new TreeSelectionRedirector();
2590            selectionModel.addTreeSelectionListener(selectionRedirector);
2591        }
2592    // Listener to TreeModel.
2593
if(treeModel != null) {
2594        treeModelListener = createTreeModelListener();
2595        if(treeModelListener != null)
2596        treeModel.addTreeModelListener(treeModelListener);
2597    }
2598    }
2599
2600    /**
2601     * Returns an object that can be archived indicating what nodes are
2602     * expanded and what aren't. The objects from the model are NOT
2603     * written out.
2604     */

2605    private Object JavaDoc getArchivableExpandedState() {
2606    TreeModel model = getModel();
2607
2608    if(model != null) {
2609        Enumeration paths = expandedState.keys();
2610
2611        if(paths != null) {
2612        Vector state = new Vector();
2613
2614        while(paths.hasMoreElements()) {
2615            TreePath path = (TreePath)paths.nextElement();
2616            Object JavaDoc archivePath;
2617
2618            try {
2619            archivePath = getModelIndexsForPath(path);
2620            } catch (Error JavaDoc error) {
2621            archivePath = null;
2622            }
2623            if(archivePath != null) {
2624            state.addElement(archivePath);
2625            state.addElement(expandedState.get(path));
2626            }
2627        }
2628        return state;
2629        }
2630    }
2631    return null;
2632    }
2633
2634    /**
2635     * Updates the expanded state of nodes in the tree based on the
2636     * previously archived state <code>state</code>.
2637     */

2638    private void unarchiveExpandedState(Object JavaDoc state) {
2639    if(state instanceof Vector) {
2640        Vector paths = (Vector)state;
2641
2642        for(int counter = paths.size() - 1; counter >= 0; counter--) {
2643        Boolean JavaDoc eState = (Boolean JavaDoc)paths.elementAt(counter--);
2644        TreePath path;
2645
2646        try {
2647            path = getPathForIndexs((int[])paths.elementAt(counter));
2648            if(path != null)
2649            expandedState.put(path, eState);
2650        } catch (Error JavaDoc error) {}
2651        }
2652    }
2653    }
2654
2655    /**
2656     * Returns an array of integers specifying the indexs of the
2657     * components in the <code>path</code>. If <code>path</code> is
2658     * the root, this will return an empty array. If <code>path</code>
2659     * is <code>null</code>, <code>null</code> will be returned.
2660     */

2661    private int[] getModelIndexsForPath(TreePath path) {
2662    if(path != null) {
2663        TreeModel model = getModel();
2664        int count = path.getPathCount();
2665        int[] indexs = new int[count - 1];
2666        Object JavaDoc parent = model.getRoot();
2667
2668        for(int counter = 1; counter < count; counter++) {
2669        indexs[counter - 1] = model.getIndexOfChild
2670                       (parent, path.getPathComponent(counter));
2671        parent = path.getPathComponent(counter);
2672        if(indexs[counter - 1] < 0)
2673            return null;
2674        }
2675        return indexs;
2676    }
2677    return null;
2678    }
2679
2680    /**
2681     * Returns a <code>TreePath</code> created by obtaining the children
2682     * for each of the indices in <code>indexs</code>. If <code>indexs</code>
2683     * or the <code>TreeModel</code> is <code>null</code>, it will return
2684     * <code>null</code>.
2685     */

2686    private TreePath getPathForIndexs(int[] indexs) {
2687    if(indexs == null)
2688        return null;
2689
2690    TreeModel model = getModel();
2691
2692    if(model == null)
2693        return null;
2694
2695    int count = indexs.length;
2696    Object JavaDoc parent = model.getRoot();
2697    TreePath parentPath = new TreePath(parent);
2698
2699    for(int counter = 0; counter < count; counter++) {
2700        parent = model.getChild(parent, indexs[counter]);
2701        if(parent == null)
2702        return null;
2703        parentPath = parentPath.pathByAddingChild(parent);
2704    }
2705    return parentPath;
2706    }
2707
2708    /**
2709     * <code>EmptySelectionModel</code> is a <code>TreeSelectionModel</code>
2710     * that does not allow anything to be selected.
2711     * <p>
2712     * <strong>Warning:</strong>
2713     * Serialized objects of this class will not be compatible with
2714     * future Swing releases. The current serialization support is
2715     * appropriate for short term storage or RMI between applications running
2716     * the same version of Swing. As of 1.4, support for long term storage
2717     * of all JavaBeans<sup><font size="-2">TM</font></sup>
2718     * has been added to the <code>java.beans</code> package.
2719     * Please see {@link java.beans.XMLEncoder}.
2720     */

2721    protected static class EmptySelectionModel extends
2722              DefaultTreeSelectionModel
2723    {
2724        /** Unique shared instance. */
2725        protected static final EmptySelectionModel sharedInstance =
2726            new EmptySelectionModel();
2727
2728        /** Returns a shared instance of an empty selection model. */
2729        static public EmptySelectionModel sharedInstance() {
2730            return sharedInstance;
2731        }
2732
2733        /** A <code>null</code> implementation that selects nothing. */
2734        public void setSelectionPaths(TreePath[] pPaths) {}
2735        /** A <code>null</code> implementation that adds nothing. */
2736        public void addSelectionPaths(TreePath[] paths) {}
2737        /** A <code>null</code> implementation that removes nothing. */
2738        public void removeSelectionPaths(TreePath[] paths) {}
2739    }
2740
2741
2742    /**
2743     * Handles creating a new <code>TreeSelectionEvent</code> with the
2744     * <code>JTree</code> as the
2745     * source and passing it off to all the listeners.
2746     * <p>
2747     * <strong>Warning:</strong>
2748     * Serialized objects of this class will not be compatible with
2749     * future Swing releases. The current serialization support is
2750     * appropriate for short term storage or RMI between applications running
2751     * the same version of Swing. As of 1.4, support for long term storage
2752     * of all JavaBeans<sup><font size="-2">TM</font></sup>
2753     * has been added to the <code>java.beans</code> package.
2754     * Please see {@link java.beans.XMLEncoder}.
2755     */

2756    protected class TreeSelectionRedirector implements Serializable,
2757                    TreeSelectionListener
2758    {
2759        /**
2760         * Invoked by the <code>TreeSelectionModel</code> when the
2761         * selection changes.
2762         *
2763         * @param e the <code>TreeSelectionEvent</code> generated by the
2764         * <code>TreeSelectionModel</code>
2765         */

2766        public void valueChanged(TreeSelectionEvent e) {
2767            TreeSelectionEvent newE;
2768
2769            newE = (TreeSelectionEvent)e.cloneWithSource(JTree.this);
2770            fireValueChanged(newE);
2771        }
2772    } // End of class JTree.TreeSelectionRedirector
2773

2774    //
2775
// Scrollable interface
2776
//
2777

2778    /**
2779     * Returns the preferred display size of a <code>JTree</code>. The height is
2780     * determined from <code>getVisibleRowCount</code> and the width
2781     * is the current preferred width.
2782     *
2783     * @return a <code>Dimension</code> object containing the preferred size
2784     */

2785    public Dimension getPreferredScrollableViewportSize() {
2786        int width = getPreferredSize().width;
2787        int visRows = getVisibleRowCount();
2788        int height = -1;
2789
2790        if(isFixedRowHeight())
2791            height = visRows * getRowHeight();
2792        else {
2793            TreeUI JavaDoc ui = getUI();
2794
2795            if (ui != null && visRows > 0) {
2796                int rc = ui.getRowCount(this);
2797
2798                if (rc >= visRows) {
2799                    Rectangle bounds = getRowBounds(visRows - 1);
2800                    if (bounds != null) {
2801                        height = bounds.y + bounds.height;
2802                    }
2803                }
2804                else if (rc > 0) {
2805                    Rectangle bounds = getRowBounds(0);
2806                    if (bounds != null) {
2807                        height = bounds.height * visRows;
2808                    }
2809                }
2810            }
2811            if (height == -1) {
2812                height = 16 * visRows;
2813            }
2814        }
2815        return new Dimension(width, height);
2816    }
2817
2818    /**
2819     * Returns the amount to increment when scrolling. The amount is
2820     * the height of the first displayed row that isn't completely in view
2821     * or, if it is totally displayed, the height of the next row in the
2822     * scrolling direction.
2823     *
2824     * @param visibleRect the view area visible within the viewport
2825     * @param orientation either <code>SwingConstants.VERTICAL</code>
2826     * or <code>SwingConstants.HORIZONTAL</code>
2827     * @param direction less than zero to scroll up/left,
2828     * greater than zero for down/right
2829     * @return the "unit" increment for scrolling in the specified direction
2830     * @see JScrollBar#setUnitIncrement(int)
2831     */

2832    public int getScrollableUnitIncrement(Rectangle visibleRect,
2833                                          int orientation, int direction) {
2834        if(orientation == SwingConstants.VERTICAL) {
2835            Rectangle rowBounds;
2836            int firstIndex = getClosestRowForLocation
2837                                         (0, visibleRect.y);
2838
2839            if(firstIndex != -1) {
2840                rowBounds = getRowBounds(firstIndex);
2841                if(rowBounds.y != visibleRect.y) {
2842                    if(direction < 0) {
2843                        // UP
2844
return Math.max(0, (visibleRect.y - rowBounds.y));
2845                    }
2846                    return (rowBounds.y + rowBounds.height - visibleRect.y);
2847                }
2848                if(direction < 0) { // UP
2849
if(firstIndex != 0) {
2850                        rowBounds = getRowBounds(firstIndex - 1);
2851                        return rowBounds.height;
2852                    }
2853                }
2854                else {
2855                    return rowBounds.height;
2856                }
2857            }
2858            return 0;
2859        }
2860        return 4;
2861    }
2862
2863
2864    /**
2865     * Returns the amount for a block increment, which is the height or
2866     * width of <code>visibleRect</code>, based on <code>orientation</code>.
2867     *
2868     * @param visibleRect the view area visible within the viewport
2869     * @param orientation either <code>SwingConstants.VERTICAL</code>
2870     * or <code>SwingConstants.HORIZONTAL</code>
2871     * @param direction less than zero to scroll up/left,
2872     * greater than zero for down/right.
2873     * @return the "block" increment for scrolling in the specified direction
2874     * @see JScrollBar#setBlockIncrement(int)
2875     */

2876    public int getScrollableBlockIncrement(Rectangle visibleRect,
2877                                           int orientation, int direction) {
2878        return (orientation == SwingConstants.VERTICAL) ? visibleRect.height :
2879            visibleRect.width;
2880    }
2881    
2882    /**
2883     * Returns false to indicate that the width of the viewport does not
2884     * determine the width of the table, unless the preferred width of
2885     * the tree is smaller than the viewports width. In other words:
2886     * ensure that the tree is never smaller than its viewport.
2887     *
2888     * @return false
2889     * @see Scrollable#getScrollableTracksViewportWidth
2890     */

2891    public boolean getScrollableTracksViewportWidth() {
2892    if (getParent() instanceof JViewport JavaDoc) {
2893        return (((JViewport JavaDoc)getParent()).getWidth() > getPreferredSize().width);
2894    }
2895    return false;
2896    }
2897
2898    /**
2899     * Returns false to indicate that the height of the viewport does not
2900     * determine the height of the table, unless the preferred height
2901     * of the tree is smaller than the viewports height. In other words:
2902     * ensure that the tree is never smaller than its viewport.
2903     *
2904     * @return false
2905     * @see Scrollable#getScrollableTracksViewportHeight
2906     */

2907    public boolean getScrollableTracksViewportHeight() {
2908    if (getParent() instanceof JViewport JavaDoc) {
2909        return (((JViewport JavaDoc)getParent()).getHeight() > getPreferredSize().height);
2910    }
2911    return false;
2912    }
2913
2914    /**
2915     * Sets the expanded state of this <code>JTree</code>.
2916     * If <code>state</code> is
2917     * true, all parents of <code>path</code> and path are marked as
2918     * expanded. If <code>state</code> is false, all parents of
2919     * <code>path</code> are marked EXPANDED, but <code>path</code> itself
2920     * is marked collapsed.<p>
2921     * This will fail if a <code>TreeWillExpandListener</code> vetos it.
2922     */

2923    protected void setExpandedState(TreePath path, boolean state) {
2924    if(path != null) {
2925        // Make sure all parents of path are expanded.
2926
Stack stack;
2927        TreePath parentPath = path.getParentPath();
2928
2929        if (expandedStack.size() == 0) {
2930        stack = new Stack();
2931        }
2932        else {
2933        stack = (Stack)expandedStack.pop();
2934        }
2935
2936        try {
2937        while(parentPath != null) {
2938            if(isExpanded(parentPath)) {
2939            parentPath = null;
2940            }
2941            else {
2942            stack.push(parentPath);
2943            parentPath = parentPath.getParentPath();
2944            }
2945        }
2946        for(int counter = stack.size() - 1; counter >= 0; counter--) {
2947            parentPath = (TreePath)stack.pop();
2948            if(!isExpanded(parentPath)) {
2949            try {
2950                fireTreeWillExpand(parentPath);
2951            } catch (ExpandVetoException eve) {
2952                // Expand vetoed!
2953
return;
2954            }
2955            expandedState.put(parentPath, Boolean.TRUE);
2956            fireTreeExpanded(parentPath);
2957            if (accessibleContext != null) {
2958                ((AccessibleJTree)accessibleContext).
2959                                  fireVisibleDataPropertyChange();
2960            }
2961            }
2962        }
2963        }
2964        finally {
2965        if (expandedStack.size() < TEMP_STACK_SIZE) {
2966            stack.removeAllElements();
2967            expandedStack.push(stack);
2968        }
2969        }
2970        if(!state) {
2971        // collapse last path.
2972
Object JavaDoc cValue = expandedState.get(path);
2973
2974        if(cValue != null && ((Boolean JavaDoc)cValue).booleanValue()) {
2975            try {
2976            fireTreeWillCollapse(path);
2977            }
2978            catch (ExpandVetoException eve) {
2979            return;
2980            }
2981            expandedState.put(path, Boolean.FALSE);
2982            fireTreeCollapsed(path);
2983            if (removeDescendantSelectedPaths(path, false) &&
2984            !isPathSelected(path)) {
2985            // A descendant was selected, select the parent.
2986
addSelectionPath(path);
2987            }
2988            if (accessibleContext != null) {
2989            ((AccessibleJTree)accessibleContext).
2990                        fireVisibleDataPropertyChange();
2991            }
2992        }
2993        }
2994        else {
2995        // Expand last path.
2996
Object JavaDoc cValue = expandedState.get(path);
2997
2998        if(cValue == null || !((Boolean JavaDoc)cValue).booleanValue()) {
2999            try {
3000            fireTreeWillExpand(path);
3001            }
3002            catch (ExpandVetoException eve) {
3003            return;
3004            }
3005            expandedState.put(path, Boolean.TRUE);
3006            fireTreeExpanded(path);
3007            if (accessibleContext != null) {
3008            ((AccessibleJTree)accessibleContext).
3009                              fireVisibleDataPropertyChange();
3010            }
3011        }
3012        }
3013    }
3014    }
3015
3016    /**
3017     * Returns an <code>Enumeration</code> of <code>TreePaths</code>
3018     * that have been expanded that
3019     * are descendants of <code>parent</code>.
3020     */

3021    protected Enumeration<TreePath>
3022    getDescendantToggledPaths(TreePath parent)
3023    {
3024    if(parent == null)
3025        return null;
3026
3027    Vector descendants = new Vector();
3028    Enumeration nodes = expandedState.keys();
3029    TreePath path;
3030
3031    while(nodes.hasMoreElements()) {
3032        path = (TreePath)nodes.nextElement();
3033        if(parent.isDescendant(path))
3034        descendants.addElement(path);
3035    }
3036    return descendants.elements();
3037    }
3038    
3039    /**
3040     * Removes any descendants of the <code>TreePaths</code> in
3041     * <code>toRemove</code>
3042     * that have been expanded.
3043     */

3044     protected void
3045     removeDescendantToggledPaths(Enumeration<TreePath> toRemove)
3046    {
3047     if(toRemove != null) {
3048         while(toRemove.hasMoreElements()) {
3049         Enumeration descendants = getDescendantToggledPaths
3050                                 ((TreePath)toRemove.nextElement());
3051
3052         if(descendants != null) {
3053             while(descendants.hasMoreElements()) {
3054             expandedState.remove(descendants.nextElement());
3055             }
3056         }
3057         }
3058     }
3059     }
3060
3061     /**
3062      * Clears the cache of toggled tree paths. This does NOT send out
3063      * any <code>TreeExpansionListener</code> events.
3064      */

3065     protected void clearToggledPaths() {
3066     expandedState.clear();
3067     }
3068
3069     /**
3070      * Creates and returns an instance of <code>TreeModelHandler</code>.
3071      * The returned
3072      * object is responsible for updating the expanded state when the
3073      * <code>TreeModel</code> changes.
3074      * <p>
3075      * For more information on what expanded state means, see the
3076      * <a HREF=#jtree_description>JTree description</a> above.
3077      */

3078     protected TreeModelListener createTreeModelListener() {
3079     return new TreeModelHandler();
3080     }
3081
3082    /**
3083     * Removes any paths in the selection that are descendants of
3084     * <code>path</code>. If <code>includePath</code> is true and
3085     * <code>path</code> is selected, it will be removed from the selection.
3086     *
3087     * @return true if a descendant was selected
3088     * @since 1.3
3089     */

3090    protected boolean removeDescendantSelectedPaths(TreePath path,
3091                            boolean includePath) {
3092    TreePath[] toRemove = getDescendantSelectedPaths(path, includePath);
3093
3094    if (toRemove != null) {
3095        getSelectionModel().removeSelectionPaths(toRemove);
3096        return true;
3097    }
3098    return false;
3099    }
3100
3101    /**
3102     * Returns an array of paths in the selection that are descendants of
3103     * <code>path</code>. The returned array may contain <code>null</code>s.
3104     */

3105    private TreePath[] getDescendantSelectedPaths(TreePath path,
3106                          boolean includePath) {
3107    TreeSelectionModel sm = getSelectionModel();
3108    TreePath[] selPaths = (sm != null) ? sm.getSelectionPaths() :
3109                                    null;
3110
3111    if(selPaths != null) {
3112        boolean shouldRemove = false;
3113
3114        for(int counter = selPaths.length - 1; counter >= 0; counter--) {
3115        if(selPaths[counter] != null &&
3116           path.isDescendant(selPaths[counter]) &&
3117           (!path.equals(selPaths[counter]) || includePath))
3118            shouldRemove = true;
3119        else
3120            selPaths[counter] = null;
3121        }
3122        if(!shouldRemove) {
3123        selPaths = null;
3124        }
3125        return selPaths;
3126    }
3127    return null;
3128    }
3129
3130    /**
3131     * Removes any paths from the selection model that are descendants of
3132     * the nodes identified by in <code>e</code>.
3133     */

3134    void removeDescendantSelectedPaths(TreeModelEvent e) {
3135    TreePath pPath = e.getTreePath();
3136    Object JavaDoc[] oldChildren = e.getChildren();
3137    TreeSelectionModel sm = getSelectionModel();
3138
3139    if (sm != null && pPath != null && oldChildren != null &&
3140        oldChildren.length > 0) {
3141        for (int counter = oldChildren.length - 1; counter >= 0;
3142         counter--) {
3143        // Might be better to call getDescendantSelectedPaths
3144
// numerous times, then push to the model.
3145
removeDescendantSelectedPaths(pPath.pathByAddingChild
3146                          (oldChildren[counter]), true);
3147        }
3148    }
3149    }
3150
3151
3152     /**
3153      * Listens to the model and updates the <code>expandedState</code>
3154      * accordingly when nodes are removed, or changed.
3155      */

3156    protected class TreeModelHandler implements TreeModelListener {
3157    public void treeNodesChanged(TreeModelEvent e) { }
3158
3159    public void treeNodesInserted(TreeModelEvent e) { }
3160
3161    public void treeStructureChanged(TreeModelEvent e) {
3162        if(e == null)
3163        return;
3164
3165        // NOTE: If I change this to NOT remove the descendants
3166
// and update BasicTreeUIs treeStructureChanged method
3167
// to update descendants in response to a treeStructureChanged
3168
// event, all the children of the event won't collapse!
3169
TreePath parent = e.getTreePath();
3170
3171        if(parent == null)
3172        return;
3173
3174        if (parent.getPathCount() == 1) {
3175        // New root, remove everything!
3176
clearToggledPaths();
3177        if(treeModel.getRoot() != null &&
3178                   !treeModel.isLeaf(treeModel.getRoot())) {
3179            // Mark the root as expanded, if it isn't a leaf.
3180
expandedState.put(parent, Boolean.TRUE);
3181        }
3182        }
3183        else if(expandedState.get(parent) != null) {
3184        Vector<TreePath> toRemove = new Vector<TreePath>(1);
3185        boolean isExpanded = isExpanded(parent);
3186
3187        toRemove.addElement(parent);
3188        removeDescendantToggledPaths(toRemove.elements());
3189        if(isExpanded) {
3190            TreeModel model = getModel();
3191
3192            if(model == null || model.isLeaf
3193               (parent.getLastPathComponent()))
3194            collapsePath(parent);
3195            else
3196            expandedState.put(parent, Boolean.TRUE);
3197        }
3198        }
3199        removeDescendantSelectedPaths(parent, false);
3200    }
3201
3202    public void treeNodesRemoved(TreeModelEvent e) {
3203        if(e == null)
3204        return;
3205
3206        TreePath parent = e.getTreePath();
3207        Object JavaDoc[] children = e.getChildren();
3208
3209        if(children == null)
3210        return;
3211
3212        TreePath rPath;
3213        Vector<TreePath> toRemove
3214        = new Vector<TreePath>(Math.max(1, children.length));
3215
3216        for(int counter = children.length - 1; counter >= 0; counter--) {
3217        rPath = parent.pathByAddingChild(children[counter]);
3218        if(expandedState.get(rPath) != null)
3219            toRemove.addElement(rPath);
3220        }
3221        if(toRemove.size() > 0)
3222        removeDescendantToggledPaths(toRemove.elements());
3223
3224        TreeModel model = getModel();
3225
3226        if(model == null || model.isLeaf(parent.getLastPathComponent()))
3227        expandedState.remove(parent);
3228
3229        removeDescendantSelectedPaths(e);
3230    }
3231    }
3232
3233
3234    /**
3235     * <code>DynamicUtilTreeNode</code> can wrap
3236     * vectors/hashtables/arrays/strings and
3237     * create the appropriate children tree nodes as necessary. It is
3238     * dynamic in that it will only create the children as necessary.
3239     * <p>
3240     * <strong>Warning:</strong>
3241     * Serialized objects of this class will not be compatible with
3242     * future Swing releases. The current serialization support is
3243     * appropriate for short term storage or RMI between applications running
3244     * the same version of Swing. As of 1.4, support for long term storage
3245     * of all JavaBeans<sup><font size="-2">TM</font></sup>
3246     * has been added to the <code>java.beans</code> package.
3247     * Please see {@link java.beans.XMLEncoder}.
3248     */

3249    public static class DynamicUtilTreeNode extends DefaultMutableTreeNode {
3250        /**
3251         * Does the this <code>JTree</code> have children?
3252         * This property is currently not implemented.
3253         */

3254        protected boolean hasChildren;
3255        /** Value to create children with. */
3256        protected Object JavaDoc childValue;
3257        /** Have the children been loaded yet? */
3258        protected boolean loadedChildren;
3259
3260        /**
3261         * Adds to parent all the children in <code>children</code>.
3262         * If <code>children</code> is an array or vector all of its
3263         * elements are added is children, otherwise if <code>children</code>
3264         * is a hashtable all the key/value pairs are added in the order
3265         * <code>Enumeration</code> returns them.
3266         */

3267        public static void createChildren(DefaultMutableTreeNode parent,
3268                                          Object JavaDoc children) {
3269            if(children instanceof Vector) {
3270                Vector childVector = (Vector)children;
3271
3272                for(int counter = 0, maxCounter = childVector.size();
3273                    counter < maxCounter; counter++)
3274                    parent.add(new DynamicUtilTreeNode
3275                               (childVector.elementAt(counter),
3276                                childVector.elementAt(counter)));
3277            }
3278            else if(children instanceof Hashtable) {
3279                Hashtable childHT = (Hashtable)children;
3280                Enumeration keys = childHT.keys();
3281                Object JavaDoc aKey;
3282
3283                while(keys.hasMoreElements()) {
3284                    aKey = keys.nextElement();
3285                    parent.add(new DynamicUtilTreeNode(aKey,
3286                                                       childHT.get(aKey)));
3287                }
3288            }
3289            else if(children instanceof Object JavaDoc[]) {
3290                Object JavaDoc[] childArray = (Object JavaDoc[])children;
3291
3292                for(int counter = 0, maxCounter = childArray.length;
3293                    counter < maxCounter; counter++)
3294                    parent.add(new DynamicUtilTreeNode(childArray[counter],
3295                                                       childArray[counter]));
3296            }
3297        }
3298
3299        /**
3300         * Creates a node with the specified object as its value and
3301         * with the specified children. For the node to allow children,
3302         * the children-object must be an array of objects, a
3303         * <code>Vector</code>, or a <code>Hashtable</code> -- even
3304         * if empty. Otherwise, the node is not
3305         * allowed to have children.
3306         *
3307         * @param value the <code>Object</code> that is the value for the
3308         * new node
3309         * @param children an array of <code>Object</code>s, a
3310         * <code>Vector</code>, or a <code>Hashtable</code>
3311         * used to create the child nodes; if any other
3312         * object is specified, or if the value is
3313         * <code>null</code>,
3314         * then the node is not allowed to have children
3315         */

3316        public DynamicUtilTreeNode(Object JavaDoc value, Object JavaDoc children) {
3317            super(value);
3318            loadedChildren = false;
3319            childValue = children;
3320            if(children != null) {
3321                if(children instanceof Vector)
3322                    setAllowsChildren(true);
3323                else if(children instanceof Hashtable)
3324                    setAllowsChildren(true);
3325                else if(children instanceof Object JavaDoc[])
3326                    setAllowsChildren(true);
3327                else
3328                    setAllowsChildren(false);
3329            }
3330            else
3331                setAllowsChildren(false);
3332        }
3333
3334        /**
3335         * Returns true if this node allows children. Whether the node
3336         * allows children depends on how it was created.
3337         *
3338         * @return true if this node allows children, false otherwise
3339         * @see #JTree.DynamicUtilTreeNode
3340         */

3341        public boolean isLeaf() {
3342            return !getAllowsChildren();
3343        }
3344
3345        /**
3346         * Returns the number of child nodes.
3347         *
3348         * @return the number of child nodes
3349         */

3350        public int getChildCount() {
3351            if(!loadedChildren)
3352                loadChildren();
3353            return super.getChildCount();
3354        }
3355
3356        /**
3357         * Loads the children based on <code>childValue</code>.
3358         * If <code>childValue</code> is a <code>Vector</code>
3359         * or array each element is added as a child,
3360         * if <code>childValue</code> is a <code>Hashtable</code>
3361         * each key/value pair is added in the order that
3362         * <code>Enumeration</code> returns the keys.
3363         */

3364        protected void loadChildren() {
3365            loadedChildren = true;
3366            createChildren(this, childValue);
3367        }
3368
3369    /**
3370     * Subclassed to load the children, if necessary.
3371     */

3372    public TreeNode getChildAt(int index) {
3373        if(!loadedChildren)
3374        loadChildren();
3375        return super.getChildAt(index);
3376    }
3377
3378    /**
3379     * Subclassed to load the children, if necessary.
3380     */

3381    public Enumeration children() {
3382        if(!loadedChildren)
3383        loadChildren();
3384        return super.children();
3385    }
3386    }
3387
3388    void setUIProperty(String JavaDoc propertyName, Object JavaDoc value) {
3389        if (propertyName == "rowHeight") {
3390        if (!rowHeightSet) {
3391        setRowHeight(((Number JavaDoc)value).intValue());
3392        rowHeightSet = false;
3393        }
3394    } else if (propertyName == "scrollsOnExpand") {
3395        if (!scrollsOnExpandSet) {
3396        setScrollsOnExpand(((Boolean JavaDoc)value).booleanValue());
3397        scrollsOnExpandSet = false;
3398        }
3399    } else if (propertyName == "showsRootHandles") {
3400            if (!showsRootHandlesSet) {
3401                setShowsRootHandles(((Boolean JavaDoc)value).booleanValue());
3402                showsRootHandlesSet = false;
3403            }
3404        } else {
3405        super.setUIProperty(propertyName, value);
3406    }
3407    }
3408
3409
3410    /**
3411     * Returns a string representation of this <code>JTree</code>.
3412     * This method
3413     * is intended to be used only for debugging purposes, and the
3414     * content and format of the returned string may vary between
3415     * implementations. The returned string may be empty but may not
3416     * be <code>null</code>.
3417     *
3418     * @return a string representation of this <code>JTree</code>.
3419     */

3420    protected String JavaDoc paramString() {
3421        String JavaDoc rootVisibleString = (rootVisible ?
3422                                    "true" : "false");
3423        String JavaDoc showsRootHandlesString = (showsRootHandles ?
3424                     "true" : "false");
3425        String JavaDoc editableString = (editable ?
3426                 "true" : "false");
3427        String JavaDoc largeModelString = (largeModel ?
3428                   "true" : "false");
3429        String JavaDoc invokesStopCellEditingString = (invokesStopCellEditing ?
3430                           "true" : "false");
3431        String JavaDoc scrollsOnExpandString = (scrollsOnExpand ?
3432                    "true" : "false");
3433
3434        return super.paramString() +
3435        ",editable=" + editableString +
3436        ",invokesStopCellEditing=" + invokesStopCellEditingString +
3437        ",largeModel=" + largeModelString +
3438        ",rootVisible=" + rootVisibleString +
3439        ",rowHeight=" + rowHeight +
3440        ",scrollsOnExpand=" + scrollsOnExpandString +
3441        ",showsRootHandles=" + showsRootHandlesString +
3442        ",toggleClickCount=" + toggleClickCount +
3443        ",visibleRowCount=" + visibleRowCount;
3444    }
3445
3446/////////////////
3447
// Accessibility support
3448
////////////////
3449

3450    /**
3451     * Gets the AccessibleContext associated with this JTree.
3452     * For JTrees, the AccessibleContext takes the form of an
3453     * AccessibleJTree.
3454     * A new AccessibleJTree instance is created if necessary.
3455     *
3456     * @return an AccessibleJTree that serves as the
3457     * AccessibleContext of this JTree
3458     */

3459    public AccessibleContext getAccessibleContext() {
3460        if (accessibleContext == null) {
3461            accessibleContext = new AccessibleJTree();
3462        }
3463        return accessibleContext;
3464    }
3465
3466    /**
3467     * This class implements accessibility support for the
3468     * <code>JTree</code> class. It provides an implementation of the
3469     * Java Accessibility API appropriate to tree user-interface elements.
3470     * <p>
3471     * <strong>Warning:</strong>
3472     * Serialized objects of this class will not be compatible with
3473     * future Swing releases. The current serialization support is
3474     * appropriate for short term storage or RMI between applications running
3475     * the same version of Swing. As of 1.4, support for long term storage
3476     * of all JavaBeans<sup><font size="-2">TM</font></sup>
3477     * has been added to the <code>java.beans</code> package.
3478     * Please see {@link java.beans.XMLEncoder}.
3479     */

3480    protected class AccessibleJTree extends AccessibleJComponent
3481            implements AccessibleSelection, TreeSelectionListener,
3482                   TreeModelListener, TreeExpansionListener {
3483
3484        TreePath leadSelectionPath;
3485    Accessible leadSelectionAccessible;
3486
3487        public AccessibleJTree() {
3488            // Add a tree model listener for JTree
3489
TreeModel model = JTree.this.getModel();
3490            if (model != null) {
3491                model.addTreeModelListener(this);
3492            }
3493        JTree.this.addTreeExpansionListener(this);
3494        JTree.this.addTreeSelectionListener(this);
3495            leadSelectionPath = JTree.this.getLeadSelectionPath();
3496        leadSelectionAccessible = (leadSelectionPath != null)
3497            ? new AccessibleJTreeNode(JTree.this,
3498                                      leadSelectionPath,
3499                                      JTree.this)
3500            : null;
3501        }
3502 
3503        /**
3504         * Tree Selection Listener value change method. Used to fire the
3505     * property change
3506         *
3507         * @param e ListSelectionEvent
3508         *
3509         */

3510        public void valueChanged(TreeSelectionEvent e) {
3511            // Fixes 4546503 - JTree is sending incorrect active
3512
// descendant events
3513
TreePath oldLeadSelectionPath = e.getOldLeadSelectionPath();
3514            leadSelectionPath = e.getNewLeadSelectionPath();
3515
3516        if (oldLeadSelectionPath != leadSelectionPath) {
3517        // Set parent to null so AccessibleJTreeNode computes
3518
// its parent.
3519
Accessible oldLSA = leadSelectionAccessible;
3520        leadSelectionAccessible = (leadSelectionPath != null)
3521            ? new AccessibleJTreeNode(JTree.this,
3522                          leadSelectionPath,
3523                                  null) // parent
3524
: null;
3525                firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
3526                                   oldLSA, leadSelectionAccessible);
3527        }
3528            firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
3529                               Boolean.valueOf(false), Boolean.valueOf(true));
3530    }
3531
3532        /**
3533         * Fire a visible data property change notification.
3534         * A 'visible' data property is one that represents
3535         * something about the way the component appears on the
3536         * display, where that appearance isn't bound to any other
3537         * property. It notifies screen readers that the visual
3538         * appearance of the component has changed, so they can
3539         * notify the user.
3540         */

3541        public void fireVisibleDataPropertyChange() {
3542           firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
3543                              Boolean.valueOf(false), Boolean.valueOf(true));
3544        }
3545 
3546        // Fire the visible data changes for the model changes.
3547

3548        /**
3549         * Tree Model Node change notification.
3550         *
3551         * @param e a Tree Model event
3552         */

3553        public void treeNodesChanged(TreeModelEvent e) {
3554           fireVisibleDataPropertyChange();
3555        }
3556 
3557        /**
3558         * Tree Model Node change notification.
3559         *
3560         * @param e a Tree node insertion event
3561         */

3562        public void treeNodesInserted(TreeModelEvent e) {
3563           fireVisibleDataPropertyChange();
3564        }
3565 
3566        /**
3567         * Tree Model Node change notification.
3568         *
3569         * @param e a Tree node(s) removal event
3570         */

3571        public void treeNodesRemoved(TreeModelEvent e) {
3572           fireVisibleDataPropertyChange();
3573        }
3574 
3575        /**
3576         * Tree Model structure change change notification.
3577         *
3578         * @param e a Tree Model event
3579         */

3580        public void treeStructureChanged(TreeModelEvent e) {
3581           fireVisibleDataPropertyChange();
3582        }
3583 
3584        /**
3585         * Tree Collapsed notification.
3586         *
3587         * @param e a TreeExpansionEvent
3588         */

3589        public void treeCollapsed(TreeExpansionEvent e) {
3590        fireVisibleDataPropertyChange();
3591        TreePath path = e.getPath();
3592        if (path != null) {
3593        // Set parent to null so AccessibleJTreeNode computes
3594
// its parent.
3595
AccessibleJTreeNode node = new AccessibleJTreeNode(JTree.this,
3596                                   path,
3597                                   null);
3598        PropertyChangeEvent pce = new PropertyChangeEvent(node,
3599            AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
3600                    AccessibleState.EXPANDED,
3601            AccessibleState.COLLAPSED);
3602        firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
3603                   null, pce);
3604        }
3605        }
3606 
3607        /**
3608         * Tree Model Expansion notification.
3609         *
3610         * @param e a Tree node insertion event
3611         */

3612        public void treeExpanded(TreeExpansionEvent e) {
3613            fireVisibleDataPropertyChange();
3614        TreePath path = e.getPath();
3615        if (path != null) {
3616                // TIGER - 4839971
3617
// Set parent to null so AccessibleJTreeNode computes
3618
// its parent.
3619
AccessibleJTreeNode node = new AccessibleJTreeNode(JTree.this,
3620                                   path,
3621                                   null);
3622        PropertyChangeEvent pce = new PropertyChangeEvent(node,
3623            AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
3624            AccessibleState.COLLAPSED,
3625                    AccessibleState.EXPANDED);
3626        firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
3627                   null, pce);
3628            }
3629        }
3630
3631 
3632        private AccessibleContext getCurrentAccessibleContext() {
3633            Component c = getCurrentComponent();
3634            if (c instanceof Accessible) {
3635                return (((Accessible) c).getAccessibleContext());
3636            } else {
3637                return null;
3638            }
3639        }
3640 
3641        private Component getCurrentComponent() {
3642            // is the object visible?
3643
// if so, get row, selected, focus & leaf state,
3644
// and then get the renderer component and return it
3645
TreeModel model = JTree.this.getModel();
3646            if (model == null) {
3647                return null;
3648            }
3649            TreePath path = new TreePath(model.getRoot());
3650            if (JTree.this.isVisible(path)) {
3651                TreeCellRenderer r = JTree.this.getCellRenderer();
3652                TreeUI JavaDoc ui = JTree.this.getUI();
3653                if (ui != null) {
3654                    int row = ui.getRowForPath(JTree.this, path);
3655            int lsr = JTree.this.getLeadSelectionRow();
3656                    boolean hasFocus = JTree.this.isFocusOwner()
3657                       && (lsr == row);
3658                    boolean selected = JTree.this.isPathSelected(path);
3659                    boolean expanded = JTree.this.isExpanded(path);
3660
3661                    return r.getTreeCellRendererComponent(JTree.this,
3662                        model.getRoot(), selected, expanded,
3663                        model.isLeaf(model.getRoot()), row, hasFocus);
3664                }
3665            }
3666            return null;
3667        }
3668
3669        // Overridden methods from AccessibleJComponent
3670

3671        /**
3672         * Get the role of this object.
3673         *
3674         * @return an instance of AccessibleRole describing the role of the
3675         * object
3676         * @see AccessibleRole
3677         */

3678        public AccessibleRole getAccessibleRole() {
3679            return AccessibleRole.TREE;
3680        }
3681
3682        /**
3683         * Returns the <code>Accessible</code> child, if one exists,
3684         * contained at the local coordinate <code>Point</code>.
3685         * Otherwise returns <code>null</code>.
3686         *
3687         * @param p point in local coordinates of this <code>Accessible</code>
3688         * @return the <code>Accessible</code>, if it exists,
3689         * at the specified location; else <code>null</code>
3690         */

3691        public Accessible getAccessibleAt(Point p) {
3692            TreePath path = getClosestPathForLocation(p.x, p.y);
3693            if (path != null) {
3694        // JTree.this is NOT the parent; parent will get computed later
3695
return new AccessibleJTreeNode(JTree.this, path, null);
3696            } else {
3697                return null;
3698            }
3699        }
3700
3701        /**
3702         * Returns the number of top-level children nodes of this
3703         * JTree. Each of these nodes may in turn have children nodes.
3704         *
3705         * @return the number of accessible children nodes in the tree.
3706         */

3707        public int getAccessibleChildrenCount() {
3708        TreeModel model = JTree.this.getModel();
3709        if (model != null) {
3710        return 1;
3711        } else {
3712        return 0;
3713        }
3714        }
3715
3716        /**
3717         * Return the nth Accessible child of the object.
3718         *
3719         * @param i zero-based index of child
3720         * @return the nth Accessible child of the object
3721         */

3722        public Accessible getAccessibleChild(int i) {
3723            TreeModel model = JTree.this.getModel();
3724            if (model != null) {
3725                if (i != 0) {
3726                    return null;
3727                } else {
3728                    Object JavaDoc[] objPath = {model.getRoot()};
3729                    TreePath path = new TreePath(objPath);
3730                    return new AccessibleJTreeNode(JTree.this, path,
3731                           JTree.this);
3732                }
3733            }
3734            return null;
3735        }
3736
3737        /**
3738         * Get the index of this object in its accessible parent.
3739         *
3740         * @return the index of this object in its parent. Since a JTree
3741         * top-level object does not have an accessible parent.
3742         * @see #getAccessibleParent
3743         */

3744        public int getAccessibleIndexInParent() {
3745        // didn't ever need to override this...
3746
return super.getAccessibleIndexInParent();
3747    }
3748
3749        // AccessibleSelection methods
3750
/**
3751         * Get the AccessibleSelection associated with this object. In the
3752         * implementation of the Java Accessibility API for this class,
3753     * return this object, which is responsible for implementing the
3754         * AccessibleSelection interface on behalf of itself.
3755     *
3756     * @return this object
3757     */

3758        public AccessibleSelection getAccessibleSelection() {
3759            return this;
3760        }
3761
3762        /**
3763         * Returns the number of items currently selected.
3764         * If no items are selected, the return value will be 0.
3765         *
3766         * @return the number of items currently selected.
3767         */

3768        public int getAccessibleSelectionCount() {
3769            Object JavaDoc[] rootPath = new Object JavaDoc[1];
3770            rootPath[0] = treeModel.getRoot();
3771            TreePath childPath = new TreePath(rootPath);
3772            if (JTree.this.isPathSelected(childPath)) {
3773                return 1;
3774            } else {
3775                return 0;
3776            }
3777        }
3778
3779        /**
3780         * Returns an Accessible representing the specified selected item
3781         * in the object. If there isn't a selection, or there are
3782         * fewer items selected than the integer passed in, the return
3783         * value will be null.
3784         *
3785         * @param i the zero-based index of selected items
3786         * @return an Accessible containing the selected item
3787         */

3788        public Accessible getAccessibleSelection(int i) {
3789        // The JTree can have only one accessible child, the root.
3790
if (i == 0) {
3791                Object JavaDoc[] rootPath = new Object JavaDoc[1];
3792                rootPath[0] = treeModel.getRoot();
3793                TreePath childPath = new TreePath(rootPath);
3794                if (JTree.this.isPathSelected(childPath)) {
3795                    return new AccessibleJTreeNode(JTree.this, childPath, JTree.this);
3796                }
3797            }
3798            return null;
3799        }
3800
3801        /**
3802         * Returns true if the current child of this object is selected.
3803         *
3804         * @param i the zero-based index of the child in this Accessible object.
3805         * @see AccessibleContext#getAccessibleChild
3806         */

3807        public boolean isAccessibleChildSelected(int i) {
3808        // The JTree can have only one accessible child, the root.
3809
if (i == 0) {
3810                Object JavaDoc[] rootPath = new Object JavaDoc[1];
3811                rootPath[0] = treeModel.getRoot();
3812                TreePath childPath = new TreePath(rootPath);
3813                return JTree.this.isPathSelected(childPath);
3814        } else {
3815        return false;
3816        }
3817        }
3818
3819        /**
3820         * Adds the specified selected item in the object to the object's
3821         * selection. If the object supports multiple selections,
3822         * the specified item is added to any existing selection, otherwise
3823         * it replaces any existing selection in the object. If the
3824         * specified item is already selected, this method has no effect.
3825         *
3826         * @param i the zero-based index of selectable items
3827         */

3828        public void addAccessibleSelection(int i) {
3829           TreeModel model = JTree.this.getModel();
3830           if (model != null) {
3831               if (i == 0) {
3832                   Object JavaDoc[] objPath = {model.getRoot()};
3833                   TreePath path = new TreePath(objPath);
3834                   JTree.this.addSelectionPath(path);
3835                }
3836            }
3837        }
3838
3839        /**
3840         * Removes the specified selected item in the object from the object's
3841         * selection. If the specified item isn't currently selected, this
3842         * method has no effect.
3843         *
3844         * @param i the zero-based index of selectable items
3845         */

3846        public void removeAccessibleSelection(int i) {
3847        TreeModel model = JTree.this.getModel();
3848        if (model != null) {
3849                if (i == 0) {
3850                    Object JavaDoc[] objPath = {model.getRoot()};
3851                    TreePath path = new TreePath(objPath);
3852                    JTree.this.removeSelectionPath(path);
3853                }
3854            }
3855        }
3856
3857        /**
3858         * Clears the selection in the object, so that nothing in the
3859         * object is selected.
3860         */

3861        public void clearAccessibleSelection() {
3862            int childCount = getAccessibleChildrenCount();
3863            for (int i = 0; i < childCount; i++) {
3864                removeAccessibleSelection(i);
3865            }
3866        }
3867
3868        /**
3869         * Causes every selected item in the object to be selected
3870         * if the object supports multiple selections.
3871         */

3872        public void selectAllAccessibleSelection() {
3873            TreeModel model = JTree.this.getModel();
3874            if (model != null) {
3875                Object JavaDoc[] objPath = {model.getRoot()};
3876                TreePath path = new TreePath(objPath);
3877                JTree.this.addSelectionPath(path);
3878            }
3879        }
3880
3881        /**
3882         * This class implements accessibility support for the
3883         * <code>JTree</code> child. It provides an implementation of the
3884         * Java Accessibility API appropriate to tree nodes.
3885         */

3886        protected class AccessibleJTreeNode extends AccessibleContext
3887            implements Accessible, AccessibleComponent, AccessibleSelection,
3888            AccessibleAction {
3889
3890            private JTree JavaDoc tree = null;
3891            private TreeModel treeModel = null;
3892            private Object JavaDoc obj = null;
3893            private TreePath path = null;
3894            private Accessible accessibleParent = null;
3895            private int index = 0;
3896            private boolean isLeaf = false;
3897
3898            /**
3899             * Constructs an AccessibleJTreeNode
3900             */

3901            public AccessibleJTreeNode(JTree JavaDoc t, TreePath p, Accessible ap) {
3902                tree = t;
3903                path = p;
3904                accessibleParent = ap;
3905                treeModel = t.getModel();
3906                obj = p.getLastPathComponent();
3907                if (treeModel != null) {
3908                    isLeaf = treeModel.isLeaf(obj);
3909                }
3910            }
3911
3912            private TreePath getChildTreePath(int i) {
3913                // Tree nodes can't be so complex that they have
3914
// two sets of children -> we're ignoring that case
3915
if (i < 0 || i >= getAccessibleChildrenCount()) {
3916                    return null;
3917                } else {
3918                    Object JavaDoc childObj = treeModel.getChild(obj, i);
3919                    Object JavaDoc[] objPath = path.getPath();
3920                    Object JavaDoc[] objChildPath = new Object JavaDoc[objPath.length+1];
3921                    java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
3922                    objChildPath[objChildPath.length-1] = childObj;
3923                    return new TreePath(objChildPath);
3924                }
3925            }
3926
3927            /**
3928             * Get the AccessibleContext associated with this tree node.
3929             * In the implementation of the Java Accessibility API for
3930         * this class, return this object, which is its own
3931         * AccessibleContext.
3932             *
3933             * @return this object
3934             */

3935            public AccessibleContext getAccessibleContext() {
3936                return this;
3937            }
3938
3939            private AccessibleContext getCurrentAccessibleContext() {
3940                Component c = getCurrentComponent();
3941                if (c instanceof Accessible) {
3942                    return (((Accessible) c).getAccessibleContext());
3943                } else {
3944                    return null;
3945                }
3946            }
3947
3948            private Component getCurrentComponent() {
3949                // is the object visible?
3950
// if so, get row, selected, focus & leaf state,
3951
// and then get the renderer component and return it
3952
if (tree.isVisible(path)) {
3953                    TreeCellRenderer r = tree.getCellRenderer();
3954            if (r == null) {
3955            return null;
3956            }
3957                    TreeUI JavaDoc ui = tree.getUI();
3958                    if (ui != null) {
3959                        int row = ui.getRowForPath(JTree.this, path);
3960                        boolean selected = tree.isPathSelected(path);
3961                        boolean expanded = tree.isExpanded(path);
3962                        boolean hasFocus = false; // how to tell?? -PK
3963
return r.getTreeCellRendererComponent(tree, obj,
3964                            selected, expanded, isLeaf, row, hasFocus);
3965                    }
3966                }
3967                return null;
3968            }
3969
3970        // AccessibleContext methods
3971

3972             /**
3973              * Get the accessible name of this object.
3974              *
3975              * @return the localized name of the object; null if this
3976              * object does not have a name
3977              */

3978             public String JavaDoc getAccessibleName() {
3979                AccessibleContext ac = getCurrentAccessibleContext();
3980                if (ac != null) {
3981                    String JavaDoc name = ac.getAccessibleName();
3982                    if ((name != null) && (name != "")) {
3983                        return ac.getAccessibleName();
3984                    } else {
3985                        return null;
3986                    }
3987                }
3988                if ((accessibleName != null) && (accessibleName != "")) {
3989                    return accessibleName;
3990                } else {
3991                    return null;
3992                }
3993            }
3994    
3995            /**
3996             * Set the localized accessible name of this object.
3997             *
3998             * @param s the new localized name of the object.
3999             */

4000            public void setAccessibleName(String JavaDoc s) {
4001                AccessibleContext ac = getCurrentAccessibleContext();
4002                if (ac != null) {
4003                    ac.setAccessibleName(s);
4004                } else {
4005                    super.setAccessibleName(s);
4006                }
4007            }
4008    
4009            //
4010
// *** should check tooltip text for desc. (needs MouseEvent)
4011
//
4012
/**
4013             * Get the accessible description of this object.
4014             *
4015             * @return the localized description of the object; null if
4016             * this object does not have a description
4017             */

4018            public String JavaDoc getAccessibleDescription() {
4019                AccessibleContext ac = getCurrentAccessibleContext();
4020                if (ac != null) {
4021                    return ac.getAccessibleDescription();
4022                } else {
4023                    return super.getAccessibleDescription();
4024                }
4025            }
4026    
4027            /**
4028             * Set the accessible description of this object.
4029             *
4030             * @param s the new localized description of the object
4031             */

4032            public void setAccessibleDescription(String JavaDoc s) {
4033                AccessibleContext ac = getCurrentAccessibleContext();
4034                if (ac != null) {
4035                    ac.setAccessibleDescription(s);
4036                } else {
4037                    super.setAccessibleDescription(s);
4038                }
4039            }
4040    
4041            /**
4042             * Get the role of this object.
4043             *
4044             * @return an instance of AccessibleRole describing the role of the object
4045             * @see AccessibleRole
4046             */

4047            public AccessibleRole getAccessibleRole() {
4048                AccessibleContext ac = getCurrentAccessibleContext();
4049                if (ac != null) {
4050                    return ac.getAccessibleRole();
4051                } else {
4052                    return AccessibleRole.UNKNOWN;
4053                }
4054            }
4055    
4056            /**
4057             * Get the state set of this object.
4058             *
4059             * @return an instance of AccessibleStateSet containing the
4060             * current state set of the object
4061             * @see AccessibleState
4062             */

4063            public AccessibleStateSet getAccessibleStateSet() {
4064                AccessibleContext ac = getCurrentAccessibleContext();
4065                AccessibleStateSet states;
4066        int row = tree.getUI().getRowForPath(tree,path);
4067        int lsr = tree.getLeadSelectionRow();
4068                if (ac != null) {
4069                    states = ac.getAccessibleStateSet();
4070                } else {
4071                    states = new AccessibleStateSet();
4072                }
4073                // need to test here, 'cause the underlying component
4074
// is a cellRenderer, which is never showing...
4075
if (isShowing()) {
4076                    states.add(AccessibleState.SHOWING);
4077                } else if (states.contains(AccessibleState.SHOWING)) {
4078                    states.remove(AccessibleState.SHOWING);
4079                }
4080                if (isVisible()) {
4081                    states.add(AccessibleState.VISIBLE);
4082                } else if (states.contains(AccessibleState.VISIBLE)) {
4083                    states.remove(AccessibleState.VISIBLE);
4084                }
4085                if (tree.isPathSelected(path)){
4086                    states.add(AccessibleState.SELECTED);
4087                }
4088        if (lsr == row) {
4089                    states.add(AccessibleState.ACTIVE);
4090                }
4091                if (!isLeaf) {
4092                    states.add(AccessibleState.EXPANDABLE);
4093                }
4094                if (tree.isExpanded(path)) {
4095                    states.add(AccessibleState.EXPANDED);
4096                } else {
4097                    states.add(AccessibleState.COLLAPSED);
4098                }
4099                if (tree.isEditable()) {
4100                    states.add(AccessibleState.EDITABLE);
4101                }
4102                return states;
4103            }
4104    
4105            /**
4106             * Get the Accessible parent of this object.
4107             *
4108             * @return the Accessible parent of this object; null if this
4109             * object does not have an Accessible parent
4110             */

4111            public Accessible getAccessibleParent() {
4112        // someone wants to know, so we need to create our parent
4113
// if we don't have one (hey, we're a talented kid!)
4114
if (accessibleParent == null) {
4115            Object JavaDoc[] objPath = path.getPath();
4116            if (objPath.length > 1) {
4117            Object JavaDoc objParent = objPath[objPath.length-2];
4118            if (treeModel != null) {
4119                index = treeModel.getIndexOfChild(objParent, obj);
4120            }
4121            Object JavaDoc[] objParentPath = new Object JavaDoc[objPath.length-1];
4122            java.lang.System.arraycopy(objPath, 0, objParentPath,
4123                           0, objPath.length-1);
4124            TreePath parentPath = new TreePath(objParentPath);
4125            accessibleParent = new AccessibleJTreeNode(tree,
4126                                   parentPath,
4127                                   null);
4128            this.setAccessibleParent(accessibleParent);
4129            } else if (treeModel != null) {
4130            accessibleParent = tree; // we're the top!
4131
index = 0; // we're an only child!
4132
this.setAccessibleParent(accessibleParent);
4133            }
4134        }
4135                return accessibleParent;
4136            }
4137    
4138            /**
4139             * Get the index of this object in its accessible parent.
4140             *
4141             * @return the index of this object in its parent; -1 if this
4142             * object does not have an accessible parent.
4143             * @see #getAccessibleParent
4144             */

4145            public int getAccessibleIndexInParent() {
4146        // index is invalid 'till we have an accessibleParent...
4147
if (accessibleParent == null) {
4148            getAccessibleParent();
4149        }
4150        Object JavaDoc[] objPath = path.getPath();
4151        if (objPath.length > 1) {
4152            Object JavaDoc objParent = objPath[objPath.length-2];
4153            if (treeModel != null) {
4154            index = treeModel.getIndexOfChild(objParent, obj);
4155            }
4156        }
4157                return index;
4158            }
4159    
4160            /**
4161             * Returns the number of accessible children in the object.
4162             *
4163             * @return the number of accessible children in the object.
4164             */

4165            public int getAccessibleChildrenCount() {
4166                // Tree nodes can't be so complex that they have
4167
// two sets of children -> we're ignoring that case
4168
return treeModel.getChildCount(obj);
4169            }
4170    
4171            /**
4172             * Return the specified Accessible child of the object.
4173             *
4174             * @param i zero-based index of child
4175             * @return the Accessible child of the object
4176             */

4177            public Accessible getAccessibleChild(int i) {
4178                // Tree nodes can't be so complex that they have
4179
// two sets of children -> we're ignoring that case
4180
if (i < 0 || i >= getAccessibleChildrenCount()) {
4181                    return null;
4182                } else {
4183                    Object JavaDoc childObj = treeModel.getChild(obj, i);
4184                    Object JavaDoc[] objPath = path.getPath();
4185                    Object JavaDoc[] objChildPath = new Object JavaDoc[objPath.length+1];
4186                    java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
4187                    objChildPath[objChildPath.length-1] = childObj;
4188                    TreePath childPath = new TreePath(objChildPath);
4189                    return new AccessibleJTreeNode(JTree.this, childPath, this);
4190                }
4191            }
4192    
4193            /**
4194             * Gets the locale of the component. If the component does not have
4195             * a locale, then the locale of its parent is returned.
4196             *
4197             * @return This component's locale. If this component does not have
4198         * a locale, the locale of its parent is returned.
4199             * @exception IllegalComponentStateException
4200             * If the Component does not have its own locale and has not yet
4201         * been added to a containment hierarchy such that the locale can be
4202             * determined from the containing parent.
4203             * @see #setLocale
4204             */

4205            public Locale getLocale() {
4206                AccessibleContext ac = getCurrentAccessibleContext();
4207                if (ac != null) {
4208                    return ac.getLocale();
4209                } else {
4210                    return tree.getLocale();
4211                }
4212            }
4213    
4214            /**
4215             * Add a PropertyChangeListener to the listener list.
4216             * The listener is registered for all properties.
4217             *
4218             * @param l The PropertyChangeListener to be added
4219             */

4220            public void addPropertyChangeListener(PropertyChangeListener l) {
4221                AccessibleContext ac = getCurrentAccessibleContext();
4222                if (ac != null) {
4223                    ac.addPropertyChangeListener(l);
4224                } else {
4225                    super.addPropertyChangeListener(l);
4226                }
4227            }
4228    
4229            /**
4230             * Remove a PropertyChangeListener from the listener list.
4231             * This removes a PropertyChangeListener that was registered
4232             * for all properties.
4233             *
4234             * @param l The PropertyChangeListener to be removed
4235             */

4236            public void removePropertyChangeListener(PropertyChangeListener l) {
4237                AccessibleContext ac = getCurrentAccessibleContext();
4238                if (ac != null) {
4239                    ac.removePropertyChangeListener(l);
4240                } else {
4241                    super.removePropertyChangeListener(l);
4242                }
4243            }
4244    
4245            /**
4246             * Get the AccessibleAction associated with this object. In the
4247             * implementation of the Java Accessibility API for this class,
4248             * return this object, which is responsible for implementing the
4249             * AccessibleAction interface on behalf of itself.
4250         *
4251         * @return this object
4252             */

4253            public AccessibleAction getAccessibleAction() {
4254                return this;
4255            }
4256
4257            /**
4258             * Get the AccessibleComponent associated with this object. In the
4259             * implementation of the Java Accessibility API for this class,
4260             * return this object, which is responsible for implementing the
4261             * AccessibleComponent interface on behalf of itself.
4262             *
4263             * @return this object
4264             */

4265            public AccessibleComponent getAccessibleComponent() {
4266                return this; // to override getBounds()
4267
}
4268
4269            /**
4270             * Get the AccessibleSelection associated with this object if one
4271             * exists. Otherwise return null.
4272             *
4273             * @return the AccessibleSelection, or null
4274             */

4275            public AccessibleSelection getAccessibleSelection() {
4276                AccessibleContext ac = getCurrentAccessibleContext();
4277                if (ac != null && isLeaf) {
4278                    return getCurrentAccessibleContext().getAccessibleSelection();
4279                } else {
4280                    return this;
4281                }
4282            }
4283
4284            /**
4285             * Get the AccessibleText associated with this object if one
4286             * exists. Otherwise return null.
4287             *
4288             * @return the AccessibleText, or null
4289             */

4290            public AccessibleText getAccessibleText() {
4291                AccessibleContext ac = getCurrentAccessibleContext();
4292                if (ac != null) {
4293                    return getCurrentAccessibleContext().getAccessibleText();
4294                } else {
4295                    return null;
4296                }
4297            }
4298
4299            /**
4300             * Get the AccessibleValue associated with this object if one
4301             * exists. Otherwise return null.
4302             *
4303             * @return the AccessibleValue, or null
4304             */

4305            public AccessibleValue getAccessibleValue() {
4306                AccessibleContext ac = getCurrentAccessibleContext();
4307                if (ac != null) {
4308                    return getCurrentAccessibleContext().getAccessibleValue();
4309                } else {
4310                    return null;
4311                }
4312            }
4313
4314
4315        // AccessibleComponent methods
4316

4317            /**
4318             * Get the background color of this object.
4319             *
4320             * @return the background color, if supported, of the object;
4321             * otherwise, null
4322             */

4323            public Color getBackground() {
4324                AccessibleContext ac = getCurrentAccessibleContext();
4325                if (ac instanceof AccessibleComponent) {
4326                    return ((AccessibleComponent) ac).getBackground();
4327                } else {
4328                    Component c = getCurrentComponent();
4329                    if (c != null) {
4330                        return c.getBackground();
4331                    } else {
4332                        return null;
4333                    }
4334                }
4335            }
4336    
4337            /**
4338             * Set the background color of this object.
4339             *
4340             * @param c the new Color for the background
4341             */

4342            public void setBackground(Color c) {
4343                AccessibleContext ac = getCurrentAccessibleContext();
4344                if (ac instanceof AccessibleComponent) {
4345                    ((AccessibleComponent) ac).setBackground(c);
4346                } else {
4347                    Component cp = getCurrentComponent();
4348                    if (cp != null) {
4349                        cp.setBackground(c);
4350                    }
4351                }
4352            }
4353    
4354        
4355            /**
4356             * Get the foreground color of this object.
4357             *
4358             * @return the foreground color, if supported, of the object;
4359             * otherwise, null
4360             */

4361            public Color getForeground() {
4362                AccessibleContext ac = getCurrentAccessibleContext();
4363                if (ac instanceof AccessibleComponent) {
4364                    return ((AccessibleComponent) ac).getForeground();
4365                } else {
4366                    Component c = getCurrentComponent();
4367                    if (c != null) {
4368                        return c.getForeground();
4369                    } else {
4370                        return null;
4371                    }
4372                }
4373            }
4374    
4375            public void setForeground(Color c) {
4376                AccessibleContext ac = getCurrentAccessibleContext();
4377                if (ac instanceof AccessibleComponent) {
4378                    ((AccessibleComponent) ac).setForeground(c);
4379                } else {
4380                    Component cp = getCurrentComponent();
4381                    if (cp != null) {
4382                        cp.setForeground(c);
4383                    }
4384                }
4385            }
4386    
4387            public Cursor getCursor() {
4388                AccessibleContext ac = getCurrentAccessibleContext();
4389                if (ac instanceof AccessibleComponent) {
4390                    return ((AccessibleComponent) ac).getCursor();
4391                } else {
4392                    Component c = getCurrentComponent();
4393                    if (c != null) {
4394                        return c.getCursor();
4395                    } else {
4396                        Accessible ap = getAccessibleParent();
4397                        if (ap instanceof AccessibleComponent) {
4398                            return ((AccessibleComponent) ap).getCursor();
4399                        } else {
4400                            return null;
4401                        }
4402                    }
4403                }
4404            }
4405    
4406            public void setCursor(Cursor c) {
4407                AccessibleContext ac = getCurrentAccessibleContext();
4408                if (ac instanceof AccessibleComponent) {
4409                    ((AccessibleComponent) ac).setCursor(c);
4410                } else {
4411                    Component cp = getCurrentComponent();
4412                    if (cp != null) {
4413                        cp.setCursor(c);
4414                    }
4415                }
4416            }
4417    
4418            public Font getFont() {
4419                AccessibleContext ac = getCurrentAccessibleContext();
4420                if (ac instanceof AccessibleComponent) {
4421                    return ((AccessibleComponent) ac).getFont();
4422                } else {
4423                    Component c = getCurrentComponent();
4424                    if (c != null) {
4425                        return c.getFont();
4426                    } else {
4427                        return null;
4428                    }
4429                }
4430            }
4431    
4432            public void setFont(Font f) {
4433                AccessibleContext ac = getCurrentAccessibleContext();
4434                if (ac instanceof AccessibleComponent) {
4435                    ((AccessibleComponent) ac).setFont(f);
4436                } else {
4437                    Component c = getCurrentComponent();
4438                    if (c != null) {
4439                        c.setFont(f);
4440                    }
4441                }
4442            }
4443    
4444            public FontMetrics getFontMetrics(Font f) {
4445                AccessibleContext ac = getCurrentAccessibleContext();
4446                if (ac instanceof AccessibleComponent) {
4447                    return ((AccessibleComponent) ac).getFontMetrics(f);
4448                } else {
4449                    Component c = getCurrentComponent();
4450                    if (c != null) {
4451                        return c.getFontMetrics(f);
4452                    } else {
4453                        return null;
4454                    }
4455                }
4456            }
4457    
4458            public boolean isEnabled() {
4459                AccessibleContext ac = getCurrentAccessibleContext();
4460                if (ac instanceof AccessibleComponent) {
4461                    return ((AccessibleComponent) ac).isEnabled();
4462                } else {
4463                    Component c = getCurrentComponent();
4464                    if (c != null) {
4465                        return c.isEnabled();
4466                    } else {
4467                        return false;
4468                    }
4469                }
4470            }
4471    
4472            public void setEnabled(boolean b) {
4473                AccessibleContext ac = getCurrentAccessibleContext();
4474                if (ac instanceof AccessibleComponent) {
4475                    ((AccessibleComponent) ac).setEnabled(b);
4476                } else {
4477                    Component c = getCurrentComponent();
4478                    if (c != null) {
4479                        c.setEnabled(b);
4480                    }
4481                }
4482            }
4483    
4484            public boolean isVisible() {
4485                Rectangle pathBounds = tree.getPathBounds(path);
4486                Rectangle parentBounds = tree.getVisibleRect();
4487                if (pathBounds != null && parentBounds != null &&
4488                    parentBounds.intersects(pathBounds)) {
4489                    return true;
4490                } else {
4491                    return false;
4492                }
4493            }
4494    
4495            public void setVisible(boolean b) {
4496            }
4497    
4498            public boolean isShowing() {
4499                return (tree.isShowing() && isVisible());
4500            }
4501    
4502            public boolean contains(Point p) {
4503                AccessibleContext ac = getCurrentAccessibleContext();
4504                if (ac instanceof AccessibleComponent) {
4505                    Rectangle r = ((AccessibleComponent) ac).getBounds();
4506                    return r.contains(p);
4507                } else {
4508                    Component c = getCurrentComponent();
4509                    if (c != null) {
4510                        Rectangle r = c.getBounds();
4511                        return r.contains(p);
4512                    } else {
4513                        return getBounds().contains(p);
4514                    }
4515                }
4516            }
4517    
4518            public Point getLocationOnScreen() {
4519                if (tree != null) {
4520                    Point treeLocation = tree.getLocationOnScreen();
4521                    Rectangle pathBounds = tree.getPathBounds(path);
4522                    if (treeLocation != null && pathBounds != null) {
4523                        Point nodeLocation = new Point(pathBounds.x,
4524                                                       pathBounds.y);
4525                        nodeLocation.translate(treeLocation.x, treeLocation.y);
4526                        return nodeLocation;
4527                    } else {
4528                        return null;
4529                    }
4530                } else {
4531                    return null;
4532                }
4533            }
4534
4535            protected Point getLocationInJTree() {
4536                Rectangle r = tree.getPathBounds(path);
4537                if (r != null) {
4538                    return r.getLocation();
4539                } else {
4540                    return null;
4541                }
4542            }
4543
4544            public Point getLocation() {
4545                Rectangle r = getBounds();
4546                if (r != null) {
4547                    return r.getLocation();
4548                } else {
4549                    return null;
4550                }
4551            }
4552    
4553            public void setLocation(Point p) {
4554            }
4555                
4556            public Rectangle getBounds() {
4557                Rectangle r = tree.getPathBounds(path);
4558                Accessible parent = getAccessibleParent();
4559                if (parent != null) {
4560                    if (parent instanceof AccessibleJTreeNode) {
4561                        Point parentLoc = ((AccessibleJTreeNode) parent).getLocationInJTree();
4562                        if (parentLoc != null && r != null) {
4563                            r.translate(-parentLoc.x, -parentLoc.y);
4564                        } else {
4565                            return null; // not visible!
4566
}
4567                    }
4568                }
4569                return r;
4570            }
4571    
4572            public void setBounds(Rectangle r) {
4573                AccessibleContext ac = getCurrentAccessibleContext();
4574                if (ac instanceof AccessibleComponent) {
4575                    ((AccessibleComponent) ac).setBounds(r);
4576                } else {
4577                    Component c = getCurrentComponent();
4578                    if (c != null) {
4579                        c.setBounds(r);
4580                    }
4581                }
4582            }
4583    
4584            public Dimension getSize() {
4585                return getBounds().getSize();
4586            }
4587    
4588            public void setSize (Dimension d) {
4589                AccessibleContext ac = getCurrentAccessibleContext();
4590                if (ac instanceof AccessibleComponent) {
4591                    ((AccessibleComponent) ac).setSize(d);
4592                } else {
4593                    Component c = getCurrentComponent();
4594                    if (c != null) {
4595                        c.setSize(d);
4596                    }
4597                }
4598            }
4599    
4600            /**
4601             * Returns the <code>Accessible</code> child, if one exists,
4602             * contained at the local coordinate <code>Point</code>.
4603             * Otherwise returns <code>null</code>.
4604             *
4605             * @param p point in local coordinates of this
4606             * <code>Accessible</code>
4607             * @return the <code>Accessible</code>, if it exists,
4608             * at the specified location; else <code>null</code>
4609             */

4610            public Accessible getAccessibleAt(Point p) {
4611                AccessibleContext ac = getCurrentAccessibleContext();
4612                if (ac instanceof AccessibleComponent) {
4613                    return ((AccessibleComponent) ac).getAccessibleAt(p);
4614                } else {
4615                    return null;
4616                }
4617            }
4618    
4619            public boolean isFocusTraversable() {
4620                AccessibleContext ac = getCurrentAccessibleContext();
4621                if (ac instanceof AccessibleComponent) {
4622                    return ((AccessibleComponent) ac).isFocusTraversable();
4623                } else {
4624                    Component c = getCurrentComponent();
4625                    if (c != null) {
4626                        return c.isFocusTraversable();
4627                    } else {
4628                        return false;
4629                    }
4630                }
4631            }
4632    
4633            public void requestFocus() {
4634                AccessibleContext ac = getCurrentAccessibleContext();
4635                if (ac instanceof AccessibleComponent) {
4636                    ((AccessibleComponent) ac).requestFocus();
4637                } else {
4638                    Component c = getCurrentComponent();
4639                    if (c != null) {
4640                        c.requestFocus();
4641                    }
4642                }
4643            }
4644    
4645            public void addFocusListener(FocusListener l) {
4646                AccessibleContext ac = getCurrentAccessibleContext();
4647                if (ac instanceof AccessibleComponent) {
4648                    ((AccessibleComponent) ac).addFocusListener(l);
4649                } else {
4650                    Component c = getCurrentComponent();
4651                    if (c != null) {
4652                        c.addFocusListener(l);
4653                    }
4654                }
4655            }
4656    
4657            public void removeFocusListener(FocusListener l) {
4658                AccessibleContext ac = getCurrentAccessibleContext();
4659                if (ac instanceof AccessibleComponent) {
4660                    ((AccessibleComponent) ac).removeFocusListener(l);
4661                } else {
4662                    Component c = getCurrentComponent();
4663                    if (c != null) {
4664                        c.removeFocusListener(l);
4665                    }
4666                }
4667            }
4668
4669        // AccessibleSelection methods
4670

4671            /**
4672             * Returns the number of items currently selected.
4673             * If no items are selected, the return value will be 0.
4674             *
4675             * @return the number of items currently selected.
4676             */

4677            public int getAccessibleSelectionCount() {
4678                int count = 0;
4679                int childCount = getAccessibleChildrenCount();
4680                for (int i = 0; i < childCount; i++) {
4681                    TreePath childPath = getChildTreePath(i);
4682                    if (tree.isPathSelected(childPath)) {
4683                       count++;
4684                    }
4685                }
4686                return count;
4687            }
4688
4689            /**
4690             * Returns an Accessible representing the specified selected item
4691             * in the object. If there isn't a selection, or there are
4692             * fewer items selected than the integer passed in, the return
4693             * value will be null.
4694             *
4695             * @param i the zero-based index of selected items
4696             * @return an Accessible containing the selected item
4697             */

4698            public Accessible getAccessibleSelection(int i) {
4699                int childCount = getAccessibleChildrenCount();
4700                if (i < 0 || i >= childCount) {
4701                    return null; // out of range
4702
}
4703                int count = 0;
4704                for (int j = 0; j < childCount && i >= count; j++) {
4705                    TreePath childPath = getChildTreePath(j);
4706                    if (tree.isPathSelected(childPath)) {
4707                        if (count == i) {
4708                            return new AccessibleJTreeNode(tree, childPath, this);
4709                        } else {
4710                            count++;
4711                        }
4712                    }
4713                }
4714                return null;
4715            }
4716
4717            /**
4718             * Returns true if the current child of this object is selected.
4719             *
4720             * @param i the zero-based index of the child in this Accessible
4721             * object.
4722             * @see AccessibleContext#getAccessibleChild
4723             */

4724            public boolean isAccessibleChildSelected(int i) {
4725                int childCount = getAccessibleChildrenCount();
4726                if (i < 0 || i >= childCount) {
4727                    return false; // out of range
4728
} else {
4729                    TreePath childPath = getChildTreePath(i);
4730                    return tree.isPathSelected(childPath);
4731                }
4732            }
4733
4734            /**
4735             * Adds the specified selected item in the object to the object's
4736             * selection. If the object supports multiple selections,
4737             * the specified item is added to any existing selection, otherwise
4738             * it replaces any existing selection in the object. If the
4739             * specified item is already selected, this method has no effect.
4740             *
4741             * @param i the zero-based index of selectable items
4742             */

4743            public void addAccessibleSelection(int i) {
4744               TreeModel model = JTree.this.getModel();
4745               if (model != null) {
4746                   if (i >= 0 && i < getAccessibleChildrenCount()) {
4747                       TreePath path = getChildTreePath(i);
4748                       JTree.this.addSelectionPath(path);
4749                    }
4750                }
4751            }
4752
4753            /**
4754             * Removes the specified selected item in the object from the
4755             * object's
4756             * selection. If the specified item isn't currently selected, this
4757             * method has no effect.
4758             *
4759             * @param i the zero-based index of selectable items
4760             */

4761            public void removeAccessibleSelection(int i) {
4762               TreeModel model = JTree.this.getModel();
4763               if (model != null) {
4764                   if (i >= 0 && i < getAccessibleChildrenCount()) {
4765                       TreePath path = getChildTreePath(i);
4766                       JTree.this.removeSelectionPath(path);
4767                    }
4768                }
4769            }
4770
4771            /**
4772             * Clears the selection in the object, so that nothing in the
4773             * object is selected.
4774             */

4775            public void clearAccessibleSelection() {
4776                int childCount = getAccessibleChildrenCount();
4777                for (int i = 0; i < childCount; i++) {
4778                    removeAccessibleSelection(i);
4779                }
4780            }
4781
4782            /**
4783             * Causes every selected item in the object to be selected
4784             * if the object supports multiple selections.
4785             */

4786            public void selectAllAccessibleSelection() {
4787               TreeModel model = JTree.this.getModel();
4788               if (model != null) {
4789                   int childCount = getAccessibleChildrenCount();
4790                   TreePath path;
4791                   for (int i = 0; i < childCount; i++) {
4792                       path = getChildTreePath(i);
4793                       JTree.this.addSelectionPath(path);
4794                   }
4795                }
4796            }
4797
4798        // AccessibleAction methods
4799

4800            /**
4801             * Returns the number of accessible actions available in this
4802             * tree node. If this node is not a leaf, there is at least
4803             * one action (toggle expand), in addition to any available
4804             * on the object behind the TreeCellRenderer.
4805             *
4806             * @return the number of Actions in this object
4807             */

4808            public int getAccessibleActionCount() {
4809                AccessibleContext ac = getCurrentAccessibleContext();
4810                if (ac != null) {
4811                    AccessibleAction aa = ac.getAccessibleAction();
4812                    if (aa != null) {
4813                        return (aa.getAccessibleActionCount() + (isLeaf ? 0 : 1));
4814                    }
4815                }
4816                return isLeaf ? 0 : 1;
4817            }
4818
4819            /**
4820             * Return a description of the specified action of the tree node.
4821             * If this node is not a leaf, there is at least one action
4822             * description (toggle expand), in addition to any available
4823             * on the object behind the TreeCellRenderer.
4824             *
4825             * @param i zero-based index of the actions
4826             * @return a description of the action
4827             */

4828            public String JavaDoc getAccessibleActionDescription(int i) {
4829                if (i < 0 || i >= getAccessibleActionCount()) {
4830                    return null;
4831                }
4832                AccessibleContext ac = getCurrentAccessibleContext();
4833                if (i == 0) {
4834                    // TIGER - 4766636
4835
return AccessibleAction.TOGGLE_EXPAND;
4836                } else if (ac != null) {
4837                    AccessibleAction aa = ac.getAccessibleAction();
4838                    if (aa != null) {
4839                        return aa.getAccessibleActionDescription(i - 1);
4840                    }
4841                }
4842                return null;
4843            }
4844
4845            /**
4846             * Perform the specified Action on the tree node. If this node
4847             * is not a leaf, there is at least one action which can be
4848             * done (toggle expand), in addition to any available on the
4849             * object behind the TreeCellRenderer.
4850             *
4851             * @param i zero-based index of actions
4852             * @return true if the the action was performed; else false.
4853             */

4854            public boolean doAccessibleAction(int i) {
4855                if (i < 0 || i >= getAccessibleActionCount()) {
4856                    return false;
4857                }
4858                AccessibleContext ac = getCurrentAccessibleContext();
4859                if (i == 0) {
4860                    if (JTree.this.isExpanded(path)) {
4861                        JTree.this.collapsePath(path);
4862                    } else {
4863                        JTree.this.expandPath(path);
4864                    }
4865                    return true;
4866                } else if (ac != null) {
4867                    AccessibleAction aa = ac.getAccessibleAction();
4868                    if (aa != null) {
4869                        return aa.doAccessibleAction(i - 1);
4870                    }
4871                }
4872                return false;
4873            }
4874
4875        } // inner class AccessibleJTreeNode
4876

4877    } // inner class AccessibleJTree
4878

4879} // End of class JTree
4880

4881
Popular Tags