1/*
2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package javax.swing;
26
27import java.awt.*;
28import java.awt.event.*;
29import java.beans.JavaBean;
30import java.beans.BeanProperty;
31import java.beans.ConstructorProperties;
32import java.beans.PropertyChangeEvent;
33import java.beans.PropertyChangeListener;
34import java.io.*;
35import java.util.*;
36import javax.swing.event.*;
37import javax.swing.plaf.*;
38import javax.swing.tree.*;
39import javax.swing.text.Position;
40import javax.accessibility.*;
41
42import sun.swing.SwingUtilities2;
43import sun.swing.SwingUtilities2.Section;
44import static sun.swing.SwingUtilities2.Section.*;
45
46/**
47 * <a id="jtree_description"></a>
48 * A control that displays a set of hierarchical data as an outline.
49 * You can find task-oriented documentation and examples of using trees in
50 * <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/tree.html">How to Use Trees</a>,
51 * a section in <em>The Java Tutorial.</em>
52 * <p>
53 * A specific node in a tree can be identified either by a
54 * <code>TreePath</code> (an object
55 * that encapsulates a node and all of its ancestors), or by its
56 * display row, where each row in the display area displays one node.
57 * An <i>expanded</i> node is a non-leaf node (as identified by
58 * <code>TreeModel.isLeaf(node)</code> returning false) that will displays
59 * its children when all its ancestors are <i>expanded</i>.
60 * A <i>collapsed</i>
61 * node is one which hides them. A <i>hidden</i> node is one which is
62 * under a collapsed ancestor. All of a <i>viewable</i> nodes parents
63 * are expanded, but may or may not be displayed. A <i>displayed</i> node
64 * is both viewable and in the display area, where it can be seen.
65 * </p>
66 * The following <code>JTree</code> methods use "visible" to mean "displayed":
67 * <ul>
68 * <li><code>isRootVisible()</code>
69 * <li><code>setRootVisible()</code>
70 * <li><code>scrollPathToVisible()</code>
71 * <li><code>scrollRowToVisible()</code>
72 * <li><code>getVisibleRowCount()</code>
73 * <li><code>setVisibleRowCount()</code>
74 * </ul>
75 * The next group of <code>JTree</code> methods use "visible" to mean
76 * "viewable" (under an expanded parent):
77 * <ul>
78 * <li><code>isVisible()</code>
79 * <li><code>makeVisible()</code>
80 * </ul>
81 * If you are interested in knowing when the selection changes implement
82 * the <code>TreeSelectionListener</code> interface and add the instance
83 * using the method <code>addTreeSelectionListener</code>.
84 * <code>valueChanged</code> will be invoked when the
85 * selection changes, that is if the user clicks twice on the same
86 * node <code>valueChanged</code> will only be invoked once.
87 * <p>
88 * If you are interested in detecting either double-click events or when
89 * a user clicks on a node, regardless of whether or not it was selected,
90 * we recommend you do the following:
91 * </p>
92 * <pre>
93 * final JTree tree = ...;
94 *
95 * MouseListener ml = new MouseAdapter() {
96 *     public void <b>mousePressed</b>(MouseEvent e) {
97 *         int selRow = tree.getRowForLocation(e.getX(), e.getY());
98 *         TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
99 *         if(selRow != -1) {
100 *             if(e.getClickCount() == 1) {
101 *                 mySingleClick(selRow, selPath);
102 *             }
103 *             else if(e.getClickCount() == 2) {
104 *                 myDoubleClick(selRow, selPath);
105 *             }
106 *         }
107 *     }
108 * };
109 * tree.addMouseListener(ml);
110 * </pre>
111 * NOTE: This example obtains both the path and row, but you only need to
112 * get the one you're interested in.
113 * <p>
114 * To use <code>JTree</code> to display compound nodes
115 * (for example, nodes containing both
116 * a graphic icon and text), subclass {@link TreeCellRenderer} and use
117 * {@link #setCellRenderer} to tell the tree to use it. To edit such nodes,
118 * subclass {@link TreeCellEditor} and use {@link #setCellEditor}.
119 * </p>
120 * <p>
121 * Like all <code>JComponent</code> classes, you can use {@link InputMap} and
122 * {@link ActionMap}
123 * to associate an {@link Action} object with a {@link KeyStroke}
124 * and execute the action under specified conditions.
125 * </p>
126 * <strong>Warning:</strong> Swing is not thread safe. For more
127 * information see <a
128 * href="package-summary.html#threading">Swing's Threading
129 * Policy</a>.
130 * <p>
131 * <strong>Warning:</strong>
132 * Serialized objects of this class will not be compatible with
133 * future Swing releases. The current serialization support is
134 * appropriate for short term storage or RMI between applications running
135 * the same version of Swing.  As of 1.4, support for long term storage
136 * of all JavaBeans&trade;
137 * has been added to the <code>java.beans</code> package.
138 * Please see {@link java.beans.XMLEncoder}.
139 *</p>
140 *
141 * @author Rob Davis
142 * @author Ray Ryan
143 * @author Scott Violet
144 * @since 1.2
145 */
146@JavaBean(defaultProperty = "UI", description = "A component that displays a set of hierarchical data as an outline.")
147@SwingContainer(false)
148@SuppressWarnings("serial")
149public class JTree extends JComponent implements Scrollable, Accessible
150{
151    /**
152     * @see #getUIClassID
153     * @see #readObject
154     */
155    private static final String uiClassID = "TreeUI";
156
157    /**
158     * The model that defines the tree displayed by this object.
159     */
160    protected transient TreeModel        treeModel;
161
162    /**
163     * Models the set of selected nodes in this tree.
164     */
165    protected transient TreeSelectionModel selectionModel;
166
167    /**
168     * True if the root node is displayed, false if its children are
169     * the highest visible nodes.
170     */
171    protected boolean                    rootVisible;
172
173    /**
174     * The cell used to draw nodes. If <code>null</code>, the UI uses a default
175     * <code>cellRenderer</code>.
176     */
177    protected transient TreeCellRenderer  cellRenderer;
178
179    /**
180     * Height to use for each display row. If this is &lt;= 0 the renderer
181     * determines the height for each row.
182     */
183    protected int                         rowHeight;
184    private boolean                       rowHeightSet = false;
185
186    /**
187     * Maps from <code>TreePath</code> to <code>Boolean</code>
188     * indicating whether or not the
189     * particular path is expanded. This ONLY indicates whether a
190     * given path is expanded, and NOT if it is visible or not. That
191     * information must be determined by visiting all the parent
192     * paths and seeing if they are visible.
193     */
194    private transient Hashtable<TreePath, Boolean> expandedState;
195
196
197    /**
198     * True if handles are displayed at the topmost level of the tree.
199     * <p>
200     * A handle is a small icon that displays adjacent to the node which
201     * allows the user to click once to expand or collapse the node. A
202     * common interface shows a plus sign (+) for a node which can be
203     * expanded and a minus sign (-) for a node which can be collapsed.
204     * Handles are always shown for nodes below the topmost level.
205     * <p>
206     * If the <code>rootVisible</code> setting specifies that the root
207     * node is to be displayed, then that is the only node at the topmost
208     * level. If the root node is not displayed, then all of its
209     * children are at the topmost level of the tree. Handles are
210     * always displayed for nodes other than the topmost.
211     * <p>
212     * If the root node isn't visible, it is generally a good to make
213     * this value true. Otherwise, the tree looks exactly like a list,
214     * and users may not know that the "list entries" are actually
215     * tree nodes.
216     *
217     * @see #rootVisible
218     */
219    protected boolean           showsRootHandles;
220    private boolean             showsRootHandlesSet = false;
221
222    /**
223     * Creates a new event and passed it off the
224     * <code>selectionListeners</code>.
225     */
226    protected transient TreeSelectionRedirector selectionRedirector;
227
228    /**
229     * Editor for the entries.  Default is <code>null</code>
230     * (tree is not editable).
231     */
232    protected transient TreeCellEditor          cellEditor;
233
234    /**
235     * Is the tree editable? Default is false.
236     */
237    protected boolean                 editable;
238
239    /**
240     * Is this tree a large model? This is a code-optimization setting.
241     * A large model can be used when the cell height is the same for all
242     * nodes. The UI will then cache very little information and instead
243     * continually message the model. Without a large model the UI caches
244     * most of the information, resulting in fewer method calls to the model.
245     * <p>
246     * This value is only a suggestion to the UI. Not all UIs will
247     * take advantage of it. Default value is false.
248     */
249    protected boolean                 largeModel;
250
251    /**
252     * Number of rows to make visible at one time. This value is used for
253     * the <code>Scrollable</code> interface. It determines the preferred
254     * size of the display area.
255     */
256    protected int                     visibleRowCount;
257
258    /**
259     * If true, when editing is to be stopped by way of selection changing,
260     * data in tree changing or other means <code>stopCellEditing</code>
261     * is invoked, and changes are saved. If false,
262     * <code>cancelCellEditing</code> is invoked, and changes
263     * are discarded. Default is false.
264     */
265    protected boolean                 invokesStopCellEditing;
266
267    /**
268     * If true, when a node is expanded, as many of the descendants are
269     * scrolled to be visible.
270     */
271    protected boolean                 scrollsOnExpand;
272    private boolean                   scrollsOnExpandSet = false;
273
274    /**
275     * Number of mouse clicks before a node is expanded.
276     */
277    protected int                     toggleClickCount;
278
279    /**
280     * Updates the <code>expandedState</code>.
281     */
282    protected transient TreeModelListener       treeModelListener;
283
284    /**
285     * Used when <code>setExpandedState</code> is invoked,
286     * will be a <code>Stack</code> of <code>Stack</code>s.
287     */
288    private transient Stack<Stack<TreePath>> expandedStack;
289
290    /**
291     * Lead selection path, may not be <code>null</code>.
292     */
293    private TreePath                  leadPath;
294
295    /**
296     * Anchor path.
297     */
298    private TreePath                  anchorPath;
299
300    /**
301     * True if paths in the selection should be expanded.
302     */
303    private boolean                   expandsSelectedPaths;
304
305    /**
306     * This is set to true for the life of the <code>setUI</code> call.
307     */
308    private boolean                   settingUI;
309
310    /** If true, mouse presses on selections initiate a drag operation. */
311    private boolean dragEnabled;
312
313    /**
314     * The drop mode for this component.
315     */
316    private DropMode dropMode = DropMode.USE_SELECTION;
317
318    /**
319     * The drop location.
320     */
321    private transient DropLocation dropLocation;
322
323    /**
324     * Flag to indicate UI update is in progress
325     */
326    private transient boolean updateInProgress;
327
328    /**
329     * A subclass of <code>TransferHandler.DropLocation</code> representing
330     * a drop location for a <code>JTree</code>.
331     *
332     * @see #getDropLocation
333     * @since 1.6
334     */
335    public static final class DropLocation extends TransferHandler.DropLocation {
336        private final TreePath path;
337        private final int index;
338
339        private DropLocation(Point p, TreePath path, int index) {
340            super(p);
341            this.path = path;
342            this.index = index;
343        }
344
345        /**
346         * Returns the index where the dropped data should be inserted
347         * with respect to the path returned by <code>getPath()</code>.
348         * <p>
349         * For drop modes <code>DropMode.USE_SELECTION</code> and
350         * <code>DropMode.ON</code>, this index is unimportant (and it will
351         * always be <code>-1</code>) as the only interesting data is the
352         * path over which the drop operation occurred.
353         * <p>
354         * For drop mode <code>DropMode.INSERT</code>, this index
355         * indicates the index at which the data should be inserted into
356         * the parent path represented by <code>getPath()</code>.
357         * <code>-1</code> indicates that the drop occurred over the
358         * parent itself, and in most cases should be treated as inserting
359         * into either the beginning or the end of the parent's list of
360         * children.
361         * <p>
362         * For <code>DropMode.ON_OR_INSERT</code>, this value will be
363         * an insert index, as described above, or <code>-1</code> if
364         * the drop occurred over the path itself.
365         *
366         * @return the child index
367         * @see #getPath
368         */
369        public int getChildIndex() {
370            return index;
371        }
372
373        /**
374         * Returns the path where dropped data should be placed in the
375         * tree.
376         * <p>
377         * Interpretation of this value depends on the drop mode set on the
378         * component. If the drop mode is <code>DropMode.USE_SELECTION</code>
379         * or <code>DropMode.ON</code>, the return value is the path in the
380         * tree over which the data has been (or will be) dropped.
381         * <code>null</code> indicates that the drop is over empty space,
382         * not associated with a particular path.
383         * <p>
384         * If the drop mode is <code>DropMode.INSERT</code>, the return value
385         * refers to the path that should become the parent of the new data,
386         * in which case <code>getChildIndex()</code> indicates where the
387         * new item should be inserted into this parent path. A
388         * <code>null</code> path indicates that no parent path has been
389         * determined, which can happen for multiple reasons:
390         * <ul>
391         *    <li>The tree has no model
392         *    <li>There is no root in the tree
393         *    <li>The root is collapsed
394         *    <li>The root is a leaf node
395         * </ul>
396         * It is up to the developer to decide if and how they wish to handle
397         * the <code>null</code> case.
398         * <p>
399         * If the drop mode is <code>DropMode.ON_OR_INSERT</code>,
400         * <code>getChildIndex</code> can be used to determine whether the
401         * drop is on top of the path itself (<code>-1</code>) or the index
402         * at which it should be inserted into the path (values other than
403         * <code>-1</code>).
404         *
405         * @return the drop path
406         * @see #getChildIndex
407         */
408        public TreePath getPath() {
409            return path;
410        }
411
412        /**
413         * Returns a string representation of this drop location.
414         * This method is intended to be used for debugging purposes,
415         * and the content and format of the returned string may vary
416         * between implementations.
417         *
418         * @return a string representation of this drop location
419         */
420        public String toString() {
421            return getClass().getName()
422                   + "[dropPoint=" + getDropPoint() + ","
423                   + "path=" + path + ","
424                   + "childIndex=" + index + "]";
425        }
426    }
427
428    /**
429     * The row to expand during DnD.
430     */
431    private int expandRow = -1;
432
433    @SuppressWarnings("serial")
434    private class TreeTimer extends Timer {
435        public TreeTimer() {
436            super(2000, null);
437            setRepeats(false);
438        }
439
440        public void fireActionPerformed(ActionEvent ae) {
441            JTree.this.expandRow(expandRow);
442        }
443    }
444
445    /**
446     * A timer to expand nodes during drop.
447     */
448    private TreeTimer dropTimer;
449
450    /**
451     * When <code>addTreeExpansionListener</code> is invoked,
452     * and <code>settingUI</code> is true, this ivar gets set to the passed in
453     * <code>Listener</code>. This listener is then notified first in
454     * <code>fireTreeCollapsed</code> and <code>fireTreeExpanded</code>.
455     * <p>This is an ugly workaround for a way to have the UI listener
456     * get notified before other listeners.
457     */
458    private transient TreeExpansionListener     uiTreeExpansionListener;
459
460    /**
461     * Max number of stacks to keep around.
462     */
463    private static int                TEMP_STACK_SIZE = 11;
464
465    //
466    // Bound property names
467    //
468    /** Bound property name for <code>cellRenderer</code>. */
469    public static final String        CELL_RENDERER_PROPERTY = "cellRenderer";
470    /** Bound property name for <code>treeModel</code>. */
471    public static final String        TREE_MODEL_PROPERTY = "model";
472    /** Bound property name for <code>rootVisible</code>. */
473    public static final String        ROOT_VISIBLE_PROPERTY = "rootVisible";
474    /** Bound property name for <code>showsRootHandles</code>. */
475    public static final String        SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
476    /** Bound property name for <code>rowHeight</code>. */
477    public static final String        ROW_HEIGHT_PROPERTY = "rowHeight";
478    /** Bound property name for <code>cellEditor</code>. */
479    public static final String        CELL_EDITOR_PROPERTY = "cellEditor";
480    /** Bound property name for <code>editable</code>. */
481    public static final String        EDITABLE_PROPERTY = "editable";
482    /** Bound property name for <code>largeModel</code>. */
483    public static final String        LARGE_MODEL_PROPERTY = "largeModel";
484    /** Bound property name for selectionModel. */
485    public static final String        SELECTION_MODEL_PROPERTY = "selectionModel";
486    /** Bound property name for <code>visibleRowCount</code>. */
487    public static final String        VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
488    /** Bound property name for <code>messagesStopCellEditing</code>. */
489    public static final String        INVOKES_STOP_CELL_EDITING_PROPERTY = "invokesStopCellEditing";
490    /** Bound property name for <code>scrollsOnExpand</code>. */
491    public static final String        SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
492    /** Bound property name for <code>toggleClickCount</code>. */
493    public static final String        TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
494    /** Bound property name for <code>leadSelectionPath</code>.
495     * @since 1.3 */
496    public static final String        LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
497    /** Bound property name for anchor selection path.
498     * @since 1.3 */
499    public static final String        ANCHOR_SELECTION_PATH_PROPERTY = "anchorSelectionPath";
500    /** Bound property name for expands selected paths property
501     * @since 1.3 */
502    public static final String        EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths";
503
504
505    /**
506     * Creates and returns a sample <code>TreeModel</code>.
507     * Used primarily for beanbuilders to show something interesting.
508     *
509     * @return the default <code>TreeModel</code>
510     */
511    protected static TreeModel getDefaultTreeModel() {
512        DefaultMutableTreeNode      root = new DefaultMutableTreeNode("JTree");
513        DefaultMutableTreeNode      parent;
514
515        parent = new DefaultMutableTreeNode("colors");
516        root.add(parent);
517        parent.add(new DefaultMutableTreeNode("blue"));
518        parent.add(new DefaultMutableTreeNode("violet"));
519        parent.add(new DefaultMutableTreeNode("red"));
520        parent.add(new DefaultMutableTreeNode("yellow"));
521
522        parent = new DefaultMutableTreeNode("sports");
523        root.add(parent);
524        parent.add(new DefaultMutableTreeNode("basketball"));
525        parent.add(new DefaultMutableTreeNode("soccer"));
526        parent.add(new DefaultMutableTreeNode("football"));
527        parent.add(new DefaultMutableTreeNode("hockey"));
528
529        parent = new DefaultMutableTreeNode("food");
530        root.add(parent);
531        parent.add(new DefaultMutableTreeNode("hot dogs"));
532        parent.add(new DefaultMutableTreeNode("pizza"));
533        parent.add(new DefaultMutableTreeNode("ravioli"));
534        parent.add(new DefaultMutableTreeNode("bananas"));
535        return new DefaultTreeModel(root);
536    }
537
538    /**
539     * Returns a <code>TreeModel</code> wrapping the specified object.
540     * If the object is:<ul>
541     * <li>an array of <code>Object</code>s,
542     * <li>a <code>Hashtable</code>, or
543     * <li>a <code>Vector</code>
544     * </ul>then a new root node is created with each of the incoming
545     * objects as children. Otherwise, a new root is created with
546     * a value of {@code "root"}.
547     *
548     * @param value  the <code>Object</code> used as the foundation for
549     *          the <code>TreeModel</code>
550     * @return a <code>TreeModel</code> wrapping the specified object
551     */
552    protected static TreeModel createTreeModel(Object value) {
553        DefaultMutableTreeNode           root;
554
555        if((value instanceof Object[]) || (value instanceof Hashtable) ||
556           (value instanceof Vector)) {
557            root = new DefaultMutableTreeNode("root");
558            DynamicUtilTreeNode.createChildren(root, value);
559        }
560        else {
561            root = new DynamicUtilTreeNode("root", value);
562        }
563        return new DefaultTreeModel(root, false);
564    }
565
566    /**
567     * Returns a <code>JTree</code> with a sample model.
568     * The default model used by the tree defines a leaf node as any node
569     * without children.
570     *
571     * @see DefaultTreeModel#asksAllowsChildren
572     */
573    public JTree() {
574        this(getDefaultTreeModel());
575    }
576
577    /**
578     * Returns a <code>JTree</code> with each element of the
579     * specified array as the
580     * child of a new root node which is not displayed.
581     * By default, the tree defines a leaf node as any node without
582     * children.
583     *
584     * @param value  an array of <code>Object</code>s
585     * @see DefaultTreeModel#asksAllowsChildren
586     */
587    public JTree(Object[] value) {
588        this(createTreeModel(value));
589        this.setRootVisible(false);
590        this.setShowsRootHandles(true);
591        expandRoot();
592    }
593
594    /**
595     * Returns a <code>JTree</code> with each element of the specified
596     * <code>Vector</code> as the
597     * child of a new root node which is not displayed. By default, the
598     * tree defines a leaf node as any node without children.
599     *
600     * @param value  a <code>Vector</code>
601     * @see DefaultTreeModel#asksAllowsChildren
602     */
603    public JTree(Vector<?> value) {
604        this(createTreeModel(value));
605        this.setRootVisible(false);
606        this.setShowsRootHandles(true);
607        expandRoot();
608    }
609
610    /**
611     * Returns a <code>JTree</code> created from a <code>Hashtable</code>
612     * which does not display with root.
613     * Each value-half of the key/value pairs in the <code>HashTable</code>
614     * becomes a child of the new root node. By default, the tree defines
615     * a leaf node as any node without children.
616     *
617     * @param value  a <code>Hashtable</code>
618     * @see DefaultTreeModel#asksAllowsChildren
619     */
620    public JTree(Hashtable<?,?> value) {
621        this(createTreeModel(value));
622        this.setRootVisible(false);
623        this.setShowsRootHandles(true);
624        expandRoot();
625    }
626
627    /**
628     * Returns a <code>JTree</code> with the specified
629     * <code>TreeNode</code> as its root,
630     * which displays the root node.
631     * By default, the tree defines a leaf node as any node without children.
632     *
633     * @param root  a <code>TreeNode</code> object
634     * @see DefaultTreeModel#asksAllowsChildren
635     */
636    public JTree(TreeNode root) {
637        this(root, false);
638    }
639
640    /**
641     * Returns a <code>JTree</code> with the specified <code>TreeNode</code>
642     * as its root, which
643     * displays the root node and which decides whether a node is a
644     * leaf node in the specified manner.
645     *
646     * @param root  a <code>TreeNode</code> object
647     * @param asksAllowsChildren  if false, any node without children is a
648     *              leaf node; if true, only nodes that do not allow
649     *              children are leaf nodes
650     * @see DefaultTreeModel#asksAllowsChildren
651     */
652    public JTree(TreeNode root, boolean asksAllowsChildren) {
653        this(new DefaultTreeModel(root, asksAllowsChildren));
654    }
655
656    /**
657     * Returns an instance of <code>JTree</code> which displays the root node
658     * -- the tree is created using the specified data model.
659     *
660     * @param newModel  the <code>TreeModel</code> to use as the data model
661     */
662    @ConstructorProperties({"model"})
663    public JTree(TreeModel newModel) {
664        super();
665        expandedStack = new Stack<Stack<TreePath>>();
666        toggleClickCount = 2;
667        expandedState = new Hashtable<TreePath, Boolean>();
668        setLayout(null);
669        rowHeight = 16;
670        visibleRowCount = 20;
671        rootVisible = true;
672        selectionModel = new DefaultTreeSelectionModel();
673        cellRenderer = null;
674        scrollsOnExpand = true;
675        setOpaque(true);
676        expandsSelectedPaths = true;
677        updateUI();
678        setModel(newModel);
679    }
680
681    /**
682     * Returns the L&amp;F object that renders this component.
683     *
684     * @return the <code>TreeUI</code> object that renders this component
685     */
686    public TreeUI getUI() {
687        return (TreeUI)ui;
688    }
689
690    /**
691     * Sets the L&amp;F object that renders this component.
692     * <p>
693     * This is a bound property.
694     *
695     * @param ui  the <code>TreeUI</code> L&amp;F object
696     * @see UIDefaults#getUI
697     */
698    @BeanProperty(hidden = true, visualUpdate = true, description
699            = "The UI object that implements the Component's LookAndFeel.")
700    public void setUI(TreeUI ui) {
701        if (this.ui != ui) {
702            settingUI = true;
703            uiTreeExpansionListener = null;
704            try {
705                super.setUI(ui);
706            }
707            finally {
708                settingUI = false;
709            }
710        }
711    }
712
713    /**
714     * Notification from the <code>UIManager</code> that the L&amp;F has changed.
715     * Replaces the current UI object with the latest version from the
716     * <code>UIManager</code>.
717     *
718     * @see JComponent#updateUI
719     */
720    public void updateUI() {
721        if (!updateInProgress) {
722
723            updateInProgress = true;
724
725            try {
726                setUI((TreeUI)UIManager.getUI(this));
727
728                SwingUtilities.updateRendererOrEditorUI(getCellRenderer());
729                SwingUtilities.updateRendererOrEditorUI(getCellEditor());
730            } finally {
731                updateInProgress = false;
732            }
733        }
734    }
735
736
737    /**
738     * Returns the name of the L&amp;F class that renders this component.
739     *
740     * @return the string "TreeUI"
741     * @see JComponent#getUIClassID
742     * @see UIDefaults#getUI
743     */
744    @BeanProperty(bound = false)
745    public String getUIClassID() {
746        return uiClassID;
747    }
748
749
750    /**
751     * Returns the current <code>TreeCellRenderer</code>
752     *  that is rendering each cell.
753     *
754     * @return the <code>TreeCellRenderer</code> that is rendering each cell
755     */
756    public TreeCellRenderer getCellRenderer() {
757        return cellRenderer;
758    }
759
760    /**
761     * Sets the <code>TreeCellRenderer</code> that will be used to
762     * draw each cell.
763     * <p>
764     * This is a bound property.
765     *
766     * @param x  the <code>TreeCellRenderer</code> that is to render each cell
767     */
768    @BeanProperty(description
769            = "The TreeCellRenderer that will be used to draw each cell.")
770    public void setCellRenderer(TreeCellRenderer x) {
771        TreeCellRenderer oldValue = cellRenderer;
772
773        cellRenderer = x;
774        firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, cellRenderer);
775        invalidate();
776    }
777
778    /**
779      * Determines whether the tree is editable. Fires a property
780      * change event if the new setting is different from the existing
781      * setting.
782     * <p>
783     * This is a bound property.
784      *
785      * @param flag  a boolean value, true if the tree is editable
786      */
787    @BeanProperty(description
788            = "Whether the tree is editable.")
789    public void setEditable(boolean flag) {
790        boolean                 oldValue = this.editable;
791
792        this.editable = flag;
793        firePropertyChange(EDITABLE_PROPERTY, oldValue, flag);
794        if (accessibleContext != null) {
795            accessibleContext.firePropertyChange(
796                AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
797                (oldValue ? AccessibleState.EDITABLE : null),
798                (flag ? AccessibleState.EDITABLE : null));
799        }
800    }
801
802    /**
803     * Returns true if the tree is editable.
804     *
805     * @return true if the tree is editable
806     */
807    public boolean isEditable() {
808        return editable;
809    }
810
811    /**
812     * Sets the cell editor.  A <code>null</code> value implies that the
813     * tree cannot be edited.  If this represents a change in the
814     * <code>cellEditor</code>, the <code>propertyChange</code>
815     * method is invoked on all listeners.
816     * <p>
817     * This is a bound property.
818     *
819     * @param cellEditor the <code>TreeCellEditor</code> to use
820     */
821    @BeanProperty(description
822            = "The cell editor. A null value implies the tree cannot be edited.")
823    public void setCellEditor(TreeCellEditor cellEditor) {
824        TreeCellEditor        oldEditor = this.cellEditor;
825
826        this.cellEditor = cellEditor;
827        firePropertyChange(CELL_EDITOR_PROPERTY, oldEditor, cellEditor);
828        invalidate();
829    }
830
831    /**
832     * Returns the editor used to edit entries in the tree.
833     *
834     * @return the <code>TreeCellEditor</code> in use,
835     *          or <code>null</code> if the tree cannot be edited
836     */
837    public TreeCellEditor getCellEditor() {
838        return cellEditor;
839    }
840
841    /**
842     * Returns the <code>TreeModel</code> that is providing the data.
843     *
844     * @return the <code>TreeModel</code> that is providing the data
845     */
846    public TreeModel getModel() {
847        return treeModel;
848    }
849
850    /**
851     * Sets the <code>TreeModel</code> that will provide the data.
852     * <p>
853     * This is a bound property.
854     *
855     * @param newModel the <code>TreeModel</code> that is to provide the data
856     */
857    @BeanProperty(description
858            = "The TreeModel that will provide the data.")
859    public void setModel(TreeModel newModel) {
860        clearSelection();
861
862        TreeModel oldModel = treeModel;
863
864        if(treeModel != null && treeModelListener != null)
865            treeModel.removeTreeModelListener(treeModelListener);
866
867        if (accessibleContext != null) {
868            if (treeModel != null) {
869                treeModel.removeTreeModelListener((TreeModelListener)accessibleContext);
870            }
871            if (newModel != null) {
872                newModel.addTreeModelListener((TreeModelListener)accessibleContext);
873            }
874        }
875
876        treeModel = newModel;
877        clearToggledPaths();
878        if(treeModel != null) {
879            if(treeModelListener == null)
880                treeModelListener = createTreeModelListener();
881            if(treeModelListener != null)
882                treeModel.addTreeModelListener(treeModelListener);
883
884            // Mark the root as expanded, if it isn't a leaf.
885            Object treeRoot = treeModel.getRoot();
886            if(treeRoot != null &&
887               !treeModel.isLeaf(treeRoot)) {
888                expandedState.put(new TreePath(treeRoot),
889                                    Boolean.TRUE);
890            }
891        }
892        firePropertyChange(TREE_MODEL_PROPERTY, oldModel, treeModel);
893        invalidate();
894    }
895
896    /**
897     * Returns true if the root node of the tree is displayed.
898     *
899     * @return true if the root node of the tree is displayed
900     * @see #rootVisible
901     */
902    public boolean isRootVisible() {
903        return rootVisible;
904    }
905
906    /**
907     * Determines whether or not the root node from
908     * the <code>TreeModel</code> is visible.
909     * <p>
910     * This is a bound property.
911     *
912     * @param rootVisible true if the root node of the tree is to be displayed
913     * @see #rootVisible
914     */
915    @BeanProperty(description
916            = "Whether or not the root node from the TreeModel is visible.")
917    public void setRootVisible(boolean rootVisible) {
918        boolean                oldValue = this.rootVisible;
919
920        this.rootVisible = rootVisible;
921        firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, this.rootVisible);
922        if (accessibleContext != null) {
923            ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
924        }
925    }
926
927    /**
928     * Sets the value of the <code>showsRootHandles</code> property,
929     * which specifies whether the node handles should be displayed.
930     * The default value of this property depends on the constructor
931     * used to create the <code>JTree</code>.
932     * Some look and feels might not support handles;
933     * they will ignore this property.
934     * <p>
935     * This is a bound property.
936     *
937     * @param newValue <code>true</code> if root handles should be displayed;
938     *                 otherwise, <code>false</code>
939     * @see #showsRootHandles
940     * @see #getShowsRootHandles
941     */
942    @BeanProperty(description
943            = "Whether the node handles are to be displayed.")
944    public void setShowsRootHandles(boolean newValue) {
945        boolean                oldValue = showsRootHandles;
946        TreeModel              model = getModel();
947
948        showsRootHandles = newValue;
949        showsRootHandlesSet = true;
950        firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue,
951                           showsRootHandles);
952        if (accessibleContext != null) {
953            ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
954        }
955        invalidate();
956    }
957
958    /**
959     * Returns the value of the <code>showsRootHandles</code> property.
960     *
961     * @return the value of the <code>showsRootHandles</code> property
962     * @see #showsRootHandles
963     */
964    public boolean getShowsRootHandles()
965    {
966        return showsRootHandles;
967    }
968
969    /**
970     * Sets the height of each cell, in pixels.  If the specified value
971     * is less than or equal to zero the current cell renderer is
972     * queried for each row's height.
973     * <p>
974     * This is a bound property.
975     *
976     * @param rowHeight the height of each cell, in pixels
977     */
978    @BeanProperty(description
979            = "The height of each cell.")
980    public void setRowHeight(int rowHeight)
981    {
982        int                oldValue = this.rowHeight;
983
984        this.rowHeight = rowHeight;
985        rowHeightSet = true;
986        firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, this.rowHeight);
987        invalidate();
988    }
989
990    /**
991     * Returns the height of each row.  If the returned value is less than
992     * or equal to 0 the height for each row is determined by the
993     * renderer.
994     *
995     * @return the height of each row
996     */
997    public int getRowHeight()
998    {
999        return rowHeight;
1000    }
1001
1002    /**
1003     * Returns true if the height of each display row is a fixed size.
1004     *
1005     * @return true if the height of each row is a fixed size
1006     */
1007    @BeanProperty(bound = false)
1008    public boolean isFixedRowHeight()
1009    {
1010        return (rowHeight > 0);
1011    }
1012
1013    /**
1014     * Specifies whether the UI should use a large model.
1015     * (Not all UIs will implement this.) Fires a property change
1016     * for the LARGE_MODEL_PROPERTY.
1017     * <p>
1018     * This is a bound property.
1019     *
1020     * @param newValue true to suggest a large model to the UI
1021     * @see #largeModel
1022     */
1023    @BeanProperty(description
1024            = "Whether the UI should use a large model.")
1025    public void setLargeModel(boolean newValue) {
1026        boolean                oldValue = largeModel;
1027
1028        largeModel = newValue;
1029        firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, newValue);
1030    }
1031
1032    /**
1033     * Returns true if the tree is configured for a large model.
1034     *
1035     * @return true if a large model is suggested
1036     * @see #largeModel
1037     */
1038    public boolean isLargeModel() {
1039        return largeModel;
1040    }
1041
1042    /**
1043     * Determines what happens when editing is interrupted by selecting
1044     * another node in the tree, a change in the tree's data, or by some
1045     * other means. Setting this property to <code>true</code> causes the
1046     * changes to be automatically saved when editing is interrupted.
1047     * <p>
1048     * Fires a property change for the INVOKES_STOP_CELL_EDITING_PROPERTY.
1049     *
1050     * @param newValue true means that <code>stopCellEditing</code> is invoked
1051     *        when editing is interrupted, and data is saved; false means that
1052     *        <code>cancelCellEditing</code> is invoked, and changes are lost
1053     */
1054    @BeanProperty(description
1055            = "Determines what happens when editing is interrupted, selecting another node in the tree, "
1056            + "a change in the tree's data, or some other means.")
1057    public void setInvokesStopCellEditing(boolean newValue) {
1058        boolean                  oldValue = invokesStopCellEditing;
1059
1060        invokesStopCellEditing = newValue;
1061        firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, oldValue,
1062                           newValue);
1063    }
1064
1065    /**
1066     * Returns the indicator that tells what happens when editing is
1067     * interrupted.
1068     *
1069     * @return the indicator that tells what happens when editing is
1070     *         interrupted
1071     * @see #setInvokesStopCellEditing
1072     */
1073    public boolean getInvokesStopCellEditing() {
1074        return invokesStopCellEditing;
1075    }
1076
1077    /**
1078     * Sets the <code>scrollsOnExpand</code> property,
1079     * which determines whether the
1080     * tree might scroll to show previously hidden children.
1081     * If this property is <code>true</code> (the default),
1082     * when a node expands
1083     * the tree can use scrolling to make
1084     * the maximum possible number of the node's descendants visible.
1085     * In some look and feels, trees might not need to scroll when expanded;
1086     * those look and feels will ignore this property.
1087     * <p>
1088     * This is a bound property.
1089     *
1090     * @param newValue <code>false</code> to disable scrolling on expansion;
1091     *                 <code>true</code> to enable it
1092     * @see #getScrollsOnExpand
1093     */
1094    @BeanProperty(description
1095            = "Indicates if a node descendant should be scrolled when expanded.")
1096    public void setScrollsOnExpand(boolean newValue) {
1097        boolean           oldValue = scrollsOnExpand;
1098
1099        scrollsOnExpand = newValue;
1100        scrollsOnExpandSet = true;
1101        firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue,
1102                           newValue);
1103    }
1104
1105    /**
1106     * Returns the value of the <code>scrollsOnExpand</code> property.
1107     *
1108     * @return the value of the <code>scrollsOnExpand</code> property
1109     */
1110    public boolean getScrollsOnExpand() {
1111        return scrollsOnExpand;
1112    }
1113
1114    /**
1115     * Sets the number of mouse clicks before a node will expand or close.
1116     * The default is two.
1117     * <p>
1118     * This is a bound property.
1119     *
1120     * @param clickCount the number of mouse clicks to get a node expanded or closed
1121     * @since 1.3
1122     */
1123    @BeanProperty(description
1124            = "Number of clicks before a node will expand/collapse.")
1125    public void setToggleClickCount(int clickCount) {
1126        int         oldCount = toggleClickCount;
1127
1128        toggleClickCount = clickCount;
1129        firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldCount,
1130                           clickCount);
1131    }
1132
1133    /**
1134     * Returns the number of mouse clicks needed to expand or close a node.
1135     *
1136     * @return number of mouse clicks before node is expanded
1137     * @since 1.3
1138     */
1139    public int getToggleClickCount() {
1140        return toggleClickCount;
1141    }
1142
1143    /**
1144     * Configures the <code>expandsSelectedPaths</code> property. If
1145     * true, any time the selection is changed, either via the
1146     * <code>TreeSelectionModel</code>, or the cover methods provided by
1147     * <code>JTree</code>, the <code>TreePath</code>s parents will be
1148     * expanded to make them visible (visible meaning the parent path is
1149     * expanded, not necessarily in the visible rectangle of the
1150     * <code>JTree</code>). If false, when the selection
1151     * changes the nodes parent is not made visible (all its parents expanded).
1152     * This is useful if you wish to have your selection model maintain paths
1153     * that are not always visible (all parents expanded).
1154     * <p>
1155     * This is a bound property.
1156     *
1157     * @param newValue the new value for <code>expandsSelectedPaths</code>
1158     *
1159     * @since 1.3
1160     */
1161    @BeanProperty(description
1162            = "Indicates whether changes to the selection should make the parent of the path visible.")
1163    public void setExpandsSelectedPaths(boolean newValue) {
1164        boolean         oldValue = expandsSelectedPaths;
1165
1166        expandsSelectedPaths = newValue;
1167        firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue,
1168                           newValue);
1169    }
1170
1171    /**
1172     * Returns the <code>expandsSelectedPaths</code> property.
1173     * @return true if selection changes result in the parent path being
1174     *         expanded
1175     * @since 1.3
1176     * @see #setExpandsSelectedPaths
1177     */
1178    public boolean getExpandsSelectedPaths() {
1179        return expandsSelectedPaths;
1180    }
1181
1182    /**
1183     * Turns on or off automatic drag handling. In order to enable automatic
1184     * drag handling, this property should be set to {@code true}, and the
1185     * tree's {@code TransferHandler} needs to be {@code non-null}.
1186     * The default value of the {@code dragEnabled} property is {@code false}.
1187     * <p>
1188     * The job of honoring this property, and recognizing a user drag gesture,
1189     * lies with the look and feel implementation, and in particular, the tree's
1190     * {@code TreeUI}. When automatic drag handling is enabled, most look and
1191     * feels (including those that subclass {@code BasicLookAndFeel}) begin a
1192     * drag and drop operation whenever the user presses the mouse button over
1193     * an item and then moves the mouse a few pixels. Setting this property to
1194     * {@code true} can therefore have a subtle effect on how selections behave.
1195     * <p>
1196     * If a look and feel is used that ignores this property, you can still
1197     * begin a drag and drop operation by calling {@code exportAsDrag} on the
1198     * tree's {@code TransferHandler}.
1199     *
1200     * @param b whether or not to enable automatic drag handling
1201     * @exception HeadlessException if
1202     *            <code>b</code> is <code>true</code> and
1203     *            <code>GraphicsEnvironment.isHeadless()</code>
1204     *            returns <code>true</code>
1205     * @see java.awt.GraphicsEnvironment#isHeadless
1206     * @see #getDragEnabled
1207     * @see #setTransferHandler
1208     * @see TransferHandler
1209     * @since 1.4
1210     */
1211    @BeanProperty(bound = false, description
1212            = "determines whether automatic drag handling is enabled")
1213    public void setDragEnabled(boolean b) {
1214        checkDragEnabled(b);
1215        dragEnabled = b;
1216    }
1217
1218    private static void checkDragEnabled(boolean b) {
1219        if (b && GraphicsEnvironment.isHeadless()) {
1220            throw new HeadlessException();
1221        }
1222    }
1223
1224    /**
1225     * Returns whether or not automatic drag handling is enabled.
1226     *
1227     * @return the value of the {@code dragEnabled} property
1228     * @see #setDragEnabled
1229     * @since 1.4
1230     */
1231    public boolean getDragEnabled() {
1232        return dragEnabled;
1233    }
1234
1235    /**
1236     * Sets the drop mode for this component. For backward compatibility,
1237     * the default for this property is <code>DropMode.USE_SELECTION</code>.
1238     * Usage of one of the other modes is recommended, however, for an
1239     * improved user experience. <code>DropMode.ON</code>, for instance,
1240     * offers similar behavior of showing items as selected, but does so without
1241     * affecting the actual selection in the tree.
1242     * <p>
1243     * <code>JTree</code> supports the following drop modes:
1244     * <ul>
1245     *    <li><code>DropMode.USE_SELECTION</code></li>
1246     *    <li><code>DropMode.ON</code></li>
1247     *    <li><code>DropMode.INSERT</code></li>
1248     *    <li><code>DropMode.ON_OR_INSERT</code></li>
1249     * </ul>
1250     * <p>
1251     * The drop mode is only meaningful if this component has a
1252     * <code>TransferHandler</code> that accepts drops.
1253     *
1254     * @param dropMode the drop mode to use
1255     * @throws IllegalArgumentException if the drop mode is unsupported
1256     *         or <code>null</code>
1257     * @see #getDropMode
1258     * @see #getDropLocation
1259     * @see #setTransferHandler
1260     * @see TransferHandler
1261     * @since 1.6
1262     */
1263    public final void setDropMode(DropMode dropMode) {
1264        checkDropMode(dropMode);
1265        this.dropMode = dropMode;
1266    }
1267
1268    private static void checkDropMode(DropMode dropMode) {
1269        if (dropMode != null) {
1270            switch (dropMode) {
1271                case USE_SELECTION:
1272                case ON:
1273                case INSERT:
1274                case ON_OR_INSERT:
1275                    return;
1276            }
1277        }
1278
1279        throw new IllegalArgumentException(dropMode +
1280                ": Unsupported drop mode for tree");
1281    }
1282
1283    /**
1284     * Returns the drop mode for this component.
1285     *
1286     * @return the drop mode for this component
1287     * @see #setDropMode
1288     * @since 1.6
1289     */
1290    public final DropMode getDropMode() {
1291        return dropMode;
1292    }
1293
1294    /**
1295     * Calculates a drop location in this component, representing where a
1296     * drop at the given point should insert data.
1297     *
1298     * @param p the point to calculate a drop location for
1299     * @return the drop location, or <code>null</code>
1300     */
1301    DropLocation dropLocationForPoint(Point p) {
1302        DropLocation location = null;
1303
1304        int row = getClosestRowForLocation(p.x, p.y);
1305        Rectangle bounds = getRowBounds(row);
1306        TreeModel model = getModel();
1307        Object root = (model == null) ? null : model.getRoot();
1308        TreePath rootPath = (root == null) ? null : new TreePath(root);
1309
1310        TreePath child;
1311        TreePath parent;
1312        boolean outside = row == -1
1313                          || p.y < bounds.y
1314                          || p.y >= bounds.y + bounds.height;
1315
1316        switch(dropMode) {
1317            case USE_SELECTION:
1318            case ON:
1319                if (outside) {
1320                    location = new DropLocation(p, null, -1);
1321                } else {
1322                    location = new DropLocation(p, getPathForRow(row), -1);
1323                }
1324
1325                break;
1326            case INSERT:
1327            case ON_OR_INSERT:
1328                if (row == -1) {
1329                    if (root != null && !model.isLeaf(root) && isExpanded(rootPath)) {
1330                        location = new DropLocation(p, rootPath, 0);
1331                    } else {
1332                        location = new DropLocation(p, null, -1);
1333                    }
1334
1335                    break;
1336                }
1337
1338                boolean checkOn = dropMode == DropMode.ON_OR_INSERT
1339                                  || !model.isLeaf(getPathForRow(row).getLastPathComponent());
1340
1341                Section section = SwingUtilities2.liesInVertical(bounds, p, checkOn);
1342                if(section == LEADING) {
1343                    child = getPathForRow(row);
1344                    parent = child.getParentPath();
1345                } else if (section == TRAILING) {
1346                    int index = row + 1;
1347                    if (index >= getRowCount()) {
1348                        if (model.isLeaf(root) || !isExpanded(rootPath)) {
1349                            location = new DropLocation(p, null, -1);
1350                        } else {
1351                            parent = rootPath;
1352                            index = model.getChildCount(root);
1353                            location = new DropLocation(p, parent, index);
1354                        }
1355
1356                        break;
1357                    }
1358
1359                    child = getPathForRow(index);
1360                    parent = child.getParentPath();
1361                    TreePath prev = getPathForRow(row).getParentPath();
1362                    if (prev != null && !prev.equals(parent)) {
1363                        location = new DropLocation(p, prev,
1364                              model.getChildCount(prev.getLastPathComponent()));
1365                        break;
1366                    }
1367
1368                } else {
1369                    assert checkOn;
1370                    location = new DropLocation(p, getPathForRow(row), -1);
1371                    break;
1372                }
1373
1374                if (parent != null) {
1375                    location = new DropLocation(p, parent,
1376                        model.getIndexOfChild(parent.getLastPathComponent(),
1377                                              child.getLastPathComponent()));
1378                } else if (checkOn || !model.isLeaf(root)) {
1379                    location = new DropLocation(p, rootPath, -1);
1380                } else {
1381                    location = new DropLocation(p, null, -1);
1382                }
1383
1384                break;
1385            default:
1386                assert false : "Unexpected drop mode";
1387        }
1388
1389        if (outside || row != expandRow) {
1390            cancelDropTimer();
1391        }
1392
1393        if (!outside && row != expandRow) {
1394            if (isCollapsed(row)) {
1395                expandRow = row;
1396                startDropTimer();
1397            }
1398        }
1399
1400        return location;
1401    }
1402
1403    /**
1404     * Called to set or clear the drop location during a DnD operation.
1405     * In some cases, the component may need to use it's internal selection
1406     * temporarily to indicate the drop location. To help facilitate this,
1407     * this method returns and accepts as a parameter a state object.
1408     * This state object can be used to store, and later restore, the selection
1409     * state. Whatever this method returns will be passed back to it in
1410     * future calls, as the state parameter. If it wants the DnD system to
1411     * continue storing the same state, it must pass it back every time.
1412     * Here's how this is used:
1413     * <p>
1414     * Let's say that on the first call to this method the component decides
1415     * to save some state (because it is about to use the selection to show
1416     * a drop index). It can return a state object to the caller encapsulating
1417     * any saved selection state. On a second call, let's say the drop location
1418     * is being changed to something else. The component doesn't need to
1419     * restore anything yet, so it simply passes back the same state object
1420     * to have the DnD system continue storing it. Finally, let's say this
1421     * method is messaged with <code>null</code>. This means DnD
1422     * is finished with this component for now, meaning it should restore
1423     * state. At this point, it can use the state parameter to restore
1424     * said state, and of course return <code>null</code> since there's
1425     * no longer anything to store.
1426     *
1427     * @param location the drop location (as calculated by
1428     *        <code>dropLocationForPoint</code>) or <code>null</code>
1429     *        if there's no longer a valid drop location
1430     * @param state the state object saved earlier for this component,
1431     *        or <code>null</code>
1432     * @param forDrop whether or not the method is being called because an
1433     *        actual drop occurred
1434     * @return any saved state for this component, or <code>null</code> if none
1435     */
1436    Object setDropLocation(TransferHandler.DropLocation location,
1437                           Object state,
1438                           boolean forDrop) {
1439
1440        Object retVal = null;
1441        DropLocation treeLocation = (DropLocation)location;
1442
1443        if (dropMode == DropMode.USE_SELECTION) {
1444            if (treeLocation == null) {
1445                if (!forDrop && state != null) {
1446                    setSelectionPaths(((TreePath[][])state)[0]);
1447                    setAnchorSelectionPath(((TreePath[][])state)[1][0]);
1448                    setLeadSelectionPath(((TreePath[][])state)[1][1]);
1449                }
1450            } else {
1451                if (dropLocation == null) {
1452                    TreePath[] paths = getSelectionPaths();
1453                    if (paths == null) {
1454                        paths = new TreePath[0];
1455                    }
1456
1457                    retVal = new TreePath[][] {paths,
1458                            {getAnchorSelectionPath(), getLeadSelectionPath()}};
1459                } else {
1460                    retVal = state;
1461                }
1462
1463                setSelectionPath(treeLocation.getPath());
1464            }
1465        }
1466
1467        DropLocation old = dropLocation;
1468        dropLocation = treeLocation;
1469        firePropertyChange("dropLocation", old, dropLocation);
1470
1471        return retVal;
1472    }
1473
1474    /**
1475     * Called to indicate to this component that DnD is done.
1476     * Allows for us to cancel the expand timer.
1477     */
1478    void dndDone() {
1479        cancelDropTimer();
1480        dropTimer = null;
1481    }
1482
1483    /**
1484     * Returns the location that this component should visually indicate
1485     * as the drop location during a DnD operation over the component,
1486     * or {@code null} if no location is to currently be shown.
1487     * <p>
1488     * This method is not meant for querying the drop location
1489     * from a {@code TransferHandler}, as the drop location is only
1490     * set after the {@code TransferHandler}'s <code>canImport</code>
1491     * has returned and has allowed for the location to be shown.
1492     * <p>
1493     * When this property changes, a property change event with
1494     * name "dropLocation" is fired by the component.
1495     *
1496     * @return the drop location
1497     * @see #setDropMode
1498     * @see TransferHandler#canImport(TransferHandler.TransferSupport)
1499     * @since 1.6
1500     */
1501    @BeanProperty(bound = false)
1502    public final DropLocation getDropLocation() {
1503        return dropLocation;
1504    }
1505
1506    private void startDropTimer() {
1507        if (dropTimer == null) {
1508            dropTimer = new TreeTimer();
1509        }
1510        dropTimer.start();
1511    }
1512
1513    private void cancelDropTimer() {
1514        if (dropTimer != null && dropTimer.isRunning()) {
1515            expandRow = -1;
1516            dropTimer.stop();
1517        }
1518    }
1519
1520    /**
1521     * Returns <code>isEditable</code>. This is invoked from the UI before
1522     * editing begins to insure that the given path can be edited. This
1523     * is provided as an entry point for subclassers to add filtered
1524     * editing without having to resort to creating a new editor.
1525     *
1526     * @param path a {@code TreePath} identifying a node
1527     * @return true if every parent node and the node itself is editable
1528     * @see #isEditable
1529     */
1530    public boolean isPathEditable(TreePath path) {
1531        return isEditable();
1532    }
1533
1534    /**
1535     * Overrides <code>JComponent</code>'s <code>getToolTipText</code>
1536     * method in order to allow
1537     * renderer's tips to be used if it has text set.
1538     * <p>
1539     * NOTE: For <code>JTree</code> to properly display tooltips of its
1540     * renderers, <code>JTree</code> must be a registered component with the
1541     * <code>ToolTipManager</code>.  This can be done by invoking
1542     * <code>ToolTipManager.sharedInstance().registerComponent(tree)</code>.
1543     * This is not done automatically!
1544     *
1545     * @param event the <code>MouseEvent</code> that initiated the
1546     *          <code>ToolTip</code> display
1547     * @return a string containing the  tooltip or <code>null</code>
1548     *          if <code>event</code> is null
1549     */
1550    public String getToolTipText(MouseEvent event) {
1551        String tip = null;
1552
1553        if(event != null) {
1554            Point p = event.getPoint();
1555            int selRow = getRowForLocation(p.x, p.y);
1556            TreeCellRenderer       r = getCellRenderer();
1557
1558            if(selRow != -1 && r != null) {
1559                TreePath     path = getPathForRow(selRow);
1560                Object       lastPath = path.getLastPathComponent();
1561                Component    rComponent = r.getTreeCellRendererComponent
1562                    (this, lastPath, isRowSelected(selRow),
1563                     isExpanded(selRow), getModel().isLeaf(lastPath), selRow,
1564                     true);
1565
1566                if(rComponent instanceof JComponent) {
1567                    MouseEvent      newEvent;
1568                    Rectangle       pathBounds = getPathBounds(path);
1569
1570                    p.translate(-pathBounds.x, -pathBounds.y);
1571                    @SuppressWarnings("deprecation")
1572                    final int modifiers = event.getModifiers();
1573                    newEvent = new MouseEvent(rComponent, event.getID(),
1574                                          event.getWhen(), modifiers,
1575                                              p.x, p.y,
1576                                              event.getXOnScreen(),
1577                                              event.getYOnScreen(),
1578                                              event.getClickCount(),
1579                                              event.isPopupTrigger(),
1580                                              MouseEvent.NOBUTTON);
1581
1582                    tip = ((JComponent)rComponent).getToolTipText(newEvent);
1583                }
1584            }
1585        }
1586        // No tip from the renderer get our own tip
1587        if (tip == null) {
1588            tip = getToolTipText();
1589        }
1590        return tip;
1591    }
1592
1593    /**
1594     * Called by the renderers to convert the specified value to
1595     * text. This implementation returns <code>value.toString</code>, ignoring
1596     * all other arguments. To control the conversion, subclass this
1597     * method and use any of the arguments you need.
1598     *
1599     * @param value the <code>Object</code> to convert to text
1600     * @param selected true if the node is selected
1601     * @param expanded true if the node is expanded
1602     * @param leaf  true if the node is a leaf node
1603     * @param row  an integer specifying the node's display row, where 0 is
1604     *             the first row in the display
1605     * @param hasFocus true if the node has the focus
1606     * @return the <code>String</code> representation of the node's value
1607     */
1608    public String convertValueToText(Object value, boolean selected,
1609                                     boolean expanded, boolean leaf, int row,
1610                                     boolean hasFocus) {
1611        if(value != null) {
1612            String sValue = value.toString();
1613            if (sValue != null) {
1614                return sValue;
1615            }
1616        }
1617        return "";
1618    }
1619
1620    //
1621    // The following are convenience methods that get forwarded to the
1622    // current TreeUI.
1623    //
1624
1625    /**
1626     * Returns the number of viewable nodes. A node is viewable if all of its
1627     * parents are expanded. The root is only included in this count if
1628     * {@code isRootVisible()} is {@code true}. This returns {@code 0} if
1629     * the UI has not been set.
1630     *
1631     * @return the number of viewable nodes
1632     */
1633    @BeanProperty(bound = false)
1634    public int getRowCount() {
1635        TreeUI            tree = getUI();
1636
1637        if(tree != null)
1638            return tree.getRowCount(this);
1639        return 0;
1640    }
1641
1642    /**
1643     * Selects the node identified by the specified path. If any
1644     * component of the path is hidden (under a collapsed node), and
1645     * <code>getExpandsSelectedPaths</code> is true it is
1646     * exposed (made viewable).
1647     *
1648     * @param path the <code>TreePath</code> specifying the node to select
1649     */
1650    public void setSelectionPath(TreePath path) {
1651        getSelectionModel().setSelectionPath(path);
1652    }
1653
1654    /**
1655     * Selects the nodes identified by the specified array of paths.
1656     * If any component in any of the paths is hidden (under a collapsed
1657     * node), and <code>getExpandsSelectedPaths</code> is true
1658     * it is exposed (made viewable).
1659     *
1660     * @param paths an array of <code>TreePath</code> objects that specifies
1661     *          the nodes to select
1662     */
1663    public void setSelectionPaths(TreePath[] paths) {
1664        getSelectionModel().setSelectionPaths(paths);
1665    }
1666
1667    /**
1668     * Sets the path identifies as the lead. The lead may not be selected.
1669     * The lead is not maintained by <code>JTree</code>,
1670     * rather the UI will update it.
1671     * <p>
1672     * This is a bound property.
1673     *
1674     * @param newPath  the new lead path
1675     * @since 1.3
1676     */
1677    @BeanProperty(description
1678            = "Lead selection path")
1679    public void setLeadSelectionPath(TreePath newPath) {
1680        TreePath          oldValue = leadPath;
1681
1682        leadPath = newPath;
1683        firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, newPath);
1684
1685        // Fire the active descendant property change here since the
1686        // leadPath got set, this is triggered both in case node
1687        // selection changed and node focus changed
1688        if (accessibleContext != null){
1689            ((AccessibleJTree)accessibleContext).
1690                fireActiveDescendantPropertyChange(oldValue, newPath);
1691        }
1692    }
1693
1694    /**
1695     * Sets the path identified as the anchor.
1696     * The anchor is not maintained by <code>JTree</code>, rather the UI will
1697     * update it.
1698     * <p>
1699     * This is a bound property.
1700     *
1701     * @param newPath  the new anchor path
1702     * @since 1.3
1703     */
1704    @BeanProperty(description
1705            = "Anchor selection path")
1706    public void setAnchorSelectionPath(TreePath newPath) {
1707        TreePath          oldValue = anchorPath;
1708
1709        anchorPath = newPath;
1710        firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, newPath);
1711    }
1712
1713    /**
1714     * Selects the node at the specified row in the display.
1715     *
1716     * @param row  the row to select, where 0 is the first row in
1717     *             the display
1718     */
1719    public void setSelectionRow(int row) {
1720        int[]             rows = { row };
1721
1722        setSelectionRows(rows);
1723    }
1724
1725    /**
1726     * Selects the nodes corresponding to each of the specified rows
1727     * in the display. If a particular element of <code>rows</code> is
1728     * &lt; 0 or &gt;= <code>getRowCount</code>, it will be ignored.
1729     * If none of the elements
1730     * in <code>rows</code> are valid rows, the selection will
1731     * be cleared. That is it will be as if <code>clearSelection</code>
1732     * was invoked.
1733     *
1734     * @param rows  an array of ints specifying the rows to select,
1735     *              where 0 indicates the first row in the display
1736     */
1737    public void setSelectionRows(int[] rows) {
1738        TreeUI               ui = getUI();
1739
1740        if(ui != null && rows != null) {
1741            int                  numRows = rows.length;
1742            TreePath[]           paths = new TreePath[numRows];
1743
1744            for(int counter = 0; counter < numRows; counter++) {
1745                paths[counter] = ui.getPathForRow(this, rows[counter]);
1746            }
1747            setSelectionPaths(paths);
1748        }
1749    }
1750
1751    /**
1752     * Adds the node identified by the specified <code>TreePath</code>
1753     * to the current selection. If any component of the path isn't
1754     * viewable, and <code>getExpandsSelectedPaths</code> is true it is
1755     * made viewable.
1756     * <p>
1757     * Note that <code>JTree</code> does not allow duplicate nodes to
1758     * exist as children under the same parent -- each sibling must be
1759     * a unique object.
1760     *
1761     * @param path the <code>TreePath</code> to add
1762     */
1763    public void addSelectionPath(TreePath path) {
1764        getSelectionModel().addSelectionPath(path);
1765    }
1766
1767    /**
1768     * Adds each path in the array of paths to the current selection. If
1769     * any component of any of the paths isn't viewable and
1770     * <code>getExpandsSelectedPaths</code> is true, it is
1771     * made viewable.
1772     * <p>
1773     * Note that <code>JTree</code> does not allow duplicate nodes to
1774     * exist as children under the same parent -- each sibling must be
1775     * a unique object.
1776     *
1777     * @param paths an array of <code>TreePath</code> objects that specifies
1778     *          the nodes to add
1779     */
1780    public void addSelectionPaths(TreePath[] paths) {
1781        getSelectionModel().addSelectionPaths(paths);
1782    }
1783
1784    /**
1785     * Adds the path at the specified row to the current selection.
1786     *
1787     * @param row  an integer specifying the row of the node to add,
1788     *             where 0 is the first row in the display
1789     */
1790    public void addSelectionRow(int row) {
1791        int[]      rows = { row };
1792
1793        addSelectionRows(rows);
1794    }
1795
1796    /**
1797     * Adds the paths at each of the specified rows to the current selection.
1798     *
1799     * @param rows  an array of ints specifying the rows to add,
1800     *              where 0 indicates the first row in the display
1801     */
1802    public void addSelectionRows(int[] rows) {
1803        TreeUI             ui = getUI();
1804
1805        if(ui != null && rows != null) {
1806            int                  numRows = rows.length;
1807            TreePath[]           paths = new TreePath[numRows];
1808
1809            for(int counter = 0; counter < numRows; counter++)
1810                paths[counter] = ui.getPathForRow(this, rows[counter]);
1811            addSelectionPaths(paths);
1812        }
1813    }
1814
1815    /**
1816     * Returns the last path component of the selected path. This is
1817     * a convenience method for
1818     * {@code getSelectionModel().getSelectionPath().getLastPathComponent()}.
1819     * This is typically only useful if the selection has one path.
1820     *
1821     * @return the last path component of the selected path, or
1822     *         <code>null</code> if nothing is selected
1823     * @see TreePath#getLastPathComponent
1824     */
1825    @BeanProperty(bound = false)
1826    public Object getLastSelectedPathComponent() {
1827        TreePath     selPath = getSelectionModel().getSelectionPath();
1828
1829        if(selPath != null)
1830            return selPath.getLastPathComponent();
1831        return null;
1832    }
1833
1834    /**
1835     * Returns the path identified as the lead.
1836     * @return path identified as the lead
1837     */
1838    public TreePath getLeadSelectionPath() {
1839        return leadPath;
1840    }
1841
1842    /**
1843     * Returns the path identified as the anchor.
1844     * @return path identified as the anchor
1845     * @since 1.3
1846     */
1847    public TreePath getAnchorSelectionPath() {
1848        return anchorPath;
1849    }
1850
1851    /**
1852     * Returns the path to the first selected node.
1853     *
1854     * @return the <code>TreePath</code> for the first selected node,
1855     *          or <code>null</code> if nothing is currently selected
1856     */
1857    public TreePath getSelectionPath() {
1858        return getSelectionModel().getSelectionPath();
1859    }
1860
1861    /**
1862     * Returns the paths of all selected values.
1863     *
1864     * @return an array of <code>TreePath</code> objects indicating the selected
1865     *         nodes, or <code>null</code> if nothing is currently selected
1866     */
1867    public TreePath[] getSelectionPaths() {
1868        TreePath[] selectionPaths = getSelectionModel().getSelectionPaths();
1869
1870        return (selectionPaths != null && selectionPaths.length > 0) ? selectionPaths : null;
1871    }
1872
1873    /**
1874     * Returns all of the currently selected rows. This method is simply
1875     * forwarded to the <code>TreeSelectionModel</code>.
1876     * If nothing is selected <code>null</code> or an empty array will
1877     * be returned, based on the <code>TreeSelectionModel</code>
1878     * implementation.
1879     *
1880     * @return an array of integers that identifies all currently selected rows
1881     *         where 0 is the first row in the display
1882     */
1883    public int[] getSelectionRows() {
1884        return getSelectionModel().getSelectionRows();
1885    }
1886
1887    /**
1888     * Returns the number of nodes selected.
1889     *
1890     * @return the number of nodes selected
1891     */
1892    @BeanProperty(bound = false)
1893    public int getSelectionCount() {
1894        return selectionModel.getSelectionCount();
1895    }
1896
1897    /**
1898     * Returns the smallest selected row. If the selection is empty, or
1899     * none of the selected paths are viewable, {@code -1} is returned.
1900     *
1901     * @return the smallest selected row
1902     */
1903    @BeanProperty(bound = false)
1904    public int getMinSelectionRow() {
1905        return getSelectionModel().getMinSelectionRow();
1906    }
1907
1908    /**
1909     * Returns the largest selected row. If the selection is empty, or
1910     * none of the selected paths are viewable, {@code -1} is returned.
1911     *
1912     * @return the largest selected row
1913     */
1914    @BeanProperty(bound = false)
1915    public int getMaxSelectionRow() {
1916        return getSelectionModel().getMaxSelectionRow();
1917    }
1918
1919    /**
1920     * Returns the row index corresponding to the lead path.
1921     *
1922     * @return an integer giving the row index of the lead path,
1923     *          where 0 is the first row in the display; or -1
1924     *          if <code>leadPath</code> is <code>null</code>
1925     */
1926    @BeanProperty(bound = false)
1927    public int getLeadSelectionRow() {
1928        TreePath leadPath = getLeadSelectionPath();
1929
1930        if (leadPath != null) {
1931            return getRowForPath(leadPath);
1932        }
1933        return -1;
1934    }
1935
1936    /**
1937     * Returns true if the item identified by the path is currently selected.
1938     *
1939     * @param path a <code>TreePath</code> identifying a node
1940     * @return true if the node is selected
1941     */
1942    public boolean isPathSelected(TreePath path) {
1943        return getSelectionModel().isPathSelected(path);
1944    }
1945
1946    /**
1947     * Returns true if the node identified by row is selected.
1948     *
1949     * @param row  an integer specifying a display row, where 0 is the first
1950     *             row in the display
1951     * @return true if the node is selected
1952     */
1953    public boolean isRowSelected(int row) {
1954        return getSelectionModel().isRowSelected(row);
1955    }
1956
1957    /**
1958     * Returns an <code>Enumeration</code> of the descendants of the
1959     * path <code>parent</code> that
1960     * are currently expanded. If <code>parent</code> is not currently
1961     * expanded, this will return <code>null</code>.
1962     * If you expand/collapse nodes while
1963     * iterating over the returned <code>Enumeration</code>
1964     * this may not return all
1965     * the expanded paths, or may return paths that are no longer expanded.
1966     *
1967     * @param parent  the path which is to be examined
1968     * @return an <code>Enumeration</code> of the descendents of
1969     *          <code>parent</code>, or <code>null</code> if
1970     *          <code>parent</code> is not currently expanded
1971     */
1972    public Enumeration<TreePath> getExpandedDescendants(TreePath parent) {
1973        if(!isExpanded(parent))
1974            return null;
1975
1976        Enumeration<TreePath> toggledPaths = expandedState.keys();
1977        Vector<TreePath> elements = null;
1978        TreePath          path;
1979        Object            value;
1980
1981        if(toggledPaths != null) {
1982            while(toggledPaths.hasMoreElements()) {
1983                path = toggledPaths.nextElement();
1984                value = expandedState.get(path);
1985                // Add the path if it is expanded, a descendant of parent,
1986                // and it is visible (all parents expanded). This is rather
1987                // expensive!
1988                if(path != parent && value != null &&
1989                   ((Boolean)value).booleanValue() &&
1990                   parent.isDescendant(path) && isVisible(path)) {
1991                    if (elements == null) {
1992                        elements = new Vector<TreePath>();
1993                    }
1994                    elements.addElement(path);
1995                }
1996            }
1997        }
1998        if (elements == null) {
1999            Set<TreePath> empty = Collections.emptySet();
2000            return Collections.enumeration(empty);
2001        }
2002        return elements.elements();
2003    }
2004
2005    /**
2006     * Returns true if the node identified by the path has ever been
2007     * expanded.
2008     *
2009     * @param path a {@code TreePath} identifying a node
2010     * @return true if the <code>path</code> has ever been expanded
2011     */
2012    public boolean hasBeenExpanded(TreePath path) {
2013        return (path != null && expandedState.get(path) != null);
2014    }
2015
2016    /**
2017     * Returns true if the node identified by the path is currently expanded,
2018     *
2019     * @param path  the <code>TreePath</code> specifying the node to check
2020     * @return false if any of the nodes in the node's path are collapsed,
2021     *               true if all nodes in the path are expanded
2022     */
2023    public boolean isExpanded(TreePath path) {
2024
2025        if(path == null)
2026            return false;
2027        Object  value;
2028
2029        do{
2030            value = expandedState.get(path);
2031            if(value == null || !((Boolean)value).booleanValue())
2032                return false;
2033        } while( (path=path.getParentPath())!=null );
2034
2035        return true;
2036    }
2037
2038    /**
2039     * Returns true if the node at the specified display row is currently
2040     * expanded.
2041     *
2042     * @param row  the row to check, where 0 is the first row in the
2043     *             display
2044     * @return true if the node is currently expanded, otherwise false
2045     */
2046    public boolean isExpanded(int row) {
2047        TreeUI                  tree = getUI();
2048
2049        if(tree != null) {
2050            TreePath         path = tree.getPathForRow(this, row);
2051
2052            if(path != null) {
2053                Boolean value = expandedState.get(path);
2054
2055                return (value != null && value.booleanValue());
2056            }
2057        }
2058        return false;
2059    }
2060
2061    /**
2062     * Returns true if the value identified by path is currently collapsed,
2063     * this will return false if any of the values in path are currently
2064     * not being displayed.
2065     *
2066     * @param path  the <code>TreePath</code> to check
2067     * @return true if any of the nodes in the node's path are collapsed,
2068     *               false if all nodes in the path are expanded
2069     */
2070    public boolean isCollapsed(TreePath path) {
2071        return !isExpanded(path);
2072    }
2073
2074    /**
2075     * Returns true if the node at the specified display row is collapsed.
2076     *
2077     * @param row  the row to check, where 0 is the first row in the
2078     *             display
2079     * @return true if the node is currently collapsed, otherwise false
2080     */
2081    public boolean isCollapsed(int row) {
2082        return !isExpanded(row);
2083    }
2084
2085    /**
2086     * Ensures that the node identified by path is currently viewable.
2087     *
2088     * @param path  the <code>TreePath</code> to make visible
2089     */
2090    public void makeVisible(TreePath path) {
2091        if(path != null) {
2092            TreePath        parentPath = path.getParentPath();
2093
2094            if(parentPath != null) {
2095                expandPath(parentPath);
2096            }
2097        }
2098    }
2099
2100    /**
2101     * Returns true if the value identified by path is currently viewable,
2102     * which means it is either the root or all of its parents are expanded.
2103     * Otherwise, this method returns false.
2104     *
2105     * @param path {@code TreePath} identifying a node
2106     * @return true if the node is viewable, otherwise false
2107     */
2108    public boolean isVisible(TreePath path) {
2109        if(path != null) {
2110            TreePath        parentPath = path.getParentPath();
2111
2112            if(parentPath != null)
2113                return isExpanded(parentPath);
2114            // Root.
2115            return true;
2116        }
2117        return false;
2118    }
2119
2120    /**
2121     * Returns the <code>Rectangle</code> that the specified node will be drawn
2122     * into. Returns <code>null</code> if any component in the path is hidden
2123     * (under a collapsed parent).
2124     * <p>
2125     * Note:<br>
2126     * This method returns a valid rectangle, even if the specified
2127     * node is not currently displayed.
2128     *
2129     * @param path the <code>TreePath</code> identifying the node
2130     * @return the <code>Rectangle</code> the node is drawn in,
2131     *          or <code>null</code>
2132     */
2133    public Rectangle getPathBounds(TreePath path) {
2134        TreeUI                   tree = getUI();
2135
2136        if(tree != null)
2137            return tree.getPathBounds(this, path);
2138        return null;
2139    }
2140
2141    /**
2142     * Returns the <code>Rectangle</code> that the node at the specified row is
2143     * drawn in.
2144     *
2145     * @param row  the row to be drawn, where 0 is the first row in the
2146     *             display
2147     * @return the <code>Rectangle</code> the node is drawn in
2148     */
2149    public Rectangle getRowBounds(int row) {
2150        return getPathBounds(getPathForRow(row));
2151    }
2152
2153    /**
2154     * Makes sure all the path components in path are expanded (except
2155     * for the last path component) and scrolls so that the
2156     * node identified by the path is displayed. Only works when this
2157     * <code>JTree</code> is contained in a <code>JScrollPane</code>.
2158     *
2159     * @param path  the <code>TreePath</code> identifying the node to
2160     *          bring into view
2161     */
2162    public void scrollPathToVisible(TreePath path) {
2163        if(path != null) {
2164            makeVisible(path);
2165
2166            Rectangle          bounds = getPathBounds(path);
2167
2168            if(bounds != null) {
2169                scrollRectToVisible(bounds);
2170                if (accessibleContext != null) {
2171                    ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
2172                }
2173            }
2174        }
2175    }
2176
2177    /**
2178     * Scrolls the item identified by row until it is displayed. The minimum
2179     * of amount of scrolling necessary to bring the row into view
2180     * is performed. Only works when this <code>JTree</code> is contained in a
2181     * <code>JScrollPane</code>.
2182     *
2183     * @param row  an integer specifying the row to scroll, where 0 is the
2184     *             first row in the display
2185     */
2186    public void scrollRowToVisible(int row) {
2187        scrollPathToVisible(getPathForRow(row));
2188    }
2189
2190    /**
2191     * Returns the path for the specified row.  If <code>row</code> is
2192     * not visible, or a {@code TreeUI} has not been set, <code>null</code>
2193     * is returned.
2194     *
2195     * @param row  an integer specifying a row
2196     * @return the <code>TreePath</code> to the specified node,
2197     *          <code>null</code> if <code>row &lt; 0</code>
2198     *          or <code>row &gt;= getRowCount()</code>
2199     */
2200    @BeanProperty(bound = false)
2201    public TreePath getPathForRow(int row) {
2202        TreeUI                  tree = getUI();
2203
2204        if(tree != null)
2205            return tree.getPathForRow(this, row);
2206        return null;
2207    }
2208
2209    /**
2210     * Returns the row that displays the node identified by the specified
2211     * path.
2212     *
2213     * @param path  the <code>TreePath</code> identifying a node
2214     * @return an integer specifying the display row, where 0 is the first
2215     *         row in the display, or -1 if any of the elements in path
2216     *         are hidden under a collapsed parent.
2217     */
2218    public int getRowForPath(TreePath path) {
2219        TreeUI                  tree = getUI();
2220
2221        if(tree != null)
2222            return tree.getRowForPath(this, path);
2223        return -1;
2224    }
2225
2226    /**
2227     * Ensures that the node identified by the specified path is
2228     * expanded and viewable. If the last item in the path is a
2229     * leaf, this will have no effect.
2230     *
2231     * @param path  the <code>TreePath</code> identifying a node
2232     */
2233    public void expandPath(TreePath path) {
2234        // Only expand if not leaf!
2235        TreeModel          model = getModel();
2236
2237        if(path != null && model != null &&
2238           !model.isLeaf(path.getLastPathComponent())) {
2239            setExpandedState(path, true);
2240        }
2241    }
2242
2243    /**
2244     * Ensures that the node in the specified row is expanded and
2245     * viewable.
2246     * <p>
2247     * If <code>row</code> is &lt; 0 or &gt;= <code>getRowCount</code> this
2248     * will have no effect.
2249     *
2250     * @param row  an integer specifying a display row, where 0 is the
2251     *             first row in the display
2252     */
2253    public void expandRow(int row) {
2254        expandPath(getPathForRow(row));
2255    }
2256
2257    /**
2258     * Ensures that the node identified by the specified path is
2259     * collapsed and viewable.
2260     *
2261     * @param path  the <code>TreePath</code> identifying a node
2262      */
2263    public void collapsePath(TreePath path) {
2264        setExpandedState(path, false);
2265    }
2266
2267    /**
2268     * Ensures that the node in the specified row is collapsed.
2269     * <p>
2270     * If <code>row</code> is &lt; 0 or &gt;= <code>getRowCount</code> this
2271     * will have no effect.
2272     *
2273     * @param row  an integer specifying a display row, where 0 is the
2274     *             first row in the display
2275      */
2276    public void collapseRow(int row) {
2277        collapsePath(getPathForRow(row));
2278    }
2279
2280    /**
2281     * Returns the path for the node at the specified location.
2282     *
2283     * @param x an integer giving the number of pixels horizontally from
2284     *          the left edge of the display area, minus any left margin
2285     * @param y an integer giving the number of pixels vertically from
2286     *          the top of the display area, minus any top margin
2287     * @return  the <code>TreePath</code> for the node at that location
2288     */
2289    public TreePath getPathForLocation(int x, int y) {
2290        TreePath          closestPath = getClosestPathForLocation(x, y);
2291
2292        if(closestPath != null) {
2293            Rectangle       pathBounds = getPathBounds(closestPath);
2294
2295            if(pathBounds != null &&
2296               x >= pathBounds.x && x < (pathBounds.x + pathBounds.width) &&
2297               y >= pathBounds.y && y < (pathBounds.y + pathBounds.height))
2298                return closestPath;
2299        }
2300        return null;
2301    }
2302
2303    /**
2304     * Returns the row for the specified location.
2305     *
2306     * @param x an integer giving the number of pixels horizontally from
2307     *          the left edge of the display area, minus any left margin
2308     * @param y an integer giving the number of pixels vertically from
2309     *          the top of the display area, minus any top margin
2310     * @return the row corresponding to the location, or -1 if the
2311     *         location is not within the bounds of a displayed cell
2312     * @see #getClosestRowForLocation
2313     */
2314    public int getRowForLocation(int x, int y) {
2315        return getRowForPath(getPathForLocation(x, y));
2316    }
2317
2318    /**
2319     * Returns the path to the node that is closest to x,y.  If
2320     * no nodes are currently viewable, or there is no model, returns
2321     * <code>null</code>, otherwise it always returns a valid path.  To test if
2322     * the node is exactly at x, y, get the node's bounds and
2323     * test x, y against that.
2324     *
2325     * @param x an integer giving the number of pixels horizontally from
2326     *          the left edge of the display area, minus any left margin
2327     * @param y an integer giving the number of pixels vertically from
2328     *          the top of the display area, minus any top margin
2329     * @return  the <code>TreePath</code> for the node closest to that location,
2330     *          <code>null</code> if nothing is viewable or there is no model
2331     *
2332     * @see #getPathForLocation
2333     * @see #getPathBounds
2334     */
2335    public TreePath getClosestPathForLocation(int x, int y) {
2336        TreeUI                  tree = getUI();
2337
2338        if(tree != null)
2339            return tree.getClosestPathForLocation(this, x, y);
2340        return null;
2341    }
2342
2343    /**
2344     * Returns the row to the node that is closest to x,y.  If no nodes
2345     * are viewable or there is no model, returns -1. Otherwise,
2346     * it always returns a valid row.  To test if the returned object is
2347     * exactly at x, y, get the bounds for the node at the returned
2348     * row and test x, y against that.
2349     *
2350     * @param x an integer giving the number of pixels horizontally from
2351     *          the left edge of the display area, minus any left margin
2352     * @param y an integer giving the number of pixels vertically from
2353     *          the top of the display area, minus any top margin
2354     * @return the row closest to the location, -1 if nothing is
2355     *         viewable or there is no model
2356     *
2357     * @see #getRowForLocation
2358     * @see #getRowBounds
2359     */
2360    public int getClosestRowForLocation(int x, int y) {
2361        return getRowForPath(getClosestPathForLocation(x, y));
2362    }
2363
2364    /**
2365     * Returns true if the tree is being edited. The item that is being
2366     * edited can be obtained using <code>getSelectionPath</code>.
2367     *
2368     * @return true if the user is currently editing a node
2369     * @see #getSelectionPath
2370     */
2371    @BeanProperty(bound = false)
2372    public boolean isEditing() {
2373        TreeUI                  tree = getUI();
2374
2375        if(tree != null)
2376            return tree.isEditing(this);
2377        return false;
2378    }
2379
2380    /**
2381     * Ends the current editing session.
2382     * (The <code>DefaultTreeCellEditor</code>
2383     * object saves any edits that are currently in progress on a cell.
2384     * Other implementations may operate differently.)
2385     * Has no effect if the tree isn't being edited.
2386     * <blockquote>
2387     * <b>Note:</b><br>
2388     * To make edit-saves automatic whenever the user changes
2389     * their position in the tree, use {@link #setInvokesStopCellEditing}.
2390     * </blockquote>
2391     *
2392     * @return true if editing was in progress and is now stopped,
2393     *              false if editing was not in progress
2394     */
2395    public boolean stopEditing() {
2396        TreeUI                  tree = getUI();
2397
2398        if(tree != null)
2399            return tree.stopEditing(this);
2400        return false;
2401    }
2402
2403    /**
2404     * Cancels the current editing session. Has no effect if the
2405     * tree isn't being edited.
2406     */
2407    public void  cancelEditing() {
2408        TreeUI                  tree = getUI();
2409
2410        if(tree != null)
2411            tree.cancelEditing(this);
2412    }
2413
2414    /**
2415     * Selects the node identified by the specified path and initiates
2416     * editing.  The edit-attempt fails if the <code>CellEditor</code>
2417     * does not allow
2418     * editing for the specified item.
2419     *
2420     * @param path  the <code>TreePath</code> identifying a node
2421     */
2422    public void startEditingAtPath(TreePath path) {
2423        TreeUI                  tree = getUI();
2424
2425        if(tree != null)
2426            tree.startEditingAtPath(this, path);
2427    }
2428
2429    /**
2430     * Returns the path to the element that is currently being edited.
2431     *
2432     * @return  the <code>TreePath</code> for the node being edited
2433     */
2434    @BeanProperty(bound = false)
2435    public TreePath getEditingPath() {
2436        TreeUI                  tree = getUI();
2437
2438        if(tree != null)
2439            return tree.getEditingPath(this);
2440        return null;
2441    }
2442
2443    //
2444    // Following are primarily convenience methods for mapping from
2445    // row based selections to path selections.  Sometimes it is
2446    // easier to deal with these than paths (mouse downs, key downs
2447    // usually just deal with index based selections).
2448    // Since row based selections require a UI many of these won't work
2449    // without one.
2450    //
2451
2452    /**
2453     * Sets the tree's selection model. When a <code>null</code> value is
2454     * specified an empty
2455     * <code>selectionModel</code> is used, which does not allow selections.
2456     * <p>
2457     * This is a bound property.
2458     *
2459     * @param selectionModel the <code>TreeSelectionModel</code> to use,
2460     *          or <code>null</code> to disable selections
2461     * @see TreeSelectionModel
2462     */
2463    @BeanProperty(description
2464            = "The tree's selection model.")
2465    public void setSelectionModel(TreeSelectionModel selectionModel) {
2466        if(selectionModel == null)
2467            selectionModel = EmptySelectionModel.sharedInstance();
2468
2469        TreeSelectionModel         oldValue = this.selectionModel;
2470
2471        if (this.selectionModel != null && selectionRedirector != null) {
2472            this.selectionModel.removeTreeSelectionListener
2473                                (selectionRedirector);
2474        }
2475        if (accessibleContext != null) {
2476           this.selectionModel.removeTreeSelectionListener((TreeSelectionListener)accessibleContext);
2477           selectionModel.addTreeSelectionListener((TreeSelectionListener)accessibleContext);
2478        }
2479
2480        this.selectionModel = selectionModel;
2481        if (selectionRedirector != null) {
2482            this.selectionModel.addTreeSelectionListener(selectionRedirector);
2483        }
2484        firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue,
2485                           this.selectionModel);
2486
2487        if (accessibleContext != null) {
2488            accessibleContext.firePropertyChange(
2489                    AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
2490                    Boolean.valueOf(false), Boolean.valueOf(true));
2491        }
2492    }
2493
2494    /**
2495     * Returns the model for selections. This should always return a
2496     * non-<code>null</code> value. If you don't want to allow anything
2497     * to be selected
2498     * set the selection model to <code>null</code>, which forces an empty
2499     * selection model to be used.
2500     *
2501     * @return the model for selections
2502     * @see #setSelectionModel
2503     */
2504    public TreeSelectionModel getSelectionModel() {
2505        return selectionModel;
2506    }
2507
2508    /**
2509     * Returns the paths (inclusive) between the specified rows. If
2510     * the specified indices are within the viewable set of rows, or
2511     * bound the viewable set of rows, then the indices are
2512     * constrained by the viewable set of rows. If the specified
2513     * indices are not within the viewable set of rows, or do not
2514     * bound the viewable set of rows, then an empty array is
2515     * returned. For example, if the row count is {@code 10}, and this
2516     * method is invoked with {@code -1, 20}, then the specified
2517     * indices are constrained to the viewable set of rows, and this is
2518     * treated as if invoked with {@code 0, 9}. On the other hand, if
2519     * this were invoked with {@code -10, -1}, then the specified
2520     * indices do not bound the viewable set of rows, and an empty
2521     * array is returned.
2522     * <p>
2523     * The parameters are not order dependent. That is, {@code
2524     * getPathBetweenRows(x, y)} is equivalent to
2525     * {@code getPathBetweenRows(y, x)}.
2526     * <p>
2527     * An empty array is returned if the row count is {@code 0}, or
2528     * the specified indices do not bound the viewable set of rows.
2529     *
2530     * @param index0 the first index in the range
2531     * @param index1 the last index in the range
2532     * @return the paths (inclusive) between the specified row indices
2533     */
2534    protected TreePath[] getPathBetweenRows(int index0, int index1) {
2535        TreeUI           tree = getUI();
2536        if (tree != null) {
2537            int rowCount = getRowCount();
2538            if (rowCount > 0 && !((index0 < 0 && index1 < 0) ||
2539                                  (index0 >= rowCount && index1 >= rowCount))){
2540                index0 = Math.min(rowCount - 1, Math.max(index0, 0));
2541                index1 = Math.min(rowCount - 1, Math.max(index1, 0));
2542                int minIndex = Math.min(index0, index1);
2543                int maxIndex = Math.max(index0, index1);
2544                TreePath[] selection = new TreePath[
2545                        maxIndex - minIndex + 1];
2546                for(int counter = minIndex; counter <= maxIndex; counter++) {
2547                    selection[counter - minIndex] =
2548                            tree.getPathForRow(this, counter);
2549                }
2550                return selection;
2551            }
2552        }
2553        return new TreePath[0];
2554    }
2555
2556    /**
2557     * Selects the rows in the specified interval (inclusive). If
2558     * the specified indices are within the viewable set of rows, or bound
2559     * the viewable set of rows, then the specified rows are constrained by
2560     * the viewable set of rows. If the specified indices are not within the
2561     * viewable set of rows, or do not bound the viewable set of rows, then
2562     * the selection is cleared. For example, if the row count is {@code
2563     * 10}, and this method is invoked with {@code -1, 20}, then the
2564     * specified indices bounds the viewable range, and this is treated as
2565     * if invoked with {@code 0, 9}. On the other hand, if this were
2566     * invoked with {@code -10, -1}, then the specified indices do not
2567     * bound the viewable set of rows, and the selection is cleared.
2568     * <p>
2569     * The parameters are not order dependent. That is, {@code
2570     * setSelectionInterval(x, y)} is equivalent to
2571     * {@code setSelectionInterval(y, x)}.
2572     *
2573     * @param index0 the first index in the range to select
2574     * @param index1 the last index in the range to select
2575    */
2576    public void setSelectionInterval(int index0, int index1) {
2577        TreePath[]         paths = getPathBetweenRows(index0, index1);
2578
2579        this.getSelectionModel().setSelectionPaths(paths);
2580    }
2581
2582    /**
2583     * Adds the specified rows (inclusive) to the selection. If the
2584     * specified indices are within the viewable set of rows, or bound
2585     * the viewable set of rows, then the specified indices are
2586     * constrained by the viewable set of rows. If the indices are not
2587     * within the viewable set of rows, or do not bound the viewable
2588     * set of rows, then the selection is unchanged. For example, if
2589     * the row count is {@code 10}, and this method is invoked with
2590     * {@code -1, 20}, then the specified indices bounds the viewable
2591     * range, and this is treated as if invoked with {@code 0, 9}. On
2592     * the other hand, if this were invoked with {@code -10, -1}, then
2593     * the specified indices do not bound the viewable set of rows,
2594     * and the selection is unchanged.
2595     * <p>
2596     * The parameters are not order dependent. That is, {@code
2597     * addSelectionInterval(x, y)} is equivalent to
2598     * {@code addSelectionInterval(y, x)}.
2599     *
2600     * @param index0 the first index in the range to add to the selection
2601     * @param index1 the last index in the range to add to the selection
2602     */
2603    public void addSelectionInterval(int index0, int index1) {
2604        TreePath[]         paths = getPathBetweenRows(index0, index1);
2605
2606        if (paths != null && paths.length > 0) {
2607            this.getSelectionModel().addSelectionPaths(paths);
2608        }
2609    }
2610
2611    /**
2612     * Removes the specified rows (inclusive) from the selection. If
2613     * the specified indices are within the viewable set of rows, or bound
2614     * the viewable set of rows, then the specified indices are constrained by
2615     * the viewable set of rows. If the specified indices are not within the
2616     * viewable set of rows, or do not bound the viewable set of rows, then
2617     * the selection is unchanged. For example, if the row count is {@code
2618     * 10}, and this method is invoked with {@code -1, 20}, then the
2619     * specified range bounds the viewable range, and this is treated as
2620     * if invoked with {@code 0, 9}. On the other hand, if this were
2621     * invoked with {@code -10, -1}, then the specified range does not
2622     * bound the viewable set of rows, and the selection is unchanged.
2623     * <p>
2624     * The parameters are not order dependent. That is, {@code
2625     * removeSelectionInterval(x, y)} is equivalent to
2626     * {@code removeSelectionInterval(y, x)}.
2627     *
2628     * @param index0 the first row to remove from the selection
2629     * @param index1 the last row to remove from the selection
2630     */
2631    public void removeSelectionInterval(int index0, int index1) {
2632        TreePath[]         paths = getPathBetweenRows(index0, index1);
2633
2634        if (paths != null && paths.length > 0) {
2635            this.getSelectionModel().removeSelectionPaths(paths);
2636        }
2637    }
2638
2639    /**
2640     * Removes the node identified by the specified path from the current
2641     * selection.
2642     *
2643     * @param path  the <code>TreePath</code> identifying a node
2644     */
2645    public void removeSelectionPath(TreePath path) {
2646        this.getSelectionModel().removeSelectionPath(path);
2647    }
2648
2649    /**
2650     * Removes the nodes identified by the specified paths from the
2651     * current selection.
2652     *
2653     * @param paths an array of <code>TreePath</code> objects that
2654     *              specifies the nodes to remove
2655     */
2656    public void removeSelectionPaths(TreePath[] paths) {
2657        this.getSelectionModel().removeSelectionPaths(paths);
2658    }
2659
2660    /**
2661     * Removes the row at the index <code>row</code> from the current
2662     * selection.
2663     *
2664     * @param row  the row to remove
2665     */
2666    public void removeSelectionRow(int row) {
2667        int[]             rows = { row };
2668
2669        removeSelectionRows(rows);
2670    }
2671
2672    /**
2673     * Removes the rows that are selected at each of the specified
2674     * rows.
2675     *
2676     * @param rows  an array of ints specifying display rows, where 0 is
2677     *             the first row in the display
2678     */
2679    public void removeSelectionRows(int[] rows) {
2680        TreeUI             ui = getUI();
2681
2682        if(ui != null && rows != null) {
2683            int                  numRows = rows.length;
2684            TreePath[]           paths = new TreePath[numRows];
2685
2686            for(int counter = 0; counter < numRows; counter++)
2687                paths[counter] = ui.getPathForRow(this, rows[counter]);
2688            removeSelectionPaths(paths);
2689        }
2690    }
2691
2692    /**
2693     * Clears the selection.
2694     */
2695    public void clearSelection() {
2696        getSelectionModel().clearSelection();
2697    }
2698
2699    /**
2700     * Returns true if the selection is currently empty.
2701     *
2702     * @return true if the selection is currently empty
2703     */
2704    @BeanProperty(bound = false)
2705    public boolean isSelectionEmpty() {
2706        return getSelectionModel().isSelectionEmpty();
2707    }
2708
2709    /**
2710     * Adds a listener for <code>TreeExpansion</code> events.
2711     *
2712     * @param tel a TreeExpansionListener that will be notified when
2713     *            a tree node is expanded or collapsed (a "negative
2714     *            expansion")
2715     */
2716    public void addTreeExpansionListener(TreeExpansionListener tel) {
2717        if (settingUI) {
2718            uiTreeExpansionListener = tel;
2719        }
2720        listenerList.add(TreeExpansionListener.class, tel);
2721    }
2722
2723    /**
2724     * Removes a listener for <code>TreeExpansion</code> events.
2725     *
2726     * @param tel the <code>TreeExpansionListener</code> to remove
2727     */
2728    public void removeTreeExpansionListener(TreeExpansionListener tel) {
2729        listenerList.remove(TreeExpansionListener.class, tel);
2730        if (uiTreeExpansionListener == tel) {
2731            uiTreeExpansionListener = null;
2732        }
2733    }
2734
2735    /**
2736     * Returns an array of all the <code>TreeExpansionListener</code>s added
2737     * to this JTree with addTreeExpansionListener().
2738     *
2739     * @return all of the <code>TreeExpansionListener</code>s added or an empty
2740     *         array if no listeners have been added
2741     * @since 1.4
2742     */
2743    @BeanProperty(bound = false)
2744    public TreeExpansionListener[] getTreeExpansionListeners() {
2745        return listenerList.getListeners(TreeExpansionListener.class);
2746    }
2747
2748    /**
2749     * Adds a listener for <code>TreeWillExpand</code> events.
2750     *
2751     * @param tel a <code>TreeWillExpandListener</code> that will be notified
2752     *            when a tree node will be expanded or collapsed (a "negative
2753     *            expansion")
2754     */
2755    public void addTreeWillExpandListener(TreeWillExpandListener tel) {
2756        listenerList.add(TreeWillExpandListener.class, tel);
2757    }
2758
2759    /**
2760     * Removes a listener for <code>TreeWillExpand</code> events.
2761     *
2762     * @param tel the <code>TreeWillExpandListener</code> to remove
2763     */
2764    public void removeTreeWillExpandListener(TreeWillExpandListener tel) {
2765        listenerList.remove(TreeWillExpandListener.class, tel);
2766    }
2767
2768    /**
2769     * Returns an array of all the <code>TreeWillExpandListener</code>s added
2770     * to this JTree with addTreeWillExpandListener().
2771     *
2772     * @return all of the <code>TreeWillExpandListener</code>s added or an empty
2773     *         array if no listeners have been added
2774     * @since 1.4
2775     */
2776    @BeanProperty(bound = false)
2777    public TreeWillExpandListener[] getTreeWillExpandListeners() {
2778        return listenerList.getListeners(TreeWillExpandListener.class);
2779    }
2780
2781    /**
2782     * Notifies all listeners that have registered interest for
2783     * notification on this event type.  The event instance
2784     * is lazily created using the <code>path</code> parameter.
2785     *
2786     * @param path the <code>TreePath</code> indicating the node that was
2787     *          expanded
2788     * @see EventListenerList
2789     */
2790     public void fireTreeExpanded(TreePath path) {
2791        // Guaranteed to return a non-null array
2792        Object[] listeners = listenerList.getListenerList();
2793        TreeExpansionEvent e = null;
2794        if (uiTreeExpansionListener != null) {
2795            e = new TreeExpansionEvent(this, path);
2796            uiTreeExpansionListener.treeExpanded(e);
2797        }
2798        // Process the listeners last to first, notifying
2799        // those that are interested in this event
2800        for (int i = listeners.length-2; i>=0; i-=2) {
2801            if (listeners[i]==TreeExpansionListener.class &&
2802                listeners[i + 1] != uiTreeExpansionListener) {
2803                // Lazily create the event:
2804                if (e == null)
2805                    e = new TreeExpansionEvent(this, path);
2806                ((TreeExpansionListener)listeners[i+1]).
2807                    treeExpanded(e);
2808            }
2809        }
2810    }
2811
2812    /**
2813     * Notifies all listeners that have registered interest for
2814     * notification on this event type.  The event instance
2815     * is lazily created using the <code>path</code> parameter.
2816     *
2817     * @param path the <code>TreePath</code> indicating the node that was
2818     *          collapsed
2819     * @see EventListenerList
2820     */
2821    public void fireTreeCollapsed(TreePath path) {
2822        // Guaranteed to return a non-null array
2823        Object[] listeners = listenerList.getListenerList();
2824        TreeExpansionEvent e = null;
2825        if (uiTreeExpansionListener != null) {
2826            e = new TreeExpansionEvent(this, path);
2827            uiTreeExpansionListener.treeCollapsed(e);
2828        }
2829        // Process the listeners last to first, notifying
2830        // those that are interested in this event
2831        for (int i = listeners.length-2; i>=0; i-=2) {
2832            if (listeners[i]==TreeExpansionListener.class &&
2833                listeners[i + 1] != uiTreeExpansionListener) {
2834                // Lazily create the event:
2835                if (e == null)
2836                    e = new TreeExpansionEvent(this, path);
2837                ((TreeExpansionListener)listeners[i+1]).
2838                    treeCollapsed(e);
2839            }
2840        }
2841    }
2842
2843    /**
2844     * Notifies all listeners that have registered interest for
2845     * notification on this event type.  The event instance
2846     * is lazily created using the <code>path</code> parameter.
2847     *
2848     * @param path the <code>TreePath</code> indicating the node that was
2849     *          expanded
2850     * @throws ExpandVetoException if the expansion is prevented from occurring
2851     * @see EventListenerList
2852     */
2853     public void fireTreeWillExpand(TreePath path) throws ExpandVetoException {
2854        // Guaranteed to return a non-null array
2855        Object[] listeners = listenerList.getListenerList();
2856        TreeExpansionEvent e = null;
2857        // Process the listeners last to first, notifying
2858        // those that are interested in this event
2859        for (int i = listeners.length-2; i>=0; i-=2) {
2860            if (listeners[i]==TreeWillExpandListener.class) {
2861                // Lazily create the event:
2862                if (e == null)
2863                    e = new TreeExpansionEvent(this, path);
2864                ((TreeWillExpandListener)listeners[i+1]).
2865                    treeWillExpand(e);
2866            }
2867        }
2868    }
2869
2870    /**
2871     * Notifies all listeners that have registered interest for
2872     * notification on this event type.  The event instance
2873     * is lazily created using the <code>path</code> parameter.
2874     *
2875     * @param path the <code>TreePath</code> indicating the node that was
2876     *          expanded
2877     * @throws ExpandVetoException if the collapse is prevented from occurring
2878     * @see EventListenerList
2879     */
2880     public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException {
2881        // Guaranteed to return a non-null array
2882        Object[] listeners = listenerList.getListenerList();
2883        TreeExpansionEvent e = null;
2884        // Process the listeners last to first, notifying
2885        // those that are interested in this event
2886        for (int i = listeners.length-2; i>=0; i-=2) {
2887            if (listeners[i]==TreeWillExpandListener.class) {
2888                // Lazily create the event:
2889                if (e == null)
2890                    e = new TreeExpansionEvent(this, path);
2891                ((TreeWillExpandListener)listeners[i+1]).
2892                    treeWillCollapse(e);
2893            }
2894        }
2895    }
2896
2897    /**
2898     * Adds a listener for <code>TreeSelection</code> events.
2899     *
2900     * @param tsl the <code>TreeSelectionListener</code> that will be notified
2901     *            when a node is selected or deselected (a "negative
2902     *            selection")
2903     */
2904    public void addTreeSelectionListener(TreeSelectionListener tsl) {
2905        listenerList.add(TreeSelectionListener.class,tsl);
2906        if(listenerList.getListenerCount(TreeSelectionListener.class) != 0
2907           && selectionRedirector == null) {
2908            selectionRedirector = new TreeSelectionRedirector();
2909            selectionModel.addTreeSelectionListener(selectionRedirector);
2910        }
2911    }
2912
2913    /**
2914     * Removes a <code>TreeSelection</code> listener.
2915     *
2916     * @param tsl the <code>TreeSelectionListener</code> to remove
2917     */
2918    public void removeTreeSelectionListener(TreeSelectionListener tsl) {
2919        listenerList.remove(TreeSelectionListener.class,tsl);
2920        if(listenerList.getListenerCount(TreeSelectionListener.class) == 0
2921           && selectionRedirector != null) {
2922            selectionModel.removeTreeSelectionListener
2923                (selectionRedirector);
2924            selectionRedirector = null;
2925        }
2926    }
2927
2928    /**
2929     * Returns an array of all the <code>TreeSelectionListener</code>s added
2930     * to this JTree with addTreeSelectionListener().
2931     *
2932     * @return all of the <code>TreeSelectionListener</code>s added or an empty
2933     *         array if no listeners have been added
2934     * @since 1.4
2935     */
2936    @BeanProperty(bound = false)
2937    public TreeSelectionListener[] getTreeSelectionListeners() {
2938        return listenerList.getListeners(TreeSelectionListener.class);
2939    }
2940
2941    /**
2942     * Notifies all listeners that have registered interest for
2943     * notification on this event type.
2944     *
2945     * @param e the <code>TreeSelectionEvent</code> to be fired;
2946     *          generated by the
2947     *          <code>TreeSelectionModel</code>
2948     *          when a node is selected or deselected
2949     * @see EventListenerList
2950     */
2951    protected void fireValueChanged(TreeSelectionEvent e) {
2952        // Guaranteed to return a non-null array
2953        Object[] listeners = listenerList.getListenerList();
2954        // Process the listeners last to first, notifying
2955        // those that are interested in this event
2956        for (int i = listeners.length-2; i>=0; i-=2) {
2957            // TreeSelectionEvent e = null;
2958            if (listeners[i]==TreeSelectionListener.class) {
2959                // Lazily create the event:
2960                // if (e == null)
2961                // e = new ListSelectionEvent(this, firstIndex, lastIndex);
2962                ((TreeSelectionListener)listeners[i+1]).valueChanged(e);
2963            }
2964        }
2965    }
2966
2967    /**
2968     * Sent when the tree has changed enough that we need to resize
2969     * the bounds, but not enough that we need to remove the
2970     * expanded node set (e.g nodes were expanded or collapsed, or
2971     * nodes were inserted into the tree). You should never have to
2972     * invoke this, the UI will invoke this as it needs to.
2973     */
2974    public void treeDidChange() {
2975        revalidate();
2976        repaint();
2977    }
2978
2979    /**
2980     * Sets the number of rows that are to be displayed.
2981     * This will only work if the tree is contained in a
2982     * <code>JScrollPane</code>,
2983     * and will adjust the preferred size and size of that scrollpane.
2984     * <p>
2985     * This is a bound property.
2986     *
2987     * @param newCount the number of rows to display
2988     */
2989    @BeanProperty(description
2990            = "The number of rows that are to be displayed.")
2991    public void setVisibleRowCount(int newCount) {
2992        int                 oldCount = visibleRowCount;
2993
2994        visibleRowCount = newCount;
2995        firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldCount,
2996                           visibleRowCount);
2997        invalidate();
2998        if (accessibleContext != null) {
2999            ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
3000        }
3001    }
3002
3003    /**
3004     * Returns the number of rows that are displayed in the display area.
3005     *
3006     * @return the number of rows displayed
3007     */
3008    public int getVisibleRowCount() {
3009        return visibleRowCount;
3010    }
3011
3012    /**
3013     * Expands the root path, assuming the current TreeModel has been set.
3014     */
3015    private void expandRoot() {
3016        TreeModel   model = getModel();
3017        if(model != null && model.getRoot() != null) {
3018            expandPath(new TreePath(model.getRoot()));
3019        }
3020    }
3021
3022    /**
3023     * Returns the TreePath to the next tree element that
3024     * begins with a prefix. To handle the conversion of a
3025     * <code>TreePath</code> into a String, <code>convertValueToText</code>
3026     * is used.
3027     *
3028     * @param prefix the string to test for a match
3029     * @param startingRow the row for starting the search
3030     * @param bias the search direction, either
3031     * Position.Bias.Forward or Position.Bias.Backward.
3032     * @return the TreePath of the next tree element that
3033     * starts with the prefix; otherwise null
3034     * @exception IllegalArgumentException if prefix is null
3035     * or startingRow is out of bounds
3036     * @since 1.4
3037     */
3038    public TreePath getNextMatch(String prefix, int startingRow,
3039                                 Position.Bias bias) {
3040
3041        int max = getRowCount();
3042        if (prefix == null) {
3043            throw new IllegalArgumentException();
3044        }
3045        if (startingRow < 0 || startingRow >= max) {
3046            throw new IllegalArgumentException();
3047        }
3048        prefix = prefix.toUpperCase();
3049
3050        // start search from the next/previous element froom the
3051        // selected element
3052        int increment = (bias == Position.Bias.Forward) ? 1 : -1;
3053        int row = startingRow;
3054        do {
3055            TreePath path = getPathForRow(row);
3056            String text = convertValueToText(
3057                path.getLastPathComponent(), isRowSelected(row),
3058                isExpanded(row), true, row, false);
3059
3060            if (text.toUpperCase().startsWith(prefix)) {
3061                return path;
3062            }
3063            row = (row + increment + max) % max;
3064        } while (row != startingRow);
3065        return null;
3066    }
3067
3068    // Serialization support.
3069    private void writeObject(ObjectOutputStream s) throws IOException {
3070        Vector<Object> values = new Vector<Object>();
3071
3072        s.defaultWriteObject();
3073        // Save the cellRenderer, if its Serializable.
3074        if(cellRenderer != null && cellRenderer instanceof Serializable) {
3075            values.addElement("cellRenderer");
3076            values.addElement(cellRenderer);
3077        }
3078        // Save the cellEditor, if its Serializable.
3079        if(cellEditor != null && cellEditor instanceof Serializable) {
3080            values.addElement("cellEditor");
3081            values.addElement(cellEditor);
3082        }
3083        // Save the treeModel, if its Serializable.
3084        if(treeModel != null && treeModel instanceof Serializable) {
3085            values.addElement("treeModel");
3086            values.addElement(treeModel);
3087        }
3088        // Save the selectionModel, if its Serializable.
3089        if(selectionModel != null && selectionModel instanceof Serializable) {
3090            values.addElement("selectionModel");
3091            values.addElement(selectionModel);
3092        }
3093
3094        Object      expandedData = getArchivableExpandedState();
3095
3096        if(expandedData != null) {
3097            values.addElement("expandedState");
3098            values.addElement(expandedData);
3099        }
3100
3101        s.writeObject(values);
3102        if (getUIClassID().equals(uiClassID)) {
3103            byte count = JComponent.getWriteObjCounter(this);
3104            JComponent.setWriteObjCounter(this, --count);
3105            if (count == 0 && ui != null) {
3106                ui.installUI(this);
3107            }
3108        }
3109    }
3110
3111    private void readObject(ObjectInputStream s)
3112        throws IOException, ClassNotFoundException {
3113        ObjectInputStream.GetField f = s.readFields();
3114
3115        rootVisible = f.get("rootVisible", false);
3116        rowHeight = f.get("rowHeight", 0);
3117        rowHeightSet = f.get("rowHeightSet", false);
3118        showsRootHandles = f.get("showsRootHandles", false);
3119        showsRootHandlesSet = f.get("showsRootHandlesSet", false);
3120        editable = f.get("editable", false);
3121        largeModel = f.get("largeModel", false);
3122        visibleRowCount = f.get("visibleRowCount", 0);
3123        invokesStopCellEditing = f.get("invokesStopCellEditing", false);
3124        scrollsOnExpand = f.get("scrollsOnExpand", false);
3125        scrollsOnExpandSet = f.get("scrollsOnExpandSet", false);
3126        toggleClickCount = f.get("toggleClickCount", 0);
3127        leadPath = (TreePath) f.get("leadPath", null);
3128        anchorPath = (TreePath) f.get("anchorPath", null);
3129        expandsSelectedPaths = f.get("expandsSelectedPaths", false);
3130        settingUI = f.get("settingUI", false);
3131        boolean newDragEnabled = f.get("dragEnabled", false);
3132        checkDragEnabled(newDragEnabled);
3133        dragEnabled = newDragEnabled;
3134        DropMode newDropMode = (DropMode) f.get("dropMode",
3135                DropMode.USE_SELECTION);
3136        checkDropMode(newDropMode);
3137        dropMode = newDropMode;
3138
3139        expandRow = f.get("expandRow", -1);
3140        dropTimer = (TreeTimer) f.get("dropTimer", null);
3141
3142        // Create an instance of expanded state.
3143
3144        expandedState = new Hashtable<TreePath, Boolean>();
3145
3146        expandedStack = new Stack<Stack<TreePath>>();
3147
3148        Vector<?>          values = (Vector)s.readObject();
3149        int             indexCounter = 0;
3150        int             maxCounter = values.size();
3151
3152        if(indexCounter < maxCounter && values.elementAt(indexCounter).
3153           equals("cellRenderer")) {
3154            cellRenderer = (TreeCellRenderer)values.elementAt(++indexCounter);
3155            indexCounter++;
3156        }
3157        if(indexCounter < maxCounter && values.elementAt(indexCounter).
3158           equals("cellEditor")) {
3159            cellEditor = (TreeCellEditor)values.elementAt(++indexCounter);
3160            indexCounter++;
3161        }
3162        if(indexCounter < maxCounter && values.elementAt(indexCounter).
3163           equals("treeModel")) {
3164            treeModel = (TreeModel)values.elementAt(++indexCounter);
3165            indexCounter++;
3166        }
3167        if(indexCounter < maxCounter && values.elementAt(indexCounter).
3168           equals("selectionModel")) {
3169            selectionModel = (TreeSelectionModel)values.elementAt(++indexCounter);
3170            indexCounter++;
3171        }
3172        if(indexCounter < maxCounter && values.elementAt(indexCounter).
3173           equals("expandedState")) {
3174            unarchiveExpandedState(values.elementAt(++indexCounter));
3175            indexCounter++;
3176        }
3177        // Reinstall the redirector.
3178        if(listenerList.getListenerCount(TreeSelectionListener.class) != 0) {
3179            selectionRedirector = new TreeSelectionRedirector();
3180            selectionModel.addTreeSelectionListener(selectionRedirector);
3181        }
3182        // Listener to TreeModel.
3183        if(treeModel != null) {
3184            treeModelListener = createTreeModelListener();
3185            if(treeModelListener != null)
3186                treeModel.addTreeModelListener(treeModelListener);
3187        }
3188    }
3189
3190    /**
3191     * Returns an object that can be archived indicating what nodes are
3192     * expanded and what aren't. The objects from the model are NOT
3193     * written out.
3194     */
3195    private Object getArchivableExpandedState() {
3196        TreeModel       model = getModel();
3197
3198        if(model != null) {
3199            Enumeration<TreePath> paths = expandedState.keys();
3200
3201            if(paths != null) {
3202                Vector<Object> state = new Vector<Object>();
3203
3204                while(paths.hasMoreElements()) {
3205                    TreePath path = paths.nextElement();
3206                    Object     archivePath;
3207
3208                    try {
3209                        archivePath = getModelIndexsForPath(path);
3210                    } catch (Error error) {
3211                        archivePath = null;
3212                    }
3213                    if(archivePath != null) {
3214                        state.addElement(archivePath);
3215                        state.addElement(expandedState.get(path));
3216                    }
3217                }
3218                return state;
3219            }
3220        }
3221        return null;
3222    }
3223
3224    /**
3225     * Updates the expanded state of nodes in the tree based on the
3226     * previously archived state <code>state</code>.
3227     */
3228    private void unarchiveExpandedState(Object state) {
3229        if(state instanceof Vector) {
3230            Vector<?>          paths = (Vector)state;
3231
3232            for(int counter = paths.size() - 1; counter >= 0; counter--) {
3233                Boolean        eState = (Boolean)paths.elementAt(counter--);
3234                TreePath       path;
3235
3236                try {
3237                    path = getPathForIndexs((int[])paths.elementAt(counter));
3238                    if(path != null)
3239                        expandedState.put(path, eState);
3240                } catch (Error error) {}
3241            }
3242        }
3243    }
3244
3245    /**
3246     * Returns an array of integers specifying the indexs of the
3247     * components in the <code>path</code>. If <code>path</code> is
3248     * the root, this will return an empty array.  If <code>path</code>
3249     * is <code>null</code>, <code>null</code> will be returned.
3250     */
3251    private int[] getModelIndexsForPath(TreePath path) {
3252        if(path != null) {
3253            TreeModel   model = getModel();
3254            int         count = path.getPathCount();
3255            int[]       indexs = new int[count - 1];
3256            Object      parent = model.getRoot();
3257
3258            for(int counter = 1; counter < count; counter++) {
3259                indexs[counter - 1] = model.getIndexOfChild
3260                                   (parent, path.getPathComponent(counter));
3261                parent = path.getPathComponent(counter);
3262                if(indexs[counter - 1] < 0)
3263                    return null;
3264            }
3265            return indexs;
3266        }
3267        return null;
3268    }
3269
3270    /**
3271     * Returns a <code>TreePath</code> created by obtaining the children
3272     * for each of the indices in <code>indexs</code>. If <code>indexs</code>
3273     * or the <code>TreeModel</code> is <code>null</code>, it will return
3274     * <code>null</code>.
3275     */
3276    private TreePath getPathForIndexs(int[] indexs) {
3277        if(indexs == null)
3278            return null;
3279
3280        TreeModel    model = getModel();
3281
3282        if(model == null)
3283            return null;
3284
3285        int          count = indexs.length;
3286
3287        Object       parent = model.getRoot();
3288        if (parent == null)
3289            return null;
3290
3291        TreePath     parentPath = new TreePath(parent);
3292        for(int counter = 0; counter < count; counter++) {
3293            parent = model.getChild(parent, indexs[counter]);
3294            if(parent == null)
3295                return null;
3296            parentPath = parentPath.pathByAddingChild(parent);
3297        }
3298        return parentPath;
3299    }
3300
3301    /**
3302     * <code>EmptySelectionModel</code> is a <code>TreeSelectionModel</code>
3303     * that does not allow anything to be selected.
3304     * <p>
3305     * <strong>Warning:</strong>
3306     * Serialized objects of this class will not be compatible with
3307     * future Swing releases. The current serialization support is
3308     * appropriate for short term storage or RMI between applications running
3309     * the same version of Swing.  As of 1.4, support for long term storage
3310     * of all JavaBeans&trade;
3311     * has been added to the <code>java.beans</code> package.
3312     * Please see {@link java.beans.XMLEncoder}.
3313     */
3314    @SuppressWarnings("serial")
3315    protected static class EmptySelectionModel extends
3316              DefaultTreeSelectionModel
3317    {
3318        /**
3319         * The single instance of {@code EmptySelectionModel}.
3320         */
3321        protected static final EmptySelectionModel sharedInstance =
3322            new EmptySelectionModel();
3323
3324        /**
3325         * Returns the single instance of {@code EmptySelectionModel}.
3326         *
3327         * @return single instance of {@code EmptySelectionModel}
3328         */
3329        public static EmptySelectionModel sharedInstance() {
3330            return sharedInstance;
3331        }
3332
3333        /**
3334         * This is overriden to do nothing; {@code EmptySelectionModel}
3335         * does not allow a selection.
3336         *
3337         * @param paths the paths to select; this is ignored
3338         */
3339        public void setSelectionPaths(TreePath[] paths) {}
3340
3341        /**
3342         * This is overriden to do nothing; {@code EmptySelectionModel}
3343         * does not allow a selection.
3344         *
3345         * @param paths the paths to add to the selection; this is ignored
3346         */
3347        public void addSelectionPaths(TreePath[] paths) {}
3348
3349        /**
3350         * This is overriden to do nothing; {@code EmptySelectionModel}
3351         * does not allow a selection.
3352         *
3353         * @param paths the paths to remove; this is ignored
3354         */
3355        public void removeSelectionPaths(TreePath[] paths) {}
3356
3357        /**
3358         * This is overriden to do nothing; {@code EmptySelectionModel}
3359         * does not allow a selection.
3360         *
3361         * @param mode the selection mode; this is ignored
3362         * @since 1.7
3363         */
3364        public void setSelectionMode(int mode) {
3365        }
3366
3367        /**
3368         * This is overriden to do nothing; {@code EmptySelectionModel}
3369         * does not allow a selection.
3370         *
3371         * @param mapper the {@code RowMapper} instance; this is ignored
3372         * @since 1.7
3373         */
3374        public void setRowMapper(RowMapper mapper) {
3375        }
3376
3377        /**
3378         * This is overriden to do nothing; {@code EmptySelectionModel}
3379         * does not allow a selection.
3380         *
3381         * @param listener the listener to add; this is ignored
3382         * @since 1.7
3383         */
3384        public void addTreeSelectionListener(TreeSelectionListener listener) {
3385        }
3386
3387        /**
3388         * This is overriden to do nothing; {@code EmptySelectionModel}
3389         * does not allow a selection.
3390         *
3391         * @param listener the listener to remove; this is ignored
3392         * @since 1.7
3393         */
3394        public void removeTreeSelectionListener(
3395                TreeSelectionListener listener) {
3396        }
3397
3398        /**
3399         * This is overriden to do nothing; {@code EmptySelectionModel}
3400         * does not allow a selection.
3401         *
3402         * @param listener the listener to add; this is ignored
3403         * @since 1.7
3404         */
3405        public void addPropertyChangeListener(
3406                                PropertyChangeListener listener) {
3407        }
3408
3409        /**
3410         * This is overriden to do nothing; {@code EmptySelectionModel}
3411         * does not allow a selection.
3412         *
3413         * @param listener the listener to remove; this is ignored
3414         * @since 1.7
3415         */
3416        public void removePropertyChangeListener(
3417                                PropertyChangeListener listener) {
3418        }
3419    }
3420
3421
3422    /**
3423     * Handles creating a new <code>TreeSelectionEvent</code> with the
3424     * <code>JTree</code> as the
3425     * source and passing it off to all the listeners.
3426     * <p>
3427     * <strong>Warning:</strong>
3428     * Serialized objects of this class will not be compatible with
3429     * future Swing releases. The current serialization support is
3430     * appropriate for short term storage or RMI between applications running
3431     * the same version of Swing.  As of 1.4, support for long term storage
3432     * of all JavaBeans&trade;
3433     * has been added to the <code>java.beans</code> package.
3434     * Please see {@link java.beans.XMLEncoder}.
3435     */
3436    @SuppressWarnings("serial")
3437    protected class TreeSelectionRedirector implements Serializable,
3438                    TreeSelectionListener
3439    {
3440        /**
3441         * Invoked by the <code>TreeSelectionModel</code> when the
3442         * selection changes.
3443         *
3444         * @param e the <code>TreeSelectionEvent</code> generated by the
3445         *              <code>TreeSelectionModel</code>
3446         */
3447        public void valueChanged(TreeSelectionEvent e) {
3448            TreeSelectionEvent       newE;
3449
3450            newE = (TreeSelectionEvent)e.cloneWithSource(JTree.this);
3451            fireValueChanged(newE);
3452        }
3453    } // End of class JTree.TreeSelectionRedirector
3454
3455    //
3456    // Scrollable interface
3457    //
3458
3459    /**
3460     * Returns the preferred display size of a <code>JTree</code>. The height is
3461     * determined from <code>getVisibleRowCount</code> and the width
3462     * is the current preferred width.
3463     *
3464     * @return a <code>Dimension</code> object containing the preferred size
3465     */
3466    @BeanProperty(bound = false)
3467    public Dimension getPreferredScrollableViewportSize() {
3468        int                 width = getPreferredSize().width;
3469        int                 visRows = getVisibleRowCount();
3470        int                 height = -1;
3471
3472        if(isFixedRowHeight())
3473            height = visRows * getRowHeight();
3474        else {
3475            TreeUI          ui = getUI();
3476
3477            if (ui != null && visRows > 0) {
3478                int rc = ui.getRowCount(this);
3479
3480                if (rc >= visRows) {
3481                    Rectangle bounds = getRowBounds(visRows - 1);
3482                    if (bounds != null) {
3483                        height = bounds.y + bounds.height;
3484                    }
3485                }
3486                else if (rc > 0) {
3487                    Rectangle bounds = getRowBounds(0);
3488                    if (bounds != null) {
3489                        height = bounds.height * visRows;
3490                    }
3491                }
3492            }
3493            if (height == -1) {
3494                height = 16 * visRows;
3495            }
3496        }
3497        return new Dimension(width, height);
3498    }
3499
3500    /**
3501     * Returns the amount to increment when scrolling. The amount is
3502     * the height of the first displayed row that isn't completely in view
3503     * or, if it is totally displayed, the height of the next row in the
3504     * scrolling direction.
3505     *
3506     * @param visibleRect the view area visible within the viewport
3507     * @param orientation either <code>SwingConstants.VERTICAL</code>
3508     *          or <code>SwingConstants.HORIZONTAL</code>
3509     * @param direction less than zero to scroll up/left,
3510     *          greater than zero for down/right
3511     * @return the "unit" increment for scrolling in the specified direction
3512     * @see JScrollBar#setUnitIncrement(int)
3513     */
3514    public int getScrollableUnitIncrement(Rectangle visibleRect,
3515                                          int orientation, int direction) {
3516        if(orientation == SwingConstants.VERTICAL) {
3517            Rectangle       rowBounds;
3518            int             firstIndex = getClosestRowForLocation
3519                                         (0, visibleRect.y);
3520
3521            if(firstIndex != -1) {
3522                rowBounds = getRowBounds(firstIndex);
3523                if(rowBounds.y != visibleRect.y) {
3524                    if(direction < 0) {
3525                        // UP
3526                        return Math.max(0, (visibleRect.y - rowBounds.y));
3527                    }
3528                    return (rowBounds.y + rowBounds.height - visibleRect.y);
3529                }
3530                if(direction < 0) { // UP
3531                    if(firstIndex != 0) {
3532                        rowBounds = getRowBounds(firstIndex - 1);
3533                        return rowBounds.height;
3534                    }
3535                }
3536                else {
3537                    return rowBounds.height;
3538                }
3539            }
3540            return 0;
3541        }
3542        return 4;
3543    }
3544
3545
3546    /**
3547     * Returns the amount for a block increment, which is the height or
3548     * width of <code>visibleRect</code>, based on <code>orientation</code>.
3549     *
3550     * @param visibleRect the view area visible within the viewport
3551     * @param orientation either <code>SwingConstants.VERTICAL</code>
3552     *          or <code>SwingConstants.HORIZONTAL</code>
3553     * @param direction less than zero to scroll up/left,
3554     *          greater than zero for down/right.
3555     * @return the "block" increment for scrolling in the specified direction
3556     * @see JScrollBar#setBlockIncrement(int)
3557     */
3558    public int getScrollableBlockIncrement(Rectangle visibleRect,
3559                                           int orientation, int direction) {
3560        return (orientation == SwingConstants.VERTICAL) ? visibleRect.height :
3561            visibleRect.width;
3562    }
3563
3564    /**
3565     * Returns false to indicate that the width of the viewport does not
3566     * determine the width of the table, unless the preferred width of
3567     * the tree is smaller than the viewports width.  In other words:
3568     * ensure that the tree is never smaller than its viewport.
3569     *
3570     * @return whether the tree should track the width of the viewport
3571     * @see Scrollable#getScrollableTracksViewportWidth
3572     */
3573    @BeanProperty(bound = false)
3574    public boolean getScrollableTracksViewportWidth() {
3575        Container parent = SwingUtilities.getUnwrappedParent(this);
3576        if (parent instanceof JViewport) {
3577            return parent.getWidth() > getPreferredSize().width;
3578        }
3579        return false;
3580    }
3581
3582    /**
3583     * Returns false to indicate that the height of the viewport does not
3584     * determine the height of the table, unless the preferred height
3585     * of the tree is smaller than the viewports height.  In other words:
3586     * ensure that the tree is never smaller than its viewport.
3587     *
3588     * @return whether the tree should track the height of the viewport
3589     * @see Scrollable#getScrollableTracksViewportHeight
3590     */
3591    @BeanProperty(bound = false)
3592    public boolean getScrollableTracksViewportHeight() {
3593        Container parent = SwingUtilities.getUnwrappedParent(this);
3594        if (parent instanceof JViewport) {
3595            return parent.getHeight() > getPreferredSize().height;
3596        }
3597        return false;
3598    }
3599
3600    /**
3601     * Sets the expanded state of this <code>JTree</code>.
3602     * If <code>state</code> is
3603     * true, all parents of <code>path</code> and path are marked as
3604     * expanded. If <code>state</code> is false, all parents of
3605     * <code>path</code> are marked EXPANDED, but <code>path</code> itself
3606     * is marked collapsed.<p>
3607     * This will fail if a <code>TreeWillExpandListener</code> vetos it.
3608     *
3609     * @param path a {@code TreePath} identifying a node
3610     * @param state if {@code true}, all parents of @{code path} and path are marked as expanded.
3611     *              Otherwise, all parents of {@code path} are marked EXPANDED,
3612     *              but {@code path} itself is marked collapsed.
3613     */
3614    protected void setExpandedState(TreePath path, boolean state) {
3615        if(path != null) {
3616            // Make sure all parents of path are expanded.
3617            Stack<TreePath> stack;
3618            TreePath parentPath = path.getParentPath();
3619
3620            if (expandedStack.size() == 0) {
3621                stack = new Stack<TreePath>();
3622            }
3623            else {
3624                stack = expandedStack.pop();
3625            }
3626
3627            try {
3628                while(parentPath != null) {
3629                    if(isExpanded(parentPath)) {
3630                        parentPath = null;
3631                    }
3632                    else {
3633                        stack.push(parentPath);
3634                        parentPath = parentPath.getParentPath();
3635                    }
3636                }
3637                for(int counter = stack.size() - 1; counter >= 0; counter--) {
3638                    parentPath = stack.pop();
3639                    if(!isExpanded(parentPath)) {
3640                        try {
3641                            fireTreeWillExpand(parentPath);
3642                        } catch (ExpandVetoException eve) {
3643                            // Expand vetoed!
3644                            return;
3645                        }
3646                        expandedState.put(parentPath, Boolean.TRUE);
3647                        fireTreeExpanded(parentPath);
3648                        if (accessibleContext != null) {
3649                            ((AccessibleJTree)accessibleContext).
3650                                              fireVisibleDataPropertyChange();
3651                        }
3652                    }
3653                }
3654            }
3655            finally {
3656                if (expandedStack.size() < TEMP_STACK_SIZE) {
3657                    stack.removeAllElements();
3658                    expandedStack.push(stack);
3659                }
3660            }
3661            if(!state) {
3662                // collapse last path.
3663                Object          cValue = expandedState.get(path);
3664
3665                if(cValue != null && ((Boolean)cValue).booleanValue()) {
3666                    try {
3667                        fireTreeWillCollapse(path);
3668                    }
3669                    catch (ExpandVetoException eve) {
3670                        return;
3671                    }
3672                    expandedState.put(path, Boolean.FALSE);
3673                    fireTreeCollapsed(path);
3674                    if (removeDescendantSelectedPaths(path, false) &&
3675                        !isPathSelected(path)) {
3676                        // A descendant was selected, select the parent.
3677                        addSelectionPath(path);
3678                    }
3679                    if (accessibleContext != null) {
3680                        ((AccessibleJTree)accessibleContext).
3681                                    fireVisibleDataPropertyChange();
3682                    }
3683                }
3684            }
3685            else {
3686                // Expand last path.
3687                Object          cValue = expandedState.get(path);
3688
3689                if(cValue == null || !((Boolean)cValue).booleanValue()) {
3690                    try {
3691                        fireTreeWillExpand(path);
3692                    }
3693                    catch (ExpandVetoException eve) {
3694                        return;
3695                    }
3696                    expandedState.put(path, Boolean.TRUE);
3697                    fireTreeExpanded(path);
3698                    if (accessibleContext != null) {
3699                        ((AccessibleJTree)accessibleContext).
3700                                          fireVisibleDataPropertyChange();
3701                    }
3702                }
3703            }
3704        }
3705    }
3706
3707    /**
3708     * Returns an {@code Enumeration} of {@code TreePaths}
3709     * that have been expanded that
3710     * are descendants of {@code parent}.
3711     *
3712     * @param parent a path
3713     * @return the {@code Enumeration} of {@code TreePaths}
3714     */
3715    protected Enumeration<TreePath>
3716        getDescendantToggledPaths(TreePath parent)
3717    {
3718        if(parent == null)
3719            return null;
3720
3721        Vector<TreePath> descendants = new Vector<TreePath>();
3722        Enumeration<TreePath> nodes = expandedState.keys();
3723
3724        while(nodes.hasMoreElements()) {
3725            TreePath path = nodes.nextElement();
3726            if(parent.isDescendant(path))
3727                descendants.addElement(path);
3728        }
3729        return descendants.elements();
3730    }
3731
3732    /**
3733     * Removes any descendants of the <code>TreePaths</code> in
3734     * <code>toRemove</code>
3735     * that have been expanded.
3736     *
3737     * @param toRemove an enumeration of the paths to remove; a value of
3738     *        {@code null} is ignored
3739     * @throws ClassCastException if {@code toRemove} contains an
3740     *         element that is not a {@code TreePath}; {@code null}
3741     *         values are ignored
3742     */
3743     protected void
3744         removeDescendantToggledPaths(Enumeration<TreePath> toRemove)
3745    {
3746         if(toRemove != null) {
3747             while(toRemove.hasMoreElements()) {
3748                 Enumeration<?> descendants = getDescendantToggledPaths
3749                         (toRemove.nextElement());
3750
3751                 if(descendants != null) {
3752                     while(descendants.hasMoreElements()) {
3753                         expandedState.remove(descendants.nextElement());
3754                     }
3755                 }
3756             }
3757         }
3758     }
3759
3760     /**
3761      * Clears the cache of toggled tree paths. This does NOT send out
3762      * any <code>TreeExpansionListener</code> events.
3763      */
3764     protected void clearToggledPaths() {
3765         expandedState.clear();
3766     }
3767
3768     /**
3769      * Creates and returns an instance of <code>TreeModelHandler</code>.
3770      * The returned
3771      * object is responsible for updating the expanded state when the
3772      * <code>TreeModel</code> changes.
3773      * <p>
3774      * For more information on what expanded state means, see the
3775      * <a href=#jtree_description>JTree description</a> above.
3776      *
3777      * @return the instance of {@code TreeModelHandler}
3778      */
3779     protected TreeModelListener createTreeModelListener() {
3780         return new TreeModelHandler();
3781     }
3782
3783    /**
3784     * Removes any paths in the selection that are descendants of
3785     * <code>path</code>. If <code>includePath</code> is true and
3786     * <code>path</code> is selected, it will be removed from the selection.
3787     *
3788     * @param path a path
3789     * @param includePath is {@code true} and {@code path} is selected,
3790     *                    it will be removed from the selection.
3791     * @return true if a descendant was selected
3792     * @since 1.3
3793     */
3794    protected boolean removeDescendantSelectedPaths(TreePath path,
3795                                                    boolean includePath) {
3796        TreePath[]    toRemove = getDescendantSelectedPaths(path, includePath);
3797
3798        if (toRemove != null) {
3799            getSelectionModel().removeSelectionPaths(toRemove);
3800            return true;
3801        }
3802        return false;
3803    }
3804
3805    /**
3806     * Returns an array of paths in the selection that are descendants of
3807     * <code>path</code>. The returned array may contain <code>null</code>s.
3808     */
3809    private TreePath[] getDescendantSelectedPaths(TreePath path,
3810                                                  boolean includePath) {
3811        TreeSelectionModel   sm = getSelectionModel();
3812        TreePath[]           selPaths = (sm != null) ? sm.getSelectionPaths() :
3813                                        null;
3814
3815        if(selPaths != null) {
3816            boolean        shouldRemove = false;
3817
3818            for(int counter = selPaths.length - 1; counter >= 0; counter--) {
3819                if(selPaths[counter] != null &&
3820                   path.isDescendant(selPaths[counter]) &&
3821                   (!path.equals(selPaths[counter]) || includePath))
3822                    shouldRemove = true;
3823                else
3824                    selPaths[counter] = null;
3825            }
3826            if(!shouldRemove) {
3827                selPaths = null;
3828            }
3829            return selPaths;
3830        }
3831        return null;
3832    }
3833
3834    /**
3835     * Removes any paths from the selection model that are descendants of
3836     * the nodes identified by in <code>e</code>.
3837     */
3838    void removeDescendantSelectedPaths(TreeModelEvent e) {
3839        TreePath            pPath = SwingUtilities2.getTreePath(e, getModel());
3840        Object[]            oldChildren = e.getChildren();
3841        TreeSelectionModel  sm = getSelectionModel();
3842
3843        if (sm != null && pPath != null && oldChildren != null &&
3844            oldChildren.length > 0) {
3845            for (int counter = oldChildren.length - 1; counter >= 0;
3846                 counter--) {
3847                // Might be better to call getDescendantSelectedPaths
3848                // numerous times, then push to the model.
3849                removeDescendantSelectedPaths(pPath.pathByAddingChild
3850                                              (oldChildren[counter]), true);
3851            }
3852        }
3853    }
3854
3855
3856     /**
3857      * Listens to the model and updates the <code>expandedState</code>
3858      * accordingly when nodes are removed, or changed.
3859      */
3860    protected class TreeModelHandler implements TreeModelListener {
3861        public void treeNodesChanged(TreeModelEvent e) { }
3862
3863        public void treeNodesInserted(TreeModelEvent e) { }
3864
3865        public void treeStructureChanged(TreeModelEvent e) {
3866            if(e == null)
3867                return;
3868
3869            // NOTE: If I change this to NOT remove the descendants
3870            // and update BasicTreeUIs treeStructureChanged method
3871            // to update descendants in response to a treeStructureChanged
3872            // event, all the children of the event won't collapse!
3873            TreePath            parent = SwingUtilities2.getTreePath(e, getModel());
3874
3875            if(parent == null)
3876                return;
3877
3878            if (parent.getPathCount() == 1) {
3879                // New root, remove everything!
3880                clearToggledPaths();
3881
3882              Object treeRoot = treeModel.getRoot();
3883
3884              if(treeRoot != null &&
3885                !treeModel.isLeaf(treeRoot)) {
3886                    // Mark the root as expanded, if it isn't a leaf.
3887                    expandedState.put(parent, Boolean.TRUE);
3888                }
3889            }
3890            else if(expandedState.get(parent) != null) {
3891                Vector<TreePath>    toRemove = new Vector<TreePath>(1);
3892                boolean             isExpanded = isExpanded(parent);
3893
3894                toRemove.addElement(parent);
3895                removeDescendantToggledPaths(toRemove.elements());
3896                if(isExpanded) {
3897                    TreeModel         model = getModel();
3898
3899                    if(model == null || model.isLeaf
3900                       (parent.getLastPathComponent()))
3901                        collapsePath(parent);
3902                    else
3903                        expandedState.put(parent, Boolean.TRUE);
3904                }
3905            }
3906            removeDescendantSelectedPaths(parent, false);
3907        }
3908
3909        public void treeNodesRemoved(TreeModelEvent e) {
3910            if(e == null)
3911                return;
3912
3913            TreePath            parent = SwingUtilities2.getTreePath(e, getModel());
3914            Object[]            children = e.getChildren();
3915
3916            if(children == null)
3917                return;
3918
3919            TreePath            rPath;
3920            Vector<TreePath>    toRemove
3921                = new Vector<TreePath>(Math.max(1, children.length));
3922
3923            for(int counter = children.length - 1; counter >= 0; counter--) {
3924                rPath = parent.pathByAddingChild(children[counter]);
3925                if(expandedState.get(rPath) != null)
3926                    toRemove.addElement(rPath);
3927            }
3928            if(toRemove.size() > 0)
3929                removeDescendantToggledPaths(toRemove.elements());
3930
3931            TreeModel         model = getModel();
3932
3933            if(model == null || model.isLeaf(parent.getLastPathComponent()))
3934                expandedState.remove(parent);
3935
3936            removeDescendantSelectedPaths(e);
3937        }
3938    }
3939
3940
3941    /**
3942     * <code>DynamicUtilTreeNode</code> can wrap
3943     * vectors/hashtables/arrays/strings and
3944     * create the appropriate children tree nodes as necessary. It is
3945     * dynamic in that it will only create the children as necessary.
3946     * <p>
3947     * <strong>Warning:</strong>
3948     * Serialized objects of this class will not be compatible with
3949     * future Swing releases. The current serialization support is
3950     * appropriate for short term storage or RMI between applications running
3951     * the same version of Swing.  As of 1.4, support for long term storage
3952     * of all JavaBeans&trade;
3953     * has been added to the <code>java.beans</code> package.
3954     * Please see {@link java.beans.XMLEncoder}.
3955     */
3956    @SuppressWarnings("serial")
3957    public static class DynamicUtilTreeNode extends DefaultMutableTreeNode {
3958        /**
3959         * Does the this <code>JTree</code> have children?
3960         * This property is currently not implemented.
3961         */
3962        protected boolean            hasChildren;
3963        /** Value to create children with. */
3964        protected Object             childValue;
3965        /** Have the children been loaded yet? */
3966        protected boolean            loadedChildren;
3967
3968        /**
3969         * Adds to parent all the children in <code>children</code>.
3970         * If <code>children</code> is an array or vector all of its
3971         * elements are added is children, otherwise if <code>children</code>
3972         * is a hashtable all the key/value pairs are added in the order
3973         * <code>Enumeration</code> returns them.
3974         *
3975         * @param parent the parent node
3976         * @param children the children
3977         */
3978        public static void createChildren(DefaultMutableTreeNode parent,
3979                                          Object children) {
3980            if(children instanceof Vector) {
3981                Vector<?>          childVector = (Vector)children;
3982
3983                for(int counter = 0, maxCounter = childVector.size();
3984                    counter < maxCounter; counter++)
3985                    parent.add(new DynamicUtilTreeNode
3986                               (childVector.elementAt(counter),
3987                                childVector.elementAt(counter)));
3988            }
3989            else if(children instanceof Hashtable) {
3990                Hashtable<?,?>           childHT = (Hashtable)children;
3991                Enumeration<?>         keys = childHT.keys();
3992                Object              aKey;
3993
3994                while(keys.hasMoreElements()) {
3995                    aKey = keys.nextElement();
3996                    parent.add(new DynamicUtilTreeNode(aKey,
3997                                                       childHT.get(aKey)));
3998                }
3999            }
4000            else if(children instanceof Object[]) {
4001                Object[]             childArray = (Object[])children;
4002
4003                for(int counter = 0, maxCounter = childArray.length;
4004                    counter < maxCounter; counter++)
4005                    parent.add(new DynamicUtilTreeNode(childArray[counter],
4006                                                       childArray[counter]));
4007            }
4008        }
4009
4010        /**
4011         * Creates a node with the specified object as its value and
4012         * with the specified children. For the node to allow children,
4013         * the children-object must be an array of objects, a
4014         * <code>Vector</code>, or a <code>Hashtable</code> -- even
4015         * if empty. Otherwise, the node is not
4016         * allowed to have children.
4017         *
4018         * @param value  the <code>Object</code> that is the value for the
4019         *              new node
4020         * @param children an array of <code>Object</code>s, a
4021         *              <code>Vector</code>, or a <code>Hashtable</code>
4022         *              used to create the child nodes; if any other
4023         *              object is specified, or if the value is
4024         *              <code>null</code>,
4025         *              then the node is not allowed to have children
4026         */
4027        public DynamicUtilTreeNode(Object value, Object children) {
4028            super(value);
4029            loadedChildren = false;
4030            childValue = children;
4031            if(children != null) {
4032                if(children instanceof Vector)
4033                    setAllowsChildren(true);
4034                else if(children instanceof Hashtable)
4035                    setAllowsChildren(true);
4036                else if(children instanceof Object[])
4037                    setAllowsChildren(true);
4038                else
4039                    setAllowsChildren(false);
4040            }
4041            else
4042                setAllowsChildren(false);
4043        }
4044
4045        /**
4046         * Returns true if this node allows children. Whether the node
4047         * allows children depends on how it was created.
4048         *
4049         * @return true if this node allows children, false otherwise
4050         * @see JTree.DynamicUtilTreeNode
4051         */
4052        public boolean isLeaf() {
4053            return !getAllowsChildren();
4054        }
4055
4056        /**
4057         * Returns the number of child nodes.
4058         *
4059         * @return the number of child nodes
4060         */
4061        public int getChildCount() {
4062            if(!loadedChildren)
4063                loadChildren();
4064            return super.getChildCount();
4065        }
4066
4067        /**
4068         * Loads the children based on <code>childValue</code>.
4069         * If <code>childValue</code> is a <code>Vector</code>
4070         * or array each element is added as a child,
4071         * if <code>childValue</code> is a <code>Hashtable</code>
4072         * each key/value pair is added in the order that
4073         * <code>Enumeration</code> returns the keys.
4074         */
4075        protected void loadChildren() {
4076            loadedChildren = true;
4077            createChildren(this, childValue);
4078        }
4079
4080        /**
4081         * Subclassed to load the children, if necessary.
4082         */
4083        public TreeNode getChildAt(int index) {
4084            if(!loadedChildren)
4085                loadChildren();
4086            return super.getChildAt(index);
4087        }
4088
4089        /**
4090         * Subclassed to load the children, if necessary.
4091         */
4092        public Enumeration<TreeNode> children() {
4093            if(!loadedChildren)
4094                loadChildren();
4095            return super.children();
4096        }
4097    }
4098
4099    void setUIProperty(String propertyName, Object value) {
4100        if (propertyName == "rowHeight") {
4101            if (!rowHeightSet) {
4102                setRowHeight(((Number)value).intValue());
4103                rowHeightSet = false;
4104            }
4105        } else if (propertyName == "scrollsOnExpand") {
4106            if (!scrollsOnExpandSet) {
4107                setScrollsOnExpand(((Boolean)value).booleanValue());
4108                scrollsOnExpandSet = false;
4109            }
4110        } else if (propertyName == "showsRootHandles") {
4111            if (!showsRootHandlesSet) {
4112                setShowsRootHandles(((Boolean)value).booleanValue());
4113                showsRootHandlesSet = false;
4114            }
4115        } else {
4116            super.setUIProperty(propertyName, value);
4117        }
4118    }
4119
4120
4121    /**
4122     * Returns a string representation of this <code>JTree</code>.
4123     * This method
4124     * is intended to be used only for debugging purposes, and the
4125     * content and format of the returned string may vary between
4126     * implementations. The returned string may be empty but may not
4127     * be <code>null</code>.
4128     *
4129     * @return  a string representation of this <code>JTree</code>.
4130     */
4131    protected String paramString() {
4132        String rootVisibleString = (rootVisible ?
4133                                    "true" : "false");
4134        String showsRootHandlesString = (showsRootHandles ?
4135                                         "true" : "false");
4136        String editableString = (editable ?
4137                                 "true" : "false");
4138        String largeModelString = (largeModel ?
4139                                   "true" : "false");
4140        String invokesStopCellEditingString = (invokesStopCellEditing ?
4141                                               "true" : "false");
4142        String scrollsOnExpandString = (scrollsOnExpand ?
4143                                        "true" : "false");
4144
4145        return super.paramString() +
4146        ",editable=" + editableString +
4147        ",invokesStopCellEditing=" + invokesStopCellEditingString +
4148        ",largeModel=" + largeModelString +
4149        ",rootVisible=" + rootVisibleString +
4150        ",rowHeight=" + rowHeight +
4151        ",scrollsOnExpand=" + scrollsOnExpandString +
4152        ",showsRootHandles=" + showsRootHandlesString +
4153        ",toggleClickCount=" + toggleClickCount +
4154        ",visibleRowCount=" + visibleRowCount;
4155    }
4156
4157/////////////////
4158// Accessibility support
4159////////////////
4160
4161    /**
4162     * Gets the AccessibleContext associated with this JTree.
4163     * For JTrees, the AccessibleContext takes the form of an
4164     * AccessibleJTree.
4165     * A new AccessibleJTree instance is created if necessary.
4166     *
4167     * @return an AccessibleJTree that serves as the
4168     *         AccessibleContext of this JTree
4169     */
4170    @BeanProperty(bound = false)
4171    public AccessibleContext getAccessibleContext() {
4172        if (accessibleContext == null) {
4173            accessibleContext = new AccessibleJTree();
4174        }
4175        return accessibleContext;
4176    }
4177
4178    /**
4179     * This class implements accessibility support for the
4180     * <code>JTree</code> class.  It provides an implementation of the
4181     * Java Accessibility API appropriate to tree user-interface elements.
4182     * <p>
4183     * <strong>Warning:</strong>
4184     * Serialized objects of this class will not be compatible with
4185     * future Swing releases. The current serialization support is
4186     * appropriate for short term storage or RMI between applications running
4187     * the same version of Swing.  As of 1.4, support for long term storage
4188     * of all JavaBeans&trade;
4189     * has been added to the <code>java.beans</code> package.
4190     * Please see {@link java.beans.XMLEncoder}.
4191     */
4192    @SuppressWarnings("serial")
4193    protected class AccessibleJTree extends AccessibleJComponent
4194            implements AccessibleSelection, TreeSelectionListener,
4195                       TreeModelListener, TreeExpansionListener  {
4196
4197        TreePath   leadSelectionPath;
4198        Accessible leadSelectionAccessible;
4199
4200        /**
4201         * Constructs {@code AccessibleJTree}
4202         */
4203        public AccessibleJTree() {
4204            // Add a tree model listener for JTree
4205            TreeModel model = JTree.this.getModel();
4206            if (model != null) {
4207                model.addTreeModelListener(this);
4208            }
4209            JTree.this.addTreeExpansionListener(this);
4210            JTree.this.addTreeSelectionListener(this);
4211            leadSelectionPath = JTree.this.getLeadSelectionPath();
4212            leadSelectionAccessible = (leadSelectionPath != null)
4213                    ? new AccessibleJTreeNode(JTree.this,
4214                                              leadSelectionPath,
4215                                              JTree.this)
4216                    : null;
4217        }
4218
4219        /**
4220         * Tree Selection Listener value change method. Used to fire the
4221         * property change
4222         *
4223         * @param e ListSelectionEvent
4224         *
4225         */
4226        public void valueChanged(TreeSelectionEvent e) {
4227             firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
4228                                Boolean.valueOf(false), Boolean.valueOf(true));
4229         }
4230
4231        /**
4232         * Fire a visible data property change notification.
4233         * A 'visible' data property is one that represents
4234         * something about the way the component appears on the
4235         * display, where that appearance isn't bound to any other
4236         * property. It notifies screen readers  that the visual
4237         * appearance of the component has changed, so they can
4238         * notify the user.
4239         */
4240        public void fireVisibleDataPropertyChange() {
4241           firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
4242                              Boolean.valueOf(false), Boolean.valueOf(true));
4243        }
4244
4245        // Fire the visible data changes for the model changes.
4246
4247        /**
4248         * Tree Model Node change notification.
4249         *
4250         * @param e  a Tree Model event
4251         */
4252        public void treeNodesChanged(TreeModelEvent e) {
4253           fireVisibleDataPropertyChange();
4254        }
4255
4256        /**
4257         * Tree Model Node change notification.
4258         *
4259         * @param e  a Tree node insertion event
4260         */
4261        public void treeNodesInserted(TreeModelEvent e) {
4262           fireVisibleDataPropertyChange();
4263        }
4264
4265        /**
4266         * Tree Model Node change notification.
4267         *
4268         * @param e  a Tree node(s) removal event
4269         */
4270        public  void treeNodesRemoved(TreeModelEvent e) {
4271           fireVisibleDataPropertyChange();
4272        }
4273
4274        /**
4275         * Tree Model structure change change notification.
4276         *
4277         * @param e  a Tree Model event
4278         */
4279        public  void treeStructureChanged(TreeModelEvent e) {
4280           fireVisibleDataPropertyChange();
4281        }
4282
4283        /**
4284         * Tree Collapsed notification.
4285         *
4286         * @param e  a TreeExpansionEvent
4287         */
4288        public  void treeCollapsed(TreeExpansionEvent e) {
4289            fireVisibleDataPropertyChange();
4290            TreePath path = e.getPath();
4291            if (path != null) {
4292                // Set parent to null so AccessibleJTreeNode computes
4293                // its parent.
4294                AccessibleJTreeNode node = new AccessibleJTreeNode(JTree.this,
4295                                                                   path,
4296                                                                   null);
4297                PropertyChangeEvent pce = new PropertyChangeEvent(node,
4298                    AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4299                    AccessibleState.EXPANDED,
4300                    AccessibleState.COLLAPSED);
4301                firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4302                                   null, pce);
4303            }
4304        }
4305
4306        /**
4307         * Tree Model Expansion notification.
4308         *
4309         * @param e  a Tree node insertion event
4310         */
4311        public  void treeExpanded(TreeExpansionEvent e) {
4312            fireVisibleDataPropertyChange();
4313            TreePath path = e.getPath();
4314            if (path != null) {
4315                // TIGER - 4839971
4316                // Set parent to null so AccessibleJTreeNode computes
4317                // its parent.
4318                AccessibleJTreeNode node = new AccessibleJTreeNode(JTree.this,
4319                                                                   path,
4320                                                                   null);
4321                PropertyChangeEvent pce = new PropertyChangeEvent(node,
4322                    AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4323                    AccessibleState.COLLAPSED,
4324                    AccessibleState.EXPANDED);
4325                firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4326                                   null, pce);
4327            }
4328        }
4329
4330        /**
4331        *  Fire an active descendant property change notification.
4332        *  The active descendant is used for objects such as list,
4333        *  tree, and table, which may have transient children.
4334        *  It notifies screen readers the active child of the component
4335        *  has been changed so user can be notified from there.
4336        *
4337        * @param oldPath - lead path of previous active child
4338        * @param newPath - lead path of current active child
4339        *
4340        */
4341        void fireActiveDescendantPropertyChange(TreePath oldPath, TreePath newPath){
4342            if(oldPath != newPath){
4343                Accessible oldLSA = (oldPath != null)
4344                                    ? new AccessibleJTreeNode(JTree.this,
4345                                                              oldPath,
4346                                                              null)
4347                                    : null;
4348
4349                Accessible newLSA = (newPath != null)
4350                                    ? new AccessibleJTreeNode(JTree.this,
4351                                                              newPath,
4352                                                              null)
4353                                    : null;
4354                firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
4355                                                                oldLSA, newLSA);
4356            }
4357        }
4358
4359        private AccessibleContext getCurrentAccessibleContext() {
4360            Component c = getCurrentComponent();
4361            if (c instanceof Accessible) {
4362                return c.getAccessibleContext();
4363            } else {
4364                return null;
4365            }
4366        }
4367
4368        private Component getCurrentComponent() {
4369            // is the object visible?
4370            // if so, get row, selected, focus & leaf state,
4371            // and then get the renderer component and return it
4372            TreeModel model = JTree.this.getModel();
4373            if (model == null) {
4374                return null;
4375            }
4376
4377            Object treeRoot = model.getRoot();
4378            if (treeRoot == null) {
4379                return null;
4380            }
4381            TreePath path = new TreePath(treeRoot);
4382            if (JTree.this.isVisible(path)) {
4383                TreeCellRenderer r = JTree.this.getCellRenderer();
4384                TreeUI ui = JTree.this.getUI();
4385                if (ui != null) {
4386                    int row = ui.getRowForPath(JTree.this, path);
4387                    int lsr = JTree.this.getLeadSelectionRow();
4388                    boolean hasFocus = JTree.this.isFocusOwner()
4389                                       && (lsr == row);
4390                    boolean selected = JTree.this.isPathSelected(path);
4391                    boolean expanded = JTree.this.isExpanded(path);
4392
4393                    return r.getTreeCellRendererComponent(JTree.this,
4394                        treeRoot, selected, expanded,
4395                        model.isLeaf(treeRoot), row, hasFocus);
4396                }
4397            }
4398            return null;
4399        }
4400
4401        // Overridden methods from AccessibleJComponent
4402
4403        /**
4404         * Get the role of this object.
4405         *
4406         * @return an instance of AccessibleRole describing the role of the
4407         * object
4408         * @see AccessibleRole
4409         */
4410        public AccessibleRole getAccessibleRole() {
4411            return AccessibleRole.TREE;
4412        }
4413
4414        /**
4415         * Returns the <code>Accessible</code> child, if one exists,
4416         * contained at the local coordinate <code>Point</code>.
4417         * Otherwise returns <code>null</code>.
4418         *
4419         * @param p point in local coordinates of this <code>Accessible</code>
4420         * @return the <code>Accessible</code>, if it exists,
4421         *    at the specified location; else <code>null</code>
4422         */
4423        public Accessible getAccessibleAt(Point p) {
4424            TreePath path = getClosestPathForLocation(p.x, p.y);
4425            if (path != null) {
4426                // JTree.this is NOT the parent; parent will get computed later
4427                return new AccessibleJTreeNode(JTree.this, path, null);
4428            } else {
4429                return null;
4430            }
4431        }
4432
4433        /**
4434         * Returns the number of top-level children nodes of this
4435         * JTree.  Each of these nodes may in turn have children nodes.
4436         *
4437         * @return the number of accessible children nodes in the tree.
4438         */
4439        public int getAccessibleChildrenCount() {
4440            TreeModel model = JTree.this.getModel();
4441            if (model == null) {
4442                return 0;
4443            }
4444            if (isRootVisible()) {
4445                return 1;    // the root node
4446            }
4447
4448            Object treeRoot = model.getRoot();
4449            if (treeRoot == null)
4450                return 0;
4451
4452            // return the root's first set of children count
4453            return model.getChildCount(treeRoot);
4454        }
4455
4456        /**
4457         * Return the nth Accessible child of the object.
4458         *
4459         * @param i zero-based index of child
4460         * @return the nth Accessible child of the object
4461         */
4462        public Accessible getAccessibleChild(int i) {
4463            TreeModel model = JTree.this.getModel();
4464            if (model == null) {
4465                return null;
4466            }
4467
4468            Object treeRoot = model.getRoot();
4469            if (treeRoot == null) {
4470                return null;
4471            }
4472
4473            if (isRootVisible()) {
4474                if (i == 0) {    // return the root node Accessible
4475                    Object[] objPath = { treeRoot };
4476                    TreePath path = new TreePath(objPath);
4477                    return new AccessibleJTreeNode(JTree.this, path, JTree.this);
4478                } else {
4479                    return null;
4480                }
4481            }
4482
4483            // return Accessible for one of root's child nodes
4484            int count = model.getChildCount(treeRoot);
4485            if (i < 0 || i >= count) {
4486                return null;
4487            }
4488            Object obj = model.getChild(treeRoot, i);
4489            if (obj == null)
4490                return null;
4491
4492            Object[] objPath = {treeRoot, obj };
4493
4494            TreePath path = new TreePath(objPath);
4495            return new AccessibleJTreeNode(JTree.this, path, JTree.this);
4496        }
4497
4498        /**
4499         * Get the index of this object in its accessible parent.
4500         *
4501         * @return the index of this object in its parent.  Since a JTree
4502         * top-level object does not have an accessible parent.
4503         * @see #getAccessibleParent
4504         */
4505        public int getAccessibleIndexInParent() {
4506            // didn't ever need to override this...
4507            return super.getAccessibleIndexInParent();
4508        }
4509
4510        // AccessibleSelection methods
4511        /**
4512         * Get the AccessibleSelection associated with this object.  In the
4513         * implementation of the Java Accessibility API for this class,
4514         * return this object, which is responsible for implementing the
4515         * AccessibleSelection interface on behalf of itself.
4516         *
4517         * @return this object
4518         */
4519        public AccessibleSelection getAccessibleSelection() {
4520            return this;
4521        }
4522
4523        /**
4524         * Returns the number of items currently selected.
4525         * If no items are selected, the return value will be 0.
4526         *
4527         * @return the number of items currently selected.
4528         */
4529        public int getAccessibleSelectionCount() {
4530            Object[] rootPath = new Object[1];
4531            rootPath[0] = treeModel.getRoot();
4532            if (rootPath[0] == null)
4533                return 0;
4534
4535            TreePath childPath = new TreePath(rootPath);
4536            if (JTree.this.isPathSelected(childPath)) {
4537                return 1;
4538            } else {
4539                return 0;
4540            }
4541        }
4542
4543        /**
4544         * Returns an Accessible representing the specified selected item
4545         * in the object.  If there isn't a selection, or there are
4546         * fewer items selected than the integer passed in, the return
4547         * value will be null.
4548         *
4549         * @param i the zero-based index of selected items
4550         * @return an Accessible containing the selected item
4551         */
4552        public Accessible getAccessibleSelection(int i) {
4553            // The JTree can have only one accessible child, the root.
4554            if (i == 0) {
4555                Object[] rootPath = new Object[1];
4556                rootPath[0] = treeModel.getRoot();
4557                if (rootPath[0] == null)
4558                    return null;
4559
4560                TreePath childPath = new TreePath(rootPath);
4561                if (JTree.this.isPathSelected(childPath)) {
4562                    return new AccessibleJTreeNode(JTree.this, childPath, JTree.this);
4563                }
4564            }
4565            return null;
4566        }
4567
4568        /**
4569         * Returns true if the current child of this object is selected.
4570         *
4571         * @param i the zero-based index of the child in this Accessible object.
4572         * @see AccessibleContext#getAccessibleChild
4573         */
4574        public boolean isAccessibleChildSelected(int i) {
4575            // The JTree can have only one accessible child, the root.
4576            if (i == 0) {
4577                Object[] rootPath = new Object[1];
4578                rootPath[0] = treeModel.getRoot();
4579                if (rootPath[0] == null)
4580                    return false;
4581
4582                TreePath childPath = new TreePath(rootPath);
4583                return JTree.this.isPathSelected(childPath);
4584            } else {
4585                return false;
4586            }
4587        }
4588
4589        /**
4590         * Adds the specified selected item in the object to the object's
4591         * selection.  If the object supports multiple selections,
4592         * the specified item is added to any existing selection, otherwise
4593         * it replaces any existing selection in the object.  If the
4594         * specified item is already selected, this method has no effect.
4595         *
4596         * @param i the zero-based index of selectable items
4597         */
4598        public void addAccessibleSelection(int i) {
4599           TreeModel model = JTree.this.getModel();
4600           if (model != null) {
4601               if (i == 0) {
4602                    Object[] objPath = {model.getRoot()};
4603                    if (objPath[0] == null)
4604                        return;
4605
4606                   TreePath path = new TreePath(objPath);
4607                   JTree.this.addSelectionPath(path);
4608                }
4609            }
4610        }
4611
4612        /**
4613         * Removes the specified selected item in the object from the object's
4614         * selection.  If the specified item isn't currently selected, this
4615         * method has no effect.
4616         *
4617         * @param i the zero-based index of selectable items
4618         */
4619        public void removeAccessibleSelection(int i) {
4620            TreeModel model = JTree.this.getModel();
4621            if (model != null) {
4622                if (i == 0) {
4623                    Object[] objPath = {model.getRoot()};
4624                    if (objPath[0] == null)
4625                        return;
4626
4627                    TreePath path = new TreePath(objPath);
4628                    JTree.this.removeSelectionPath(path);
4629                }
4630            }
4631        }
4632
4633        /**
4634         * Clears the selection in the object, so that nothing in the
4635         * object is selected.
4636         */
4637        public void clearAccessibleSelection() {
4638            int childCount = getAccessibleChildrenCount();
4639            for (int i = 0; i < childCount; i++) {
4640                removeAccessibleSelection(i);
4641            }
4642        }
4643
4644        /**
4645         * Causes every selected item in the object to be selected
4646         * if the object supports multiple selections.
4647         */
4648        public void selectAllAccessibleSelection() {
4649            TreeModel model = JTree.this.getModel();
4650            if (model != null) {
4651                Object[] objPath = {model.getRoot()};
4652                if (objPath[0] == null)
4653                    return;
4654
4655                TreePath path = new TreePath(objPath);
4656                JTree.this.addSelectionPath(path);
4657            }
4658        }
4659
4660        /**
4661         * This class implements accessibility support for the
4662         * <code>JTree</code> child.  It provides an implementation of the
4663         * Java Accessibility API appropriate to tree nodes.
4664         */
4665        protected class AccessibleJTreeNode extends AccessibleContext
4666            implements Accessible, AccessibleComponent, AccessibleSelection,
4667            AccessibleAction {
4668
4669            private JTree tree = null;
4670            private TreeModel treeModel = null;
4671            private Object obj = null;
4672            private TreePath path = null;
4673            private Accessible accessibleParent = null;
4674            private int index = 0;
4675            private boolean isLeaf = false;
4676
4677            /**
4678             * Constructs an AccessibleJTreeNode
4679             *
4680             * @param t an instance of {@code JTree}
4681             * @param p an instance of {@code TreePath}
4682             * @param ap an instance of {@code Accessible}
4683             * @since 1.4
4684             */
4685            public AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) {
4686                tree = t;
4687                path = p;
4688                accessibleParent = ap;
4689                treeModel = t.getModel();
4690                obj = p.getLastPathComponent();
4691                if (treeModel != null) {
4692                    isLeaf = treeModel.isLeaf(obj);
4693                }
4694            }
4695
4696            private TreePath getChildTreePath(int i) {
4697                // Tree nodes can't be so complex that they have
4698                // two sets of children -> we're ignoring that case
4699                if (i < 0 || i >= getAccessibleChildrenCount()) {
4700                    return null;
4701                } else {
4702                    Object childObj = treeModel.getChild(obj, i);
4703                    Object[] objPath = path.getPath();
4704                    Object[] objChildPath = new Object[objPath.length+1];
4705                    java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
4706                    objChildPath[objChildPath.length-1] = childObj;
4707                    return new TreePath(objChildPath);
4708                }
4709            }
4710
4711            /**
4712             * Get the AccessibleContext associated with this tree node.
4713             * In the implementation of the Java Accessibility API for
4714             * this class, return this object, which is its own
4715             * AccessibleContext.
4716             *
4717             * @return this object
4718             */
4719            public AccessibleContext getAccessibleContext() {
4720                return this;
4721            }
4722
4723            private AccessibleContext getCurrentAccessibleContext() {
4724                Component c = getCurrentComponent();
4725                if (c instanceof Accessible) {
4726                    return c.getAccessibleContext();
4727                } else {
4728                    return null;
4729                }
4730            }
4731
4732            private Component getCurrentComponent() {
4733                // is the object visible?
4734                // if so, get row, selected, focus & leaf state,
4735                // and then get the renderer component and return it
4736                if (tree.isVisible(path)) {
4737                    TreeCellRenderer r = tree.getCellRenderer();
4738                    if (r == null) {
4739                        return null;
4740                    }
4741                    TreeUI ui = tree.getUI();
4742                    if (ui != null) {
4743                        int row = ui.getRowForPath(JTree.this, path);
4744                        boolean selected = tree.isPathSelected(path);
4745                        boolean expanded = tree.isExpanded(path);
4746                        boolean hasFocus = false; // how to tell?? -PK
4747                        return r.getTreeCellRendererComponent(tree, obj,
4748                            selected, expanded, isLeaf, row, hasFocus);
4749                    }
4750                }
4751                return null;
4752            }
4753
4754        // AccessibleContext methods
4755
4756             /**
4757              * Get the accessible name of this object.
4758              *
4759              * @return the localized name of the object; null if this
4760              * object does not have a name
4761              */
4762             public String getAccessibleName() {
4763                AccessibleContext ac = getCurrentAccessibleContext();
4764                if (ac != null) {
4765                    String name = ac.getAccessibleName();
4766                    if ((name != null) && (name != "")) {
4767                        return ac.getAccessibleName();
4768                    } else {
4769                        return null;
4770                    }
4771                }
4772                if ((accessibleName != null) && (accessibleName != "")) {
4773                    return accessibleName;
4774                } else {
4775                    // fall back to the client property
4776                    return (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
4777                }
4778            }
4779
4780            /**
4781             * Set the localized accessible name of this object.
4782             *
4783             * @param s the new localized name of the object.
4784             */
4785            public void setAccessibleName(String s) {
4786                AccessibleContext ac = getCurrentAccessibleContext();
4787                if (ac != null) {
4788                    ac.setAccessibleName(s);
4789                } else {
4790                    super.setAccessibleName(s);
4791                }
4792            }
4793
4794            //
4795            // *** should check tooltip text for desc. (needs MouseEvent)
4796            //
4797            /**
4798             * Get the accessible description of this object.
4799             *
4800             * @return the localized description of the object; null if
4801             * this object does not have a description
4802             */
4803            public String getAccessibleDescription() {
4804                AccessibleContext ac = getCurrentAccessibleContext();
4805                if (ac != null) {
4806                    return ac.getAccessibleDescription();
4807                } else {
4808                    return super.getAccessibleDescription();
4809                }
4810            }
4811
4812            /**
4813             * Set the accessible description of this object.
4814             *
4815             * @param s the new localized description of the object
4816             */
4817            public void setAccessibleDescription(String s) {
4818                AccessibleContext ac = getCurrentAccessibleContext();
4819                if (ac != null) {
4820                    ac.setAccessibleDescription(s);
4821                } else {
4822                    super.setAccessibleDescription(s);
4823                }
4824            }
4825
4826            /**
4827             * Get the role of this object.
4828             *
4829             * @return an instance of AccessibleRole describing the role of the object
4830             * @see AccessibleRole
4831             */
4832            public AccessibleRole getAccessibleRole() {
4833                AccessibleContext ac = getCurrentAccessibleContext();
4834                if (ac != null) {
4835                    return ac.getAccessibleRole();
4836                } else {
4837                    return AccessibleRole.UNKNOWN;
4838                }
4839            }
4840
4841            /**
4842             * Get the state set of this object.
4843             *
4844             * @return an instance of AccessibleStateSet containing the
4845             * current state set of the object
4846             * @see AccessibleState
4847             */
4848            public AccessibleStateSet getAccessibleStateSet() {
4849                AccessibleContext ac = getCurrentAccessibleContext();
4850                AccessibleStateSet states;
4851                if (ac != null) {
4852                    states = ac.getAccessibleStateSet();
4853                } else {
4854                    states = new AccessibleStateSet();
4855                }
4856                // need to test here, 'cause the underlying component
4857                // is a cellRenderer, which is never showing...
4858                if (isShowing()) {
4859                    states.add(AccessibleState.SHOWING);
4860                } else if (states.contains(AccessibleState.SHOWING)) {
4861                    states.remove(AccessibleState.SHOWING);
4862                }
4863                if (isVisible()) {
4864                    states.add(AccessibleState.VISIBLE);
4865                } else if (states.contains(AccessibleState.VISIBLE)) {
4866                    states.remove(AccessibleState.VISIBLE);
4867                }
4868                if (tree.isPathSelected(path)){
4869                    states.add(AccessibleState.SELECTED);
4870                }
4871                if (path == getLeadSelectionPath()) {
4872                    states.add(AccessibleState.ACTIVE);
4873                }
4874                if (!isLeaf) {
4875                    states.add(AccessibleState.EXPANDABLE);
4876                }
4877                if (tree.isExpanded(path)) {
4878                    states.add(AccessibleState.EXPANDED);
4879                } else {
4880                    states.add(AccessibleState.COLLAPSED);
4881                }
4882                if (tree.isEditable()) {
4883                    states.add(AccessibleState.EDITABLE);
4884                }
4885                return states;
4886            }
4887
4888            /**
4889             * Get the Accessible parent of this object.
4890             *
4891             * @return the Accessible parent of this object; null if this
4892             * object does not have an Accessible parent
4893             */
4894            public Accessible getAccessibleParent() {
4895                // someone wants to know, so we need to create our parent
4896                // if we don't have one (hey, we're a talented kid!)
4897                if (accessibleParent == null) {
4898                    Object[] objPath = path.getPath();
4899                    if (objPath.length > 1) {
4900                        Object objParent = objPath[objPath.length-2];
4901                        if (treeModel != null) {
4902                            index = treeModel.getIndexOfChild(objParent, obj);
4903                        }
4904                        Object[] objParentPath = new Object[objPath.length-1];
4905                        java.lang.System.arraycopy(objPath, 0, objParentPath,
4906                                                   0, objPath.length-1);
4907                        TreePath parentPath = new TreePath(objParentPath);
4908                        accessibleParent = new AccessibleJTreeNode(tree,
4909                                                                   parentPath,
4910                                                                   null);
4911                        this.setAccessibleParent(accessibleParent);
4912                    } else if (treeModel != null) {
4913                        accessibleParent = tree; // we're the top!
4914                        index = 0; // we're an only child!
4915                        this.setAccessibleParent(accessibleParent);
4916                    }
4917                }
4918                return accessibleParent;
4919            }
4920
4921            /**
4922             * Get the index of this object in its accessible parent.
4923             *
4924             * @return the index of this object in its parent; -1 if this
4925             * object does not have an accessible parent.
4926             * @see #getAccessibleParent
4927             */
4928            public int getAccessibleIndexInParent() {
4929                // index is invalid 'till we have an accessibleParent...
4930                if (accessibleParent == null) {
4931                    getAccessibleParent();
4932                }
4933                Object[] objPath = path.getPath();
4934                if (objPath.length > 1) {
4935                    Object objParent = objPath[objPath.length-2];
4936                    if (treeModel != null) {
4937                        index = treeModel.getIndexOfChild(objParent, obj);
4938                    }
4939                }
4940                return index;
4941            }
4942
4943            /**
4944             * Returns the number of accessible children in the object.
4945             *
4946             * @return the number of accessible children in the object.
4947             */
4948            public int getAccessibleChildrenCount() {
4949                // Tree nodes can't be so complex that they have
4950                // two sets of children -> we're ignoring that case
4951                return treeModel.getChildCount(obj);
4952            }
4953
4954            /**
4955             * Return the specified Accessible child of the object.
4956             *
4957             * @param i zero-based index of child
4958             * @return the Accessible child of the object
4959             */
4960            public Accessible getAccessibleChild(int i) {
4961                // Tree nodes can't be so complex that they have
4962                // two sets of children -> we're ignoring that case
4963                if (i < 0 || i >= getAccessibleChildrenCount()) {
4964                    return null;
4965                } else {
4966                    Object childObj = treeModel.getChild(obj, i);
4967                    Object[] objPath = path.getPath();
4968                    Object[] objChildPath = new Object[objPath.length+1];
4969                    java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
4970                    objChildPath[objChildPath.length-1] = childObj;
4971                    TreePath childPath = new TreePath(objChildPath);
4972                    return new AccessibleJTreeNode(JTree.this, childPath, this);
4973                }
4974            }
4975
4976            /**
4977             * Gets the locale of the component. If the component does not have
4978             * a locale, then the locale of its parent is returned.
4979             *
4980             * @return This component's locale. If this component does not have
4981             * a locale, the locale of its parent is returned.
4982             * @exception IllegalComponentStateException
4983             * If the Component does not have its own locale and has not yet
4984             * been added to a containment hierarchy such that the locale can be
4985             * determined from the containing parent.
4986             * @see #setLocale
4987             */
4988            public Locale getLocale() {
4989                AccessibleContext ac = getCurrentAccessibleContext();
4990                if (ac != null) {
4991                    return ac.getLocale();
4992                } else {
4993                    return tree.getLocale();
4994                }
4995            }
4996
4997            /**
4998             * Add a PropertyChangeListener to the listener list.
4999             * The listener is registered for all properties.
5000             *
5001             * @param l  The PropertyChangeListener to be added
5002             */
5003            public void addPropertyChangeListener(PropertyChangeListener l) {
5004                AccessibleContext ac = getCurrentAccessibleContext();
5005                if (ac != null) {
5006                    ac.addPropertyChangeListener(l);
5007                } else {
5008                    super.addPropertyChangeListener(l);
5009                }
5010            }
5011
5012            /**
5013             * Remove a PropertyChangeListener from the listener list.
5014             * This removes a PropertyChangeListener that was registered
5015             * for all properties.
5016             *
5017             * @param l  The PropertyChangeListener to be removed
5018             */
5019            public void removePropertyChangeListener(PropertyChangeListener l) {
5020                AccessibleContext ac = getCurrentAccessibleContext();
5021                if (ac != null) {
5022                    ac.removePropertyChangeListener(l);
5023                } else {
5024                    super.removePropertyChangeListener(l);
5025                }
5026            }
5027
5028            /**
5029             * Get the AccessibleAction associated with this object.  In the
5030             * implementation of the Java Accessibility API for this class,
5031             * return this object, which is responsible for implementing the
5032             * AccessibleAction interface on behalf of itself.
5033             *
5034             * @return this object
5035             */
5036            public AccessibleAction getAccessibleAction() {
5037                return this;
5038            }
5039
5040            /**
5041             * Get the AccessibleComponent associated with this object.  In the
5042             * implementation of the Java Accessibility API for this class,
5043             * return this object, which is responsible for implementing the
5044             * AccessibleComponent interface on behalf of itself.
5045             *
5046             * @return this object
5047             */
5048            public AccessibleComponent getAccessibleComponent() {
5049                return this; // to override getBounds()
5050            }
5051
5052            /**
5053             * Get the AccessibleSelection associated with this object if one
5054             * exists.  Otherwise return null.
5055             *
5056             * @return the AccessibleSelection, or null
5057             */
5058            public AccessibleSelection getAccessibleSelection() {
5059                AccessibleContext ac = getCurrentAccessibleContext();
5060                if (ac != null && isLeaf) {
5061                    return getCurrentAccessibleContext().getAccessibleSelection();
5062                } else {
5063                    return this;
5064                }
5065            }
5066
5067            /**
5068             * Get the AccessibleText associated with this object if one
5069             * exists.  Otherwise return null.
5070             *
5071             * @return the AccessibleText, or null
5072             */
5073            public AccessibleText getAccessibleText() {
5074                AccessibleContext ac = getCurrentAccessibleContext();
5075                if (ac != null) {
5076                    return getCurrentAccessibleContext().getAccessibleText();
5077                } else {
5078                    return null;
5079                }
5080            }
5081
5082            /**
5083             * Get the AccessibleValue associated with this object if one
5084             * exists.  Otherwise return null.
5085             *
5086             * @return the AccessibleValue, or null
5087             */
5088            public AccessibleValue getAccessibleValue() {
5089                AccessibleContext ac = getCurrentAccessibleContext();
5090                if (ac != null) {
5091                    return getCurrentAccessibleContext().getAccessibleValue();
5092                } else {
5093                    return null;
5094                }
5095            }
5096
5097
5098        // AccessibleComponent methods
5099
5100            /**
5101             * Get the background color of this object.
5102             *
5103             * @return the background color, if supported, of the object;
5104             * otherwise, null
5105             */
5106            public Color getBackground() {
5107                AccessibleContext ac = getCurrentAccessibleContext();
5108                if (ac instanceof AccessibleComponent) {
5109                    return ((AccessibleComponent) ac).getBackground();
5110                } else {
5111                    Component c = getCurrentComponent();
5112                    if (c != null) {
5113                        return c.getBackground();
5114                    } else {
5115                        return null;
5116                    }
5117                }
5118            }
5119
5120            /**
5121             * Set the background color of this object.
5122             *
5123             * @param c the new Color for the background
5124             */
5125            public void setBackground(Color c) {
5126                AccessibleContext ac = getCurrentAccessibleContext();
5127                if (ac instanceof AccessibleComponent) {
5128                    ((AccessibleComponent) ac).setBackground(c);
5129                } else {
5130                    Component cp = getCurrentComponent();
5131                    if (cp != null) {
5132                        cp.setBackground(c);
5133                    }
5134                }
5135            }
5136
5137
5138            /**
5139             * Get the foreground color of this object.
5140             *
5141             * @return the foreground color, if supported, of the object;
5142             * otherwise, null
5143             */
5144            public Color getForeground() {
5145                AccessibleContext ac = getCurrentAccessibleContext();
5146                if (ac instanceof AccessibleComponent) {
5147                    return ((AccessibleComponent) ac).getForeground();
5148                } else {
5149                    Component c = getCurrentComponent();
5150                    if (c != null) {
5151                        return c.getForeground();
5152                    } else {
5153                        return null;
5154                    }
5155                }
5156            }
5157
5158            public void setForeground(Color c) {
5159                AccessibleContext ac = getCurrentAccessibleContext();
5160                if (ac instanceof AccessibleComponent) {
5161                    ((AccessibleComponent) ac).setForeground(c);
5162                } else {
5163                    Component cp = getCurrentComponent();
5164                    if (cp != null) {
5165                        cp.setForeground(c);
5166                    }
5167                }
5168            }
5169
5170            public Cursor getCursor() {
5171                AccessibleContext ac = getCurrentAccessibleContext();
5172                if (ac instanceof AccessibleComponent) {
5173                    return ((AccessibleComponent) ac).getCursor();
5174                } else {
5175                    Component c = getCurrentComponent();
5176                    if (c != null) {
5177                        return c.getCursor();
5178                    } else {
5179                        Accessible ap = getAccessibleParent();
5180                        if (ap instanceof AccessibleComponent) {
5181                            return ((AccessibleComponent) ap).getCursor();
5182                        } else {
5183                            return null;
5184                        }
5185                    }
5186                }
5187            }
5188
5189            public void setCursor(Cursor c) {
5190                AccessibleContext ac = getCurrentAccessibleContext();
5191                if (ac instanceof AccessibleComponent) {
5192                    ((AccessibleComponent) ac).setCursor(c);
5193                } else {
5194                    Component cp = getCurrentComponent();
5195                    if (cp != null) {
5196                        cp.setCursor(c);
5197                    }
5198                }
5199            }
5200
5201            public Font getFont() {
5202                AccessibleContext ac = getCurrentAccessibleContext();
5203                if (ac instanceof AccessibleComponent) {
5204                    return ((AccessibleComponent) ac).getFont();
5205                } else {
5206                    Component c = getCurrentComponent();
5207                    if (c != null) {
5208                        return c.getFont();
5209                    } else {
5210                        return null;
5211                    }
5212                }
5213            }
5214
5215            public void setFont(Font f) {
5216                AccessibleContext ac = getCurrentAccessibleContext();
5217                if (ac instanceof AccessibleComponent) {
5218                    ((AccessibleComponent) ac).setFont(f);
5219                } else {
5220                    Component c = getCurrentComponent();
5221                    if (c != null) {
5222                        c.setFont(f);
5223                    }
5224                }
5225            }
5226
5227            public FontMetrics getFontMetrics(Font f) {
5228                AccessibleContext ac = getCurrentAccessibleContext();
5229                if (ac instanceof AccessibleComponent) {
5230                    return ((AccessibleComponent) ac).getFontMetrics(f);
5231                } else {
5232                    Component c = getCurrentComponent();
5233                    if (c != null) {
5234                        return c.getFontMetrics(f);
5235                    } else {
5236                        return null;
5237                    }
5238                }
5239            }
5240
5241            public boolean isEnabled() {
5242                AccessibleContext ac = getCurrentAccessibleContext();
5243                if (ac instanceof AccessibleComponent) {
5244                    return ((AccessibleComponent) ac).isEnabled();
5245                } else {
5246                    Component c = getCurrentComponent();
5247                    if (c != null) {
5248                        return c.isEnabled();
5249                    } else {
5250                        return false;
5251                    }
5252                }
5253            }
5254
5255            public void setEnabled(boolean b) {
5256                AccessibleContext ac = getCurrentAccessibleContext();
5257                if (ac instanceof AccessibleComponent) {
5258                    ((AccessibleComponent) ac).setEnabled(b);
5259                } else {
5260                    Component c = getCurrentComponent();
5261                    if (c != null) {
5262                        c.setEnabled(b);
5263                    }
5264                }
5265            }
5266
5267            public boolean isVisible() {
5268                Rectangle pathBounds = tree.getPathBounds(path);
5269                Rectangle parentBounds = tree.getVisibleRect();
5270                return pathBounds != null && parentBounds != null &&
5271                        parentBounds.intersects(pathBounds);
5272            }
5273
5274            public void setVisible(boolean b) {
5275            }
5276
5277            public boolean isShowing() {
5278                return (tree.isShowing() && isVisible());
5279            }
5280
5281            public boolean contains(Point p) {
5282                AccessibleContext ac = getCurrentAccessibleContext();
5283                if (ac instanceof AccessibleComponent) {
5284                    Rectangle r = ((AccessibleComponent) ac).getBounds();
5285                    return r.contains(p);
5286                } else {
5287                    Component c = getCurrentComponent();
5288                    if (c != null) {
5289                        Rectangle r = c.getBounds();
5290                        return r.contains(p);
5291                    } else {
5292                        return getBounds().contains(p);
5293                    }
5294                }
5295            }
5296
5297            public Point getLocationOnScreen() {
5298                if (tree != null) {
5299                    Point treeLocation = tree.getLocationOnScreen();
5300                    Rectangle pathBounds = tree.getPathBounds(path);
5301                    if (treeLocation != null && pathBounds != null) {
5302                        Point nodeLocation = new Point(pathBounds.x,
5303                                                       pathBounds.y);
5304                        nodeLocation.translate(treeLocation.x, treeLocation.y);
5305                        return nodeLocation;
5306                    } else {
5307                        return null;
5308                    }
5309                } else {
5310                    return null;
5311                }
5312            }
5313
5314            /**
5315             * Returns the relative location of the node
5316             *
5317             * @return the relative location of the node
5318             */
5319            protected Point getLocationInJTree() {
5320                Rectangle r = tree.getPathBounds(path);
5321                if (r != null) {
5322                    return r.getLocation();
5323                } else {
5324                    return null;
5325                }
5326            }
5327
5328            public Point getLocation() {
5329                Rectangle r = getBounds();
5330                if (r != null) {
5331                    return r.getLocation();
5332                } else {
5333                    return null;
5334                }
5335            }
5336
5337            public void setLocation(Point p) {
5338            }
5339
5340            public Rectangle getBounds() {
5341                Rectangle r = tree.getPathBounds(path);
5342                Accessible parent = getAccessibleParent();
5343                if (parent != null) {
5344                    if (parent instanceof AccessibleJTreeNode) {
5345                        Point parentLoc = ((AccessibleJTreeNode) parent).getLocationInJTree();
5346                        if (parentLoc != null && r != null) {
5347                            r.translate(-parentLoc.x, -parentLoc.y);
5348                        } else {
5349                            return null;        // not visible!
5350                        }
5351                    }
5352                }
5353                return r;
5354            }
5355
5356            public void setBounds(Rectangle r) {
5357                AccessibleContext ac = getCurrentAccessibleContext();
5358                if (ac instanceof AccessibleComponent) {
5359                    ((AccessibleComponent) ac).setBounds(r);
5360                } else {
5361                    Component c = getCurrentComponent();
5362                    if (c != null) {
5363                        c.setBounds(r);
5364                    }
5365                }
5366            }
5367
5368            public Dimension getSize() {
5369                return getBounds().getSize();
5370            }
5371
5372            public void setSize (Dimension d) {
5373                AccessibleContext ac = getCurrentAccessibleContext();
5374                if (ac instanceof AccessibleComponent) {
5375                    ((AccessibleComponent) ac).setSize(d);
5376                } else {
5377                    Component c = getCurrentComponent();
5378                    if (c != null) {
5379                        c.setSize(d);
5380                    }
5381                }
5382            }
5383
5384            /**
5385             * Returns the <code>Accessible</code> child, if one exists,
5386             * contained at the local coordinate <code>Point</code>.
5387             * Otherwise returns <code>null</code>.
5388             *
5389             * @param p point in local coordinates of this
5390             *    <code>Accessible</code>
5391             * @return the <code>Accessible</code>, if it exists,
5392             *    at the specified location; else <code>null</code>
5393             */
5394            public Accessible getAccessibleAt(Point p) {
5395                AccessibleContext ac = getCurrentAccessibleContext();
5396                if (ac instanceof AccessibleComponent) {
5397                    return ((AccessibleComponent) ac).getAccessibleAt(p);
5398                } else {
5399                    return null;
5400                }
5401            }
5402
5403            @SuppressWarnings("deprecation")
5404            public boolean isFocusTraversable() {
5405                AccessibleContext ac = getCurrentAccessibleContext();
5406                if (ac instanceof AccessibleComponent) {
5407                    return ((AccessibleComponent) ac).isFocusTraversable();
5408                } else {
5409                    Component c = getCurrentComponent();
5410                    if (c != null) {
5411                        return c.isFocusTraversable();
5412                    } else {
5413                        return false;
5414                    }
5415                }
5416            }
5417
5418            public void requestFocus() {
5419                AccessibleContext ac = getCurrentAccessibleContext();
5420                if (ac instanceof AccessibleComponent) {
5421                    ((AccessibleComponent) ac).requestFocus();
5422                } else {
5423                    Component c = getCurrentComponent();
5424                    if (c != null) {
5425                        c.requestFocus();
5426                    }
5427                }
5428            }
5429
5430            public void addFocusListener(FocusListener l) {
5431                AccessibleContext ac = getCurrentAccessibleContext();
5432                if (ac instanceof AccessibleComponent) {
5433                    ((AccessibleComponent) ac).addFocusListener(l);
5434                } else {
5435                    Component c = getCurrentComponent();
5436                    if (c != null) {
5437                        c.addFocusListener(l);
5438                    }
5439                }
5440            }
5441
5442            public void removeFocusListener(FocusListener l) {
5443                AccessibleContext ac = getCurrentAccessibleContext();
5444                if (ac instanceof AccessibleComponent) {
5445                    ((AccessibleComponent) ac).removeFocusListener(l);
5446                } else {
5447                    Component c = getCurrentComponent();
5448                    if (c != null) {
5449                        c.removeFocusListener(l);
5450                    }
5451                }
5452            }
5453
5454        // AccessibleSelection methods
5455
5456            /**
5457             * Returns the number of items currently selected.
5458             * If no items are selected, the return value will be 0.
5459             *
5460             * @return the number of items currently selected.
5461             */
5462            public int getAccessibleSelectionCount() {
5463                int count = 0;
5464                int childCount = getAccessibleChildrenCount();
5465                for (int i = 0; i < childCount; i++) {
5466                    TreePath childPath = getChildTreePath(i);
5467                    if (tree.isPathSelected(childPath)) {
5468                       count++;
5469                    }
5470                }
5471                return count;
5472            }
5473
5474            /**
5475             * Returns an Accessible representing the specified selected item
5476             * in the object.  If there isn't a selection, or there are
5477             * fewer items selected than the integer passed in, the return
5478             * value will be null.
5479             *
5480             * @param i the zero-based index of selected items
5481             * @return an Accessible containing the selected item
5482             */
5483            public Accessible getAccessibleSelection(int i) {
5484                int childCount = getAccessibleChildrenCount();
5485                if (i < 0 || i >= childCount) {
5486                    return null;        // out of range
5487                }
5488                int count = 0;
5489                for (int j = 0; j < childCount && i >= count; j++) {
5490                    TreePath childPath = getChildTreePath(j);
5491                    if (tree.isPathSelected(childPath)) {
5492                        if (count == i) {
5493                            return new AccessibleJTreeNode(tree, childPath, this);
5494                        } else {
5495                            count++;
5496                        }
5497                    }
5498                }
5499                return null;
5500            }
5501
5502            /**
5503             * Returns true if the current child of this object is selected.
5504             *
5505             * @param i the zero-based index of the child in this Accessible
5506             * object.
5507             * @see AccessibleContext#getAccessibleChild
5508             */
5509            public boolean isAccessibleChildSelected(int i) {
5510                int childCount = getAccessibleChildrenCount();
5511                if (i < 0 || i >= childCount) {
5512                    return false;       // out of range
5513                } else {
5514                    TreePath childPath = getChildTreePath(i);
5515                    return tree.isPathSelected(childPath);
5516                }
5517            }
5518
5519            /**
5520             * Adds the specified selected item in the object to the object's
5521             * selection.  If the object supports multiple selections,
5522             * the specified item is added to any existing selection, otherwise
5523             * it replaces any existing selection in the object.  If the
5524             * specified item is already selected, this method has no effect.
5525             *
5526             * @param i the zero-based index of selectable items
5527             */
5528            public void addAccessibleSelection(int i) {
5529               TreeModel model = JTree.this.getModel();
5530               if (model != null) {
5531                   if (i >= 0 && i < getAccessibleChildrenCount()) {
5532                       TreePath path = getChildTreePath(i);
5533                       JTree.this.addSelectionPath(path);
5534                    }
5535                }
5536            }
5537
5538            /**
5539             * Removes the specified selected item in the object from the
5540             * object's
5541             * selection.  If the specified item isn't currently selected, this
5542             * method has no effect.
5543             *
5544             * @param i the zero-based index of selectable items
5545             */
5546            public void removeAccessibleSelection(int i) {
5547               TreeModel model = JTree.this.getModel();
5548               if (model != null) {
5549                   if (i >= 0 && i < getAccessibleChildrenCount()) {
5550                       TreePath path = getChildTreePath(i);
5551                       JTree.this.removeSelectionPath(path);
5552                    }
5553                }
5554            }
5555
5556            /**
5557             * Clears the selection in the object, so that nothing in the
5558             * object is selected.
5559             */
5560            public void clearAccessibleSelection() {
5561                int childCount = getAccessibleChildrenCount();
5562                for (int i = 0; i < childCount; i++) {
5563                    removeAccessibleSelection(i);
5564                }
5565            }
5566
5567            /**
5568             * Causes every selected item in the object to be selected
5569             * if the object supports multiple selections.
5570             */
5571            public void selectAllAccessibleSelection() {
5572               TreeModel model = JTree.this.getModel();
5573               if (model != null) {
5574                   int childCount = getAccessibleChildrenCount();
5575                   TreePath path;
5576                   for (int i = 0; i < childCount; i++) {
5577                       path = getChildTreePath(i);
5578                       JTree.this.addSelectionPath(path);
5579                   }
5580                }
5581            }
5582
5583        // AccessibleAction methods
5584
5585            /**
5586             * Returns the number of accessible actions available in this
5587             * tree node.  If this node is not a leaf, there is at least
5588             * one action (toggle expand), in addition to any available
5589             * on the object behind the TreeCellRenderer.
5590             *
5591             * @return the number of Actions in this object
5592             */
5593            public int getAccessibleActionCount() {
5594                AccessibleContext ac = getCurrentAccessibleContext();
5595                if (ac != null) {
5596                    AccessibleAction aa = ac.getAccessibleAction();
5597                    if (aa != null) {
5598                        return (aa.getAccessibleActionCount() + (isLeaf ? 0 : 1));
5599                    }
5600                }
5601                return isLeaf ? 0 : 1;
5602            }
5603
5604            /**
5605             * Return a description of the specified action of the tree node.
5606             * If this node is not a leaf, there is at least one action
5607             * description (toggle expand), in addition to any available
5608             * on the object behind the TreeCellRenderer.
5609             *
5610             * @param i zero-based index of the actions
5611             * @return a description of the action
5612             */
5613            public String getAccessibleActionDescription(int i) {
5614                if (i < 0 || i >= getAccessibleActionCount()) {
5615                    return null;
5616                }
5617                AccessibleContext ac = getCurrentAccessibleContext();
5618                if (i == 0) {
5619                    // TIGER - 4766636
5620                    return AccessibleAction.TOGGLE_EXPAND;
5621                } else if (ac != null) {
5622                    AccessibleAction aa = ac.getAccessibleAction();
5623                    if (aa != null) {
5624                        return aa.getAccessibleActionDescription(i - 1);
5625                    }
5626                }
5627                return null;
5628            }
5629
5630            /**
5631             * Perform the specified Action on the tree node.  If this node
5632             * is not a leaf, there is at least one action which can be
5633             * done (toggle expand), in addition to any available on the
5634             * object behind the TreeCellRenderer.
5635             *
5636             * @param i zero-based index of actions
5637             * @return true if the action was performed; else false.
5638             */
5639            public boolean doAccessibleAction(int i) {
5640                if (i < 0 || i >= getAccessibleActionCount()) {
5641                    return false;
5642                }
5643                AccessibleContext ac = getCurrentAccessibleContext();
5644                if (i == 0) {
5645                    if (JTree.this.isExpanded(path)) {
5646                        JTree.this.collapsePath(path);
5647                    } else {
5648                        JTree.this.expandPath(path);
5649                    }
5650                    return true;
5651                } else if (ac != null) {
5652                    AccessibleAction aa = ac.getAccessibleAction();
5653                    if (aa != null) {
5654                        return aa.doAccessibleAction(i - 1);
5655                    }
5656                }
5657                return false;
5658            }
5659
5660        } // inner class AccessibleJTreeNode
5661
5662    }  // inner class AccessibleJTree
5663
5664} // End of class JTree
5665