Source Code Cross Referenced for JTree.java in  » 6.0-JDK-Core » swing » javax » swing » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » swing » javax.swing 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025
0026        package javax.swing;
0027
0028        import java.awt.*;
0029        import java.awt.event.*;
0030        import java.beans.*;
0031        import java.io.*;
0032        import java.util.*;
0033        import javax.swing.event.*;
0034        import javax.swing.plaf.*;
0035        import javax.swing.tree.*;
0036        import javax.swing.text.Position;
0037        import javax.accessibility.*;
0038        import sun.swing.SwingUtilities2;
0039        import sun.swing.SwingUtilities2.Section;
0040        import static sun.swing.SwingUtilities2.Section.*;
0041
0042        /**
0043         * <a name="jtree_description">
0044         * A control that displays a set of hierarchical data as an outline.
0045         * You can find task-oriented documentation and examples of using trees in
0046         * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>,
0047         * a section in <em>The Java Tutorial.</em>
0048         * <p>
0049         * A specific node in a tree can be identified either by a
0050         * <code>TreePath</code> (an object
0051         * that encapsulates a node and all of its ancestors), or by its
0052         * display row, where each row in the display area displays one node.
0053         * An <i>expanded</i> node is a non-leaf node (as identified by
0054         * <code>TreeModel.isLeaf(node)</code> returning false) that will displays
0055         * its children when all its ancestors are <i>expanded</i>.
0056         * A <i>collapsed</i>
0057         * node is one which hides them. A <i>hidden</i> node is one which is
0058         * under a collapsed ancestor. All of a <i>viewable</i> nodes parents
0059         * are expanded, but may or may not be displayed. A <i>displayed</i> node
0060         * is both viewable and in the display area, where it can be seen.
0061         * <p>
0062         * The following <code>JTree</code> methods use "visible" to mean "displayed":
0063         * <ul>
0064         * <li><code>isRootVisible()</code>
0065         * <li><code>setRootVisible()</code>
0066         * <li><code>scrollPathToVisible()</code>
0067         * <li><code>scrollRowToVisible()</code>
0068         * <li><code>getVisibleRowCount()</code>
0069         * <li><code>setVisibleRowCount()</code>
0070         * </ul>
0071         * <p>
0072         * The next group of <code>JTree</code> methods use "visible" to mean
0073         * "viewable" (under an expanded parent):
0074         * <ul>
0075         * <li><code>isVisible()</code>
0076         * <li><code>makeVisible()</code>
0077         * </ul>
0078         * <p>
0079         * If you are interested in knowing when the selection changes implement
0080         * the <code>TreeSelectionListener</code> interface and add the instance
0081         * using the method <code>addTreeSelectionListener</code>.
0082         * <code>valueChanged</code> will be invoked when the
0083         * selection changes, that is if the user clicks twice on the same
0084         * node <code>valueChanged</code> will only be invoked once.
0085         * <p>
0086         * If you are interested in detecting either double-click events or when
0087         * a user clicks on a node, regardless of whether or not it was selected,
0088         * we recommend you do the following:
0089         * <pre>
0090         * final JTree tree = ...;
0091         *
0092         * MouseListener ml = new MouseAdapter() {
0093         *     public void <b>mousePressed</b>(MouseEvent e) {
0094         *         int selRow = tree.getRowForLocation(e.getX(), e.getY());
0095         *         TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
0096         *         if(selRow != -1) {
0097         *             if(e.getClickCount() == 1) {
0098         *                 mySingleClick(selRow, selPath);
0099         *             }
0100         *             else if(e.getClickCount() == 2) {
0101         *                 myDoubleClick(selRow, selPath);
0102         *             }
0103         *         }
0104         *     }
0105         * };
0106         * tree.addMouseListener(ml);
0107         * </pre>
0108         * NOTE: This example obtains both the path and row, but you only need to
0109         * get the one you're interested in.
0110         * <p>
0111         * To use <code>JTree</code> to display compound nodes
0112         * (for example, nodes containing both
0113         * a graphic icon and text), subclass {@link TreeCellRenderer} and use 
0114         * {@link #setCellRenderer} to tell the tree to use it. To edit such nodes,
0115         * subclass {@link TreeCellEditor} and use {@link #setCellEditor}.
0116         * <p>
0117         * Like all <code>JComponent</code> classes, you can use {@link InputMap} and
0118         * {@link ActionMap}
0119         * to associate an {@link Action} object with a {@link KeyStroke}
0120         * and execute the action under specified conditions.
0121         * <p>
0122         * <strong>Warning:</strong> Swing is not thread safe. For more
0123         * information see <a
0124         * href="package-summary.html#threading">Swing's Threading
0125         * Policy</a>.
0126         * <p>
0127         * <strong>Warning:</strong>
0128         * Serialized objects of this class will not be compatible with
0129         * future Swing releases. The current serialization support is
0130         * appropriate for short term storage or RMI between applications running
0131         * the same version of Swing.  As of 1.4, support for long term storage
0132         * of all JavaBeans<sup><font size="-2">TM</font></sup>
0133         * has been added to the <code>java.beans</code> package.
0134         * Please see {@link java.beans.XMLEncoder}.
0135         *
0136         * @beaninfo
0137         *   attribute: isContainer false
0138         * description: A component that displays a set of hierarchical data as an outline.
0139         *
0140         * @version 1.203, 05/05/07
0141         * @author Rob Davis
0142         * @author Ray Ryan
0143         * @author Scott Violet
0144         */
0145        public class JTree extends JComponent implements  Scrollable, Accessible {
0146            /**
0147             * @see #getUIClassID
0148             * @see #readObject
0149             */
0150            private static final String uiClassID = "TreeUI";
0151
0152            /**
0153             * The model that defines the tree displayed by this object.
0154             */
0155            transient protected TreeModel treeModel;
0156
0157            /**
0158             * Models the set of selected nodes in this tree.
0159             */
0160            transient protected TreeSelectionModel selectionModel;
0161
0162            /**
0163             * True if the root node is displayed, false if its children are
0164             * the highest visible nodes.
0165             */
0166            protected boolean rootVisible;
0167
0168            /**
0169             * The cell used to draw nodes. If <code>null</code>, the UI uses a default
0170             * <code>cellRenderer</code>.
0171             */
0172            transient protected TreeCellRenderer cellRenderer;
0173
0174            /**
0175             * Height to use for each display row. If this is <= 0 the renderer 
0176             * determines the height for each row.
0177             */
0178            protected int rowHeight;
0179            private boolean rowHeightSet = false;
0180
0181            /**
0182             * Maps from <code>TreePath</code> to <code>Boolean</code>
0183             * indicating whether or not the
0184             * particular path is expanded. This ONLY indicates whether a 
0185             * given path is expanded, and NOT if it is visible or not. That
0186             * information must be determined by visiting all the parent
0187             * paths and seeing if they are visible.
0188             */
0189            transient private Hashtable expandedState;
0190
0191            /**
0192             * True if handles are displayed at the topmost level of the tree.
0193             * <p>
0194             * A handle is a small icon that displays adjacent to the node which 
0195             * allows the user to click once to expand or collapse the node. A
0196             * common interface shows a plus sign (+) for a node which can be
0197             * expanded and a minus sign (-) for a node which can be collapsed.
0198             * Handles are always shown for nodes below the topmost level.
0199             * <p>
0200             * If the <code>rootVisible</code> setting specifies that the root 
0201             * node is to be displayed, then that is the only node at the topmost
0202             * level. If the root node is not displayed, then all of its 
0203             * children are at the topmost level of the tree. Handles are 
0204             * always displayed for nodes other than the topmost.
0205             * <p> 
0206             * If the root node isn't visible, it is generally a good to make 
0207             * this value true. Otherwise, the tree looks exactly like a list,
0208             * and users may not know that the "list entries" are actually
0209             * tree nodes.
0210             *
0211             * @see #rootVisible
0212             */
0213            protected boolean showsRootHandles;
0214            private boolean showsRootHandlesSet = false;
0215
0216            /**
0217             * Creates a new event and passed it off the
0218             * <code>selectionListeners</code>.
0219             */
0220            protected transient TreeSelectionRedirector selectionRedirector;
0221
0222            /**
0223             * Editor for the entries.  Default is <code>null</code>
0224             * (tree is not editable).
0225             */
0226            transient protected TreeCellEditor cellEditor;
0227
0228            /**
0229             * Is the tree editable? Default is false.
0230             */
0231            protected boolean editable;
0232
0233            /**
0234             * Is this tree a large model? This is a code-optimization setting.
0235             * A large model can be used when the cell height is the same for all
0236             * nodes. The UI will then cache very little information and instead
0237             * continually message the model. Without a large model the UI caches 
0238             * most of the information, resulting in fewer method calls to the model.
0239             * <p>
0240             * This value is only a suggestion to the UI. Not all UIs will
0241             * take advantage of it. Default value is false.
0242             */
0243            protected boolean largeModel;
0244
0245            /**
0246             * Number of rows to make visible at one time. This value is used for
0247             * the <code>Scrollable</code> interface. It determines the preferred
0248             * size of the display area.
0249             */
0250            protected int visibleRowCount;
0251
0252            /**
0253             * If true, when editing is to be stopped by way of selection changing,
0254             * data in tree changing or other means <code>stopCellEditing</code>
0255             * is invoked, and changes are saved. If false,
0256             * <code>cancelCellEditing</code> is invoked, and changes
0257             * are discarded. Default is false.
0258             */
0259            protected boolean invokesStopCellEditing;
0260
0261            /**
0262             * If true, when a node is expanded, as many of the descendants are 
0263             * scrolled to be visible.
0264             */
0265            protected boolean scrollsOnExpand;
0266            private boolean scrollsOnExpandSet = false;
0267
0268            /**
0269             * Number of mouse clicks before a node is expanded.
0270             */
0271            protected int toggleClickCount;
0272
0273            /**
0274             * Updates the <code>expandedState</code>.
0275             */
0276            transient protected TreeModelListener treeModelListener;
0277
0278            /**
0279             * Used when <code>setExpandedState</code> is invoked,
0280             * will be a <code>Stack</code> of <code>Stack</code>s.
0281             */
0282            transient private Stack expandedStack;
0283
0284            /**
0285             * Lead selection path, may not be <code>null</code>.
0286             */
0287            private TreePath leadPath;
0288
0289            /**
0290             * Anchor path.
0291             */
0292            private TreePath anchorPath;
0293
0294            /**
0295             * True if paths in the selection should be expanded.
0296             */
0297            private boolean expandsSelectedPaths;
0298
0299            /**
0300             * This is set to true for the life of the <code>setUI</code> call.
0301             */
0302            private boolean settingUI;
0303
0304            /** If true, mouse presses on selections initiate a drag operation. */
0305            private boolean dragEnabled;
0306
0307            /**
0308             * The drop mode for this component.
0309             */
0310            private DropMode dropMode = DropMode.USE_SELECTION;
0311
0312            /**
0313             * The drop location.
0314             */
0315            private transient DropLocation dropLocation;
0316
0317            /**
0318             * A subclass of <code>TransferHandler.DropLocation</code> representing
0319             * a drop location for a <code>JTree</code>.
0320             *
0321             * @see #getDropLocation
0322             * @since 1.6
0323             */
0324            public static final class DropLocation extends
0325                    TransferHandler.DropLocation {
0326                private final TreePath path;
0327                private final int index;
0328
0329                private DropLocation(Point p, TreePath path, int index) {
0330                    super (p);
0331                    this .path = path;
0332                    this .index = index;
0333                }
0334
0335                /**
0336                 * Returns the index where the dropped data should be inserted
0337                 * with respect to the path returned by <code>getPath()</code>.
0338                 * <p>
0339                 * For drop modes <code>DropMode.USE_SELECTION</code> and
0340                 * <code>DropMode.ON</code>, this index is unimportant (and it will
0341                 * always be <code>-1</code>) as the only interesting data is the
0342                 * path over which the drop operation occurred.
0343                 * <p>
0344                 * For drop mode <code>DropMode.INSERT</code>, this index
0345                 * indicates the index at which the data should be inserted into
0346                 * the parent path represented by <code>getPath()</code>.
0347                 * <code>-1</code> indicates that the drop occurred over the
0348                 * parent itself, and in most cases should be treated as inserting
0349                 * into either the beginning or the end of the parent's list of
0350                 * children.
0351                 * <p>
0352                 * For <code>DropMode.ON_OR_INSERT</code>, this value will be
0353                 * an insert index, as described above, or <code>-1</code> if
0354                 * the drop occurred over the path itself.
0355                 *
0356                 * @return the child index
0357                 * @see #getPath
0358                 */
0359                public int getChildIndex() {
0360                    return index;
0361                }
0362
0363                /**
0364                 * Returns the path where dropped data should be placed in the
0365                 * tree.
0366                 * <p>
0367                 * Interpretation of this value depends on the drop mode set on the
0368                 * component. If the drop mode is <code>DropMode.USE_SELECTION</code>
0369                 * or <code>DropMode.ON</code>, the return value is the path in the
0370                 * tree over which the data has been (or will be) dropped.
0371                 * <code>null</code> indicates that the drop is over empty space,
0372                 * not associated with a particular path.
0373                 * <p>
0374                 * If the drop mode is <code>DropMode.INSERT</code>, the return value
0375                 * refers to the path that should become the parent of the new data,
0376                 * in which case <code>getChildIndex()</code> indicates where the
0377                 * new item should be inserted into this parent path. A
0378                 * <code>null</code> path indicates that no parent path has been
0379                 * determined, which can happen for multiple reasons:
0380                 * <ul>
0381                 *    <li>The tree has no model
0382                 *    <li>There is no root in the tree
0383                 *    <li>The root is collapsed
0384                 *    <li>The root is a leaf node
0385                 * </ul>
0386                 * It is up to the developer to decide if and how they wish to handle
0387                 * the <code>null</code> case. 
0388                 * <p>
0389                 * If the drop mode is <code>DropMode.ON_OR_INSERT</code>,
0390                 * <code>getChildIndex</code> can be used to determine whether the
0391                 * drop is on top of the path itself (<code>-1</code>) or the index
0392                 * at which it should be inserted into the path (values other than
0393                 * <code>-1</code>).
0394                 *
0395                 * @return the drop path
0396                 * @see #getChildIndex
0397                 */
0398                public TreePath getPath() {
0399                    return path;
0400                }
0401
0402                /**
0403                 * Returns a string representation of this drop location.
0404                 * This method is intended to be used for debugging purposes,
0405                 * and the content and format of the returned string may vary
0406                 * between implementations.
0407                 *
0408                 * @return a string representation of this drop location
0409                 */
0410                public String toString() {
0411                    return getClass().getName() + "[dropPoint="
0412                            + getDropPoint() + "," + "path=" + path + ","
0413                            + "childIndex=" + index + "]";
0414                }
0415            }
0416
0417            /**
0418             * The row to expand during DnD.
0419             */
0420            private int expandRow = -1;
0421
0422            private class TreeTimer extends Timer {
0423                public TreeTimer() {
0424                    super (2000, null);
0425                    setRepeats(false);
0426                }
0427
0428                public void fireActionPerformed(ActionEvent ae) {
0429                    JTree.this .expandRow(expandRow);
0430                }
0431            }
0432
0433            /**
0434             * A timer to expand nodes during drop.
0435             */
0436            private TreeTimer dropTimer;
0437
0438            /**
0439             * When <code>addTreeExpansionListener</code> is invoked,
0440             * and <code>settingUI</code> is true, this ivar gets set to the passed in
0441             * <code>Listener</code>. This listener is then notified first in
0442             * <code>fireTreeCollapsed</code> and <code>fireTreeExpanded</code>.
0443             * <p>This is an ugly workaround for a way to have the UI listener
0444             * get notified before other listeners.
0445             */
0446            private transient TreeExpansionListener uiTreeExpansionListener;
0447
0448            /**
0449             * Max number of stacks to keep around.
0450             */
0451            private static int TEMP_STACK_SIZE = 11;
0452
0453            //
0454            // Bound property names
0455            //
0456            /** Bound property name for <code>cellRenderer</code>. */
0457            public final static String CELL_RENDERER_PROPERTY = "cellRenderer";
0458            /** Bound property name for <code>treeModel</code>. */
0459            public final static String TREE_MODEL_PROPERTY = "model";
0460            /** Bound property name for <code>rootVisible</code>. */
0461            public final static String ROOT_VISIBLE_PROPERTY = "rootVisible";
0462            /** Bound property name for <code>showsRootHandles</code>. */
0463            public final static String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
0464            /** Bound property name for <code>rowHeight</code>. */
0465            public final static String ROW_HEIGHT_PROPERTY = "rowHeight";
0466            /** Bound property name for <code>cellEditor</code>. */
0467            public final static String CELL_EDITOR_PROPERTY = "cellEditor";
0468            /** Bound property name for <code>editable</code>. */
0469            public final static String EDITABLE_PROPERTY = "editable";
0470            /** Bound property name for <code>largeModel</code>. */
0471            public final static String LARGE_MODEL_PROPERTY = "largeModel";
0472            /** Bound property name for selectionModel. */
0473            public final static String SELECTION_MODEL_PROPERTY = "selectionModel";
0474            /** Bound property name for <code>visibleRowCount</code>. */
0475            public final static String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
0476            /** Bound property name for <code>messagesStopCellEditing</code>. */
0477            public final static String INVOKES_STOP_CELL_EDITING_PROPERTY = "invokesStopCellEditing";
0478            /** Bound property name for <code>scrollsOnExpand</code>. */
0479            public final static String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
0480            /** Bound property name for <code>toggleClickCount</code>. */
0481            public final static String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
0482            /** Bound property name for <code>leadSelectionPath</code>.
0483             * @since 1.3 */
0484            public final static String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
0485            /** Bound property name for anchor selection path.
0486             * @since 1.3 */
0487            public final static String ANCHOR_SELECTION_PATH_PROPERTY = "anchorSelectionPath";
0488            /** Bound property name for expands selected paths property
0489             * @since 1.3 */
0490            public final static String EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths";
0491
0492            /**
0493             * Creates and returns a sample <code>TreeModel</code>.
0494             * Used primarily for beanbuilders to show something interesting.
0495             *
0496             * @return the default <code>TreeModel</code>
0497             */
0498            protected static TreeModel getDefaultTreeModel() {
0499                DefaultMutableTreeNode root = new DefaultMutableTreeNode(
0500                        "JTree");
0501                DefaultMutableTreeNode parent;
0502
0503                parent = new DefaultMutableTreeNode("colors");
0504                root.add(parent);
0505                parent.add(new DefaultMutableTreeNode("blue"));
0506                parent.add(new DefaultMutableTreeNode("violet"));
0507                parent.add(new DefaultMutableTreeNode("red"));
0508                parent.add(new DefaultMutableTreeNode("yellow"));
0509
0510                parent = new DefaultMutableTreeNode("sports");
0511                root.add(parent);
0512                parent.add(new DefaultMutableTreeNode("basketball"));
0513                parent.add(new DefaultMutableTreeNode("soccer"));
0514                parent.add(new DefaultMutableTreeNode("football"));
0515                parent.add(new DefaultMutableTreeNode("hockey"));
0516
0517                parent = new DefaultMutableTreeNode("food");
0518                root.add(parent);
0519                parent.add(new DefaultMutableTreeNode("hot dogs"));
0520                parent.add(new DefaultMutableTreeNode("pizza"));
0521                parent.add(new DefaultMutableTreeNode("ravioli"));
0522                parent.add(new DefaultMutableTreeNode("bananas"));
0523                return new DefaultTreeModel(root);
0524            }
0525
0526            /**
0527             * Returns a <code>TreeModel</code> wrapping the specified object.
0528             * If the object is:<ul>
0529             * <li>an array of <code>Object</code>s,
0530             * <li>a <code>Hashtable</code>, or
0531             * <li>a <code>Vector</code>
0532             * </ul>then a new root node is created with each of the incoming 
0533             * objects as children. Otherwise, a new root is created with
0534             * a value of {@code "root"}.
0535             *
0536             * @param value  the <code>Object</code> used as the foundation for
0537             *		the <code>TreeModel</code>
0538             * @return a <code>TreeModel</code> wrapping the specified object
0539             */
0540            protected static TreeModel createTreeModel(Object value) {
0541                DefaultMutableTreeNode root;
0542
0543                if ((value instanceof  Object[]) || (value instanceof  Hashtable)
0544                        || (value instanceof  Vector)) {
0545                    root = new DefaultMutableTreeNode("root");
0546                    DynamicUtilTreeNode.createChildren(root, value);
0547                } else {
0548                    root = new DynamicUtilTreeNode("root", value);
0549                }
0550                return new DefaultTreeModel(root, false);
0551            }
0552
0553            /**
0554             * Returns a <code>JTree</code> with a sample model.
0555             * The default model used by the tree defines a leaf node as any node
0556             * without children.
0557             *
0558             * @see DefaultTreeModel#asksAllowsChildren
0559             */
0560            public JTree() {
0561                this (getDefaultTreeModel());
0562            }
0563
0564            /**
0565             * Returns a <code>JTree</code> with each element of the
0566             * specified array as the
0567             * child of a new root node which is not displayed.
0568             * By default, the tree defines a leaf node as any node without
0569             * children.
0570             *
0571             * @param value  an array of <code>Object</code>s
0572             * @see DefaultTreeModel#asksAllowsChildren
0573             */
0574            public JTree(Object[] value) {
0575                this (createTreeModel(value));
0576                this .setRootVisible(false);
0577                this .setShowsRootHandles(true);
0578                expandRoot();
0579            }
0580
0581            /**
0582             * Returns a <code>JTree</code> with each element of the specified
0583             * <code>Vector</code> as the
0584             * child of a new root node which is not displayed. By default, the
0585             * tree defines a leaf node as any node without children.
0586             *
0587             * @param value  a <code>Vector</code>
0588             * @see DefaultTreeModel#asksAllowsChildren
0589             */
0590            public JTree(Vector<?> value) {
0591                this (createTreeModel(value));
0592                this .setRootVisible(false);
0593                this .setShowsRootHandles(true);
0594                expandRoot();
0595            }
0596
0597            /**
0598             * Returns a <code>JTree</code> created from a <code>Hashtable</code>
0599             * which does not display with root.
0600             * Each value-half of the key/value pairs in the <code>HashTable</code>
0601             * becomes a child of the new root node. By default, the tree defines
0602             * a leaf node as any node without children.
0603             *
0604             * @param value  a <code>Hashtable</code>
0605             * @see DefaultTreeModel#asksAllowsChildren
0606             */
0607            public JTree(Hashtable<?, ?> value) {
0608                this (createTreeModel(value));
0609                this .setRootVisible(false);
0610                this .setShowsRootHandles(true);
0611                expandRoot();
0612            }
0613
0614            /**
0615             * Returns a <code>JTree</code> with the specified
0616             * <code>TreeNode</code> as its root,
0617             * which displays the root node.
0618             * By default, the tree defines a leaf node as any node without children.
0619             *
0620             * @param root  a <code>TreeNode</code> object
0621             * @see DefaultTreeModel#asksAllowsChildren
0622             */
0623            public JTree(TreeNode root) {
0624                this (root, false);
0625            }
0626
0627            /**
0628             * Returns a <code>JTree</code> with the specified <code>TreeNode</code>
0629             * as its root, which 
0630             * displays the root node and which decides whether a node is a 
0631             * leaf node in the specified manner.
0632             *
0633             * @param root  a <code>TreeNode</code> object
0634             * @param asksAllowsChildren  if false, any node without children is a 
0635             *              leaf node; if true, only nodes that do not allow 
0636             *              children are leaf nodes
0637             * @see DefaultTreeModel#asksAllowsChildren
0638             */
0639            public JTree(TreeNode root, boolean asksAllowsChildren) {
0640                this (new DefaultTreeModel(root, asksAllowsChildren));
0641            }
0642
0643            /**
0644             * Returns an instance of <code>JTree</code> which displays the root node 
0645             * -- the tree is created using the specified data model.
0646             *
0647             * @param newModel  the <code>TreeModel</code> to use as the data model
0648             */
0649            @ConstructorProperties({"model"})
0650            public JTree(TreeModel newModel) {
0651                super ();
0652                expandedStack = new Stack();
0653                toggleClickCount = 2;
0654                expandedState = new Hashtable();
0655                setLayout(null);
0656                rowHeight = 16;
0657                visibleRowCount = 20;
0658                rootVisible = true;
0659                selectionModel = new DefaultTreeSelectionModel();
0660                cellRenderer = null;
0661                scrollsOnExpand = true;
0662                setOpaque(true);
0663                expandsSelectedPaths = true;
0664                updateUI();
0665                setModel(newModel);
0666            }
0667
0668            /**
0669             * Returns the L&F object that renders this component.
0670             *
0671             * @return the <code>TreeUI</code> object that renders this component
0672             */
0673            public TreeUI getUI() {
0674                return (TreeUI) ui;
0675            }
0676
0677            /**
0678             * Sets the L&F object that renders this component.
0679             * <p>
0680             * This is a bound property.  
0681             *
0682             * @param ui  the <code>TreeUI</code> L&F object
0683             * @see UIDefaults#getUI
0684             * @beaninfo
0685             *        bound: true
0686             *       hidden: true
0687             *    attribute: visualUpdate true
0688             *  description: The UI object that implements the Component's LookAndFeel. 
0689             */
0690            public void setUI(TreeUI ui) {
0691                if ((TreeUI) this .ui != ui) {
0692                    settingUI = true;
0693                    uiTreeExpansionListener = null;
0694                    try {
0695                        super .setUI(ui);
0696                    } finally {
0697                        settingUI = false;
0698                    }
0699                }
0700            }
0701
0702            /**
0703             * Notification from the <code>UIManager</code> that the L&F has changed. 
0704             * Replaces the current UI object with the latest version from the 
0705             * <code>UIManager</code>.
0706             *
0707             * @see JComponent#updateUI
0708             */
0709            public void updateUI() {
0710                setUI((TreeUI) UIManager.getUI(this ));
0711
0712                SwingUtilities.updateRendererOrEditorUI(getCellRenderer());
0713                SwingUtilities.updateRendererOrEditorUI(getCellEditor());
0714            }
0715
0716            /**
0717             * Returns the name of the L&F class that renders this component.
0718             *
0719             * @return the string "TreeUI"
0720             * @see JComponent#getUIClassID
0721             * @see UIDefaults#getUI
0722             */
0723            public String getUIClassID() {
0724                return uiClassID;
0725            }
0726
0727            /**
0728             * Returns the current <code>TreeCellRenderer</code>
0729             *  that is rendering each cell.
0730             *
0731             * @return the <code>TreeCellRenderer</code> that is rendering each cell
0732             */
0733            public TreeCellRenderer getCellRenderer() {
0734                return cellRenderer;
0735            }
0736
0737            /**
0738             * Sets the <code>TreeCellRenderer</code> that will be used to
0739             * draw each cell.
0740             * <p>
0741             * This is a bound property.  
0742             *
0743             * @param x  the <code>TreeCellRenderer</code> that is to render each cell
0744             * @beaninfo
0745             *        bound: true
0746             *  description: The TreeCellRenderer that will be used to draw
0747             *               each cell.
0748             */
0749            public void setCellRenderer(TreeCellRenderer x) {
0750                TreeCellRenderer oldValue = cellRenderer;
0751
0752                cellRenderer = x;
0753                firePropertyChange(CELL_RENDERER_PROPERTY, oldValue,
0754                        cellRenderer);
0755                invalidate();
0756            }
0757
0758            /**
0759             * Determines whether the tree is editable. Fires a property
0760             * change event if the new setting is different from the existing
0761             * setting.
0762             * <p>
0763             * This is a bound property.  
0764             *
0765             * @param flag  a boolean value, true if the tree is editable
0766             * @beaninfo
0767             *        bound: true
0768             *  description: Whether the tree is editable.
0769             */
0770            public void setEditable(boolean flag) {
0771                boolean oldValue = this .editable;
0772
0773                this .editable = flag;
0774                firePropertyChange(EDITABLE_PROPERTY, oldValue, flag);
0775                if (accessibleContext != null) {
0776                    accessibleContext.firePropertyChange(
0777                            AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
0778                            (oldValue ? AccessibleState.EDITABLE : null),
0779                            (flag ? AccessibleState.EDITABLE : null));
0780                }
0781            }
0782
0783            /**
0784             * Returns true if the tree is editable.
0785             *
0786             * @return true if the tree is editable
0787             */
0788            public boolean isEditable() {
0789                return editable;
0790            }
0791
0792            /**
0793             * Sets the cell editor.  A <code>null</code> value implies that the
0794             * tree cannot be edited.  If this represents a change in the
0795             * <code>cellEditor</code>, the <code>propertyChange</code>
0796             * method is invoked on all listeners.
0797             * <p>
0798             * This is a bound property.  
0799             *
0800             * @param cellEditor the <code>TreeCellEditor</code> to use
0801             * @beaninfo
0802             *        bound: true
0803             *  description: The cell editor. A null value implies the tree
0804             *               cannot be edited.
0805             */
0806            public void setCellEditor(TreeCellEditor cellEditor) {
0807                TreeCellEditor oldEditor = this .cellEditor;
0808
0809                this .cellEditor = cellEditor;
0810                firePropertyChange(CELL_EDITOR_PROPERTY, oldEditor, cellEditor);
0811                invalidate();
0812            }
0813
0814            /**
0815             * Returns the editor used to edit entries in the tree.
0816             *
0817             * @return the <code>TreeCellEditor</code> in use,
0818             *		or <code>null</code> if the tree cannot be edited
0819             */
0820            public TreeCellEditor getCellEditor() {
0821                return cellEditor;
0822            }
0823
0824            /**
0825             * Returns the <code>TreeModel</code> that is providing the data.
0826             *
0827             * @return the <code>TreeModel</code> that is providing the data
0828             */
0829            public TreeModel getModel() {
0830                return treeModel;
0831            }
0832
0833            /**
0834             * Sets the <code>TreeModel</code> that will provide the data.
0835             * <p>
0836             * This is a bound property.  
0837             *
0838             * @param newModel the <code>TreeModel</code> that is to provide the data
0839             * @beaninfo
0840             *        bound: true
0841             *  description: The TreeModel that will provide the data.
0842             */
0843            public void setModel(TreeModel newModel) {
0844                clearSelection();
0845
0846                TreeModel oldModel = treeModel;
0847
0848                if (treeModel != null && treeModelListener != null)
0849                    treeModel.removeTreeModelListener(treeModelListener);
0850
0851                if (accessibleContext != null) {
0852                    if (treeModel != null) {
0853                        treeModel
0854                                .removeTreeModelListener((TreeModelListener) accessibleContext);
0855                    }
0856                    if (newModel != null) {
0857                        newModel
0858                                .addTreeModelListener((TreeModelListener) accessibleContext);
0859                    }
0860                }
0861
0862                treeModel = newModel;
0863                clearToggledPaths();
0864                if (treeModel != null) {
0865                    if (treeModelListener == null)
0866                        treeModelListener = createTreeModelListener();
0867                    if (treeModelListener != null)
0868                        treeModel.addTreeModelListener(treeModelListener);
0869                    // Mark the root as expanded, if it isn't a leaf.
0870                    if (treeModel.getRoot() != null
0871                            && !treeModel.isLeaf(treeModel.getRoot())) {
0872                        expandedState.put(new TreePath(treeModel.getRoot()),
0873                                Boolean.TRUE);
0874                    }
0875                }
0876                firePropertyChange(TREE_MODEL_PROPERTY, oldModel, treeModel);
0877                invalidate();
0878            }
0879
0880            /**
0881             * Returns true if the root node of the tree is displayed.
0882             *
0883             * @return true if the root node of the tree is displayed
0884             * @see #rootVisible
0885             */
0886            public boolean isRootVisible() {
0887                return rootVisible;
0888            }
0889
0890            /**
0891             * Determines whether or not the root node from
0892             * the <code>TreeModel</code> is visible.
0893             * <p>
0894             * This is a bound property.  
0895             *
0896             * @param rootVisible true if the root node of the tree is to be displayed
0897             * @see #rootVisible
0898             * @beaninfo
0899             *        bound: true
0900             *  description: Whether or not the root node 
0901             *               from the TreeModel is visible.
0902             */
0903            public void setRootVisible(boolean rootVisible) {
0904                boolean oldValue = this .rootVisible;
0905
0906                this .rootVisible = rootVisible;
0907                firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue,
0908                        this .rootVisible);
0909                if (accessibleContext != null) {
0910                    ((AccessibleJTree) accessibleContext)
0911                            .fireVisibleDataPropertyChange();
0912                }
0913            }
0914
0915            /**
0916             * Sets the value of the <code>showsRootHandles</code> property,
0917             * which specifies whether the node handles should be displayed.
0918             * The default value of this property depends on the constructor
0919             * used to create the <code>JTree</code>.
0920             * Some look and feels might not support handles;
0921             * they will ignore this property.
0922             * <p>
0923             * This is a bound property.  
0924             *
0925             * @param newValue <code>true</code> if root handles should be displayed;
0926             *                 otherwise, <code>false</code>
0927             * @see #showsRootHandles
0928             * @see #getShowsRootHandles
0929             * @beaninfo
0930             *        bound: true
0931             *  description: Whether the node handles are to be
0932             *               displayed.
0933             */
0934            public void setShowsRootHandles(boolean newValue) {
0935                boolean oldValue = showsRootHandles;
0936                TreeModel model = getModel();
0937
0938                showsRootHandles = newValue;
0939                showsRootHandlesSet = true;
0940                firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue,
0941                        showsRootHandles);
0942                if (accessibleContext != null) {
0943                    ((AccessibleJTree) accessibleContext)
0944                            .fireVisibleDataPropertyChange();
0945                }
0946                invalidate();
0947            }
0948
0949            /**
0950             * Returns the value of the <code>showsRootHandles</code> property.
0951             * 
0952             * @return the value of the <code>showsRootHandles</code> property
0953             * @see #showsRootHandles
0954             */
0955            public boolean getShowsRootHandles() {
0956                return showsRootHandles;
0957            }
0958
0959            /**
0960             * Sets the height of each cell, in pixels.  If the specified value
0961             * is less than or equal to zero the current cell renderer is
0962             * queried for each row's height.
0963             * <p>
0964             * This is a bound property.  
0965             *
0966             * @param rowHeight the height of each cell, in pixels
0967             * @beaninfo
0968             *        bound: true
0969             *  description: The height of each cell.
0970             */
0971            public void setRowHeight(int rowHeight) {
0972                int oldValue = this .rowHeight;
0973
0974                this .rowHeight = rowHeight;
0975                rowHeightSet = true;
0976                firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue,
0977                        this .rowHeight);
0978                invalidate();
0979            }
0980
0981            /**
0982             * Returns the height of each row.  If the returned value is less than
0983             * or equal to 0 the height for each row is determined by the
0984             * renderer.
0985             *
0986             */
0987            public int getRowHeight() {
0988                return rowHeight;
0989            }
0990
0991            /**
0992             * Returns true if the height of each display row is a fixed size.
0993             *
0994             * @return true if the height of each row is a fixed size
0995             */
0996            public boolean isFixedRowHeight() {
0997                return (rowHeight > 0);
0998            }
0999
1000            /**
1001             * Specifies whether the UI should use a large model.
1002             * (Not all UIs will implement this.) Fires a property change
1003             * for the LARGE_MODEL_PROPERTY.
1004             * <p>
1005             * This is a bound property.  
1006             * 
1007             * @param newValue true to suggest a large model to the UI
1008             * @see #largeModel
1009             * @beaninfo
1010             *        bound: true
1011             *  description: Whether the UI should use a 
1012             *               large model.
1013             */
1014            public void setLargeModel(boolean newValue) {
1015                boolean oldValue = largeModel;
1016
1017                largeModel = newValue;
1018                firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, newValue);
1019            }
1020
1021            /**
1022             * Returns true if the tree is configured for a large model.
1023             * 
1024             * @return true if a large model is suggested
1025             * @see #largeModel
1026             */
1027            public boolean isLargeModel() {
1028                return largeModel;
1029            }
1030
1031            /**
1032             * Determines what happens when editing is interrupted by selecting
1033             * another node in the tree, a change in the tree's data, or by some
1034             * other means. Setting this property to <code>true</code> causes the
1035             * changes to be automatically saved when editing is interrupted.
1036             * <p>
1037             * Fires a property change for the INVOKES_STOP_CELL_EDITING_PROPERTY.
1038             *
1039             * @param newValue true means that <code>stopCellEditing</code> is invoked 
1040             *        when editing is interrupted, and data is saved; false means that
1041             *        <code>cancelCellEditing</code> is invoked, and changes are lost
1042             * @beaninfo
1043             *        bound: true
1044             *  description: Determines what happens when editing is interrupted,
1045             *               selecting another node in the tree, a change in the
1046             *               tree's data, or some other means.
1047             */
1048            public void setInvokesStopCellEditing(boolean newValue) {
1049                boolean oldValue = invokesStopCellEditing;
1050
1051                invokesStopCellEditing = newValue;
1052                firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY,
1053                        oldValue, newValue);
1054            }
1055
1056            /**
1057             * Returns the indicator that tells what happens when editing is 
1058             * interrupted.
1059             *
1060             * @return the indicator that tells what happens when editing is 
1061             *         interrupted
1062             * @see #setInvokesStopCellEditing
1063             */
1064            public boolean getInvokesStopCellEditing() {
1065                return invokesStopCellEditing;
1066            }
1067
1068            /**
1069             * Sets the <code>scrollsOnExpand</code> property,
1070             * which determines whether the 
1071             * tree might scroll to show previously hidden children.
1072             * If this property is <code>true</code> (the default), 
1073             * when a node expands
1074             * the tree can use scrolling to make
1075             * the maximum possible number of the node's descendants visible.
1076             * In some look and feels, trees might not need to scroll when expanded;
1077             * those look and feels will ignore this property.
1078             * <p>
1079             * This is a bound property.  
1080             *
1081             * @param newValue <code>false</code> to disable scrolling on expansion;
1082             *                 <code>true</code> to enable it
1083             * @see #getScrollsOnExpand
1084             *
1085             * @beaninfo
1086             *        bound: true
1087             *  description: Indicates if a node descendant should be scrolled when expanded.
1088             */
1089            public void setScrollsOnExpand(boolean newValue) {
1090                boolean oldValue = scrollsOnExpand;
1091
1092                scrollsOnExpand = newValue;
1093                scrollsOnExpandSet = true;
1094                firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue,
1095                        newValue);
1096            }
1097
1098            /**
1099             * Returns the value of the <code>scrollsOnExpand</code> property.
1100             *
1101             * @return the value of the <code>scrollsOnExpand</code> property
1102             */
1103            public boolean getScrollsOnExpand() {
1104                return scrollsOnExpand;
1105            }
1106
1107            /**
1108             * Sets the number of mouse clicks before a node will expand or close.
1109             * The default is two. 
1110             * <p>
1111             * This is a bound property.  
1112             *
1113             * @since 1.3
1114             * @beaninfo
1115             *        bound: true
1116             *  description: Number of clicks before a node will expand/collapse.
1117             */
1118            public void setToggleClickCount(int clickCount) {
1119                int oldCount = toggleClickCount;
1120
1121                toggleClickCount = clickCount;
1122                firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldCount,
1123                        clickCount);
1124            }
1125
1126            /**
1127             * Returns the number of mouse clicks needed to expand or close a node.
1128             *
1129             * @return number of mouse clicks before node is expanded
1130             * @since 1.3
1131             */
1132            public int getToggleClickCount() {
1133                return toggleClickCount;
1134            }
1135
1136            /**
1137             * Configures the <code>expandsSelectedPaths</code> property. If
1138             * true, any time the selection is changed, either via the
1139             * <code>TreeSelectionModel</code>, or the cover methods provided by 
1140             * <code>JTree</code>, the <code>TreePath</code>s parents will be
1141             * expanded to make them visible (visible meaning the parent path is
1142             * expanded, not necessarily in the visible rectangle of the
1143             * <code>JTree</code>). If false, when the selection
1144             * changes the nodes parent is not made visible (all its parents expanded).
1145             * This is useful if you wish to have your selection model maintain paths
1146             * that are not always visible (all parents expanded).
1147             * <p>
1148             * This is a bound property.  
1149             *
1150             * @param newValue the new value for <code>expandsSelectedPaths</code>
1151             *
1152             * @since 1.3
1153             * @beaninfo
1154             *        bound: true
1155             *  description: Indicates whether changes to the selection should make
1156             *               the parent of the path visible.
1157             */
1158            public void setExpandsSelectedPaths(boolean newValue) {
1159                boolean oldValue = expandsSelectedPaths;
1160
1161                expandsSelectedPaths = newValue;
1162                firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue,
1163                        newValue);
1164            }
1165
1166            /**
1167             * Returns the <code>expandsSelectedPaths</code> property.
1168             * @return true if selection changes result in the parent path being
1169             *         expanded
1170             * @since 1.3
1171             * @see #setExpandsSelectedPaths
1172             */
1173            public boolean getExpandsSelectedPaths() {
1174                return expandsSelectedPaths;
1175            }
1176
1177            /**
1178             * Turns on or off automatic drag handling. In order to enable automatic
1179             * drag handling, this property should be set to {@code true}, and the
1180             * tree's {@code TransferHandler} needs to be {@code non-null}.
1181             * The default value of the {@code dragEnabled} property is {@code false}.
1182             * <p>
1183             * The job of honoring this property, and recognizing a user drag gesture,
1184             * lies with the look and feel implementation, and in particular, the tree's
1185             * {@code TreeUI}. When automatic drag handling is enabled, most look and
1186             * feels (including those that subclass {@code BasicLookAndFeel}) begin a
1187             * drag and drop operation whenever the user presses the mouse button over
1188             * an item and then moves the mouse a few pixels. Setting this property to
1189             * {@code true} can therefore have a subtle effect on how selections behave.
1190             * <p>
1191             * If a look and feel is used that ignores this property, you can still
1192             * begin a drag and drop operation by calling {@code exportAsDrag} on the
1193             * tree's {@code TransferHandler}.
1194             *
1195             * @param b whether or not to enable automatic drag handling
1196             * @exception HeadlessException if
1197             *            <code>b</code> is <code>true</code> and
1198             *            <code>GraphicsEnvironment.isHeadless()</code>
1199             *            returns <code>true</code>
1200             * @see java.awt.GraphicsEnvironment#isHeadless
1201             * @see #getDragEnabled
1202             * @see #setTransferHandler
1203             * @see TransferHandler
1204             * @since 1.4
1205             *
1206             * @beaninfo
1207             *  description: determines whether automatic drag handling is enabled
1208             *        bound: false
1209             */
1210            public void setDragEnabled(boolean b) {
1211                if (b && GraphicsEnvironment.isHeadless()) {
1212                    throw new HeadlessException();
1213                }
1214                dragEnabled = b;
1215            }
1216
1217            /**
1218             * Returns whether or not automatic drag handling is enabled.
1219             *
1220             * @return the value of the {@code dragEnabled} property
1221             * @see #setDragEnabled
1222             * @since 1.4
1223             */
1224            public boolean getDragEnabled() {
1225                return dragEnabled;
1226            }
1227
1228            /**
1229             * Sets the drop mode for this component. For backward compatibility,
1230             * the default for this property is <code>DropMode.USE_SELECTION</code>.
1231             * Usage of one of the other modes is recommended, however, for an
1232             * improved user experience. <code>DropMode.ON</code>, for instance,
1233             * offers similar behavior of showing items as selected, but does so without
1234             * affecting the actual selection in the tree.
1235             * <p>
1236             * <code>JTree</code> supports the following drop modes:
1237             * <ul>
1238             *    <li><code>DropMode.USE_SELECTION</code></li>
1239             *    <li><code>DropMode.ON</code></li>
1240             *    <li><code>DropMode.INSERT</code></li>
1241             *    <li><code>DropMode.ON_OR_INSERT</code></li>
1242             * </ul>
1243             * <p>
1244             * The drop mode is only meaningful if this component has a
1245             * <code>TransferHandler</code> that accepts drops.
1246             *
1247             * @param dropMode the drop mode to use
1248             * @throws IllegalArgumentException if the drop mode is unsupported
1249             *         or <code>null</code>
1250             * @see #getDropMode
1251             * @see #getDropLocation
1252             * @see #setTransferHandler
1253             * @see TransferHandler
1254             * @since 1.6
1255             */
1256            public final void setDropMode(DropMode dropMode) {
1257                if (dropMode != null) {
1258                    switch (dropMode) {
1259                    case USE_SELECTION:
1260                    case ON:
1261                    case INSERT:
1262                    case ON_OR_INSERT:
1263                        this .dropMode = dropMode;
1264                        return;
1265                    }
1266                }
1267
1268                throw new IllegalArgumentException(dropMode
1269                        + ": Unsupported drop mode for tree");
1270            }
1271
1272            /**
1273             * Returns the drop mode for this component.
1274             *
1275             * @return the drop mode for this component
1276             * @see #setDropMode
1277             * @since 1.6
1278             */
1279            public final DropMode getDropMode() {
1280                return dropMode;
1281            }
1282
1283            /**
1284             * Calculates a drop location in this component, representing where a
1285             * drop at the given point should insert data.
1286             *
1287             * @param p the point to calculate a drop location for
1288             * @return the drop location, or <code>null</code>
1289             */
1290            DropLocation dropLocationForPoint(Point p) {
1291                DropLocation location = null;
1292
1293                int row = getClosestRowForLocation(p.x, p.y);
1294                Rectangle bounds = getRowBounds(row);
1295                TreeModel model = getModel();
1296                Object root = (model == null) ? null : model.getRoot();
1297                TreePath rootPath = (root == null) ? null : new TreePath(root);
1298
1299                TreePath child = null;
1300                TreePath parent = null;
1301                boolean outside = row == -1 || p.y < bounds.y
1302                        || p.y >= bounds.y + bounds.height;
1303
1304                switch (dropMode) {
1305                case USE_SELECTION:
1306                case ON:
1307                    if (outside) {
1308                        location = new DropLocation(p, null, -1);
1309                    } else {
1310                        location = new DropLocation(p, getPathForRow(row), -1);
1311                    }
1312
1313                    break;
1314                case INSERT:
1315                case ON_OR_INSERT:
1316                    if (row == -1) {
1317                        if (root != null && !model.isLeaf(root)
1318                                && isExpanded(rootPath)) {
1319                            location = new DropLocation(p, rootPath, 0);
1320                        } else {
1321                            location = new DropLocation(p, null, -1);
1322                        }
1323
1324                        break;
1325                    }
1326
1327                    boolean checkOn = dropMode == DropMode.ON_OR_INSERT
1328                            || !model.isLeaf(getPathForRow(row)
1329                                    .getLastPathComponent());
1330
1331                    Section section = SwingUtilities2.liesInVertical(bounds, p,
1332                            checkOn);
1333                    if (section == LEADING) {
1334                        child = getPathForRow(row);
1335                        parent = child.getParentPath();
1336                    } else if (section == TRAILING) {
1337                        int index = row + 1;
1338                        if (index >= getRowCount()) {
1339                            if (model.isLeaf(root) || !isExpanded(rootPath)) {
1340                                location = new DropLocation(p, null, -1);
1341                            } else {
1342                                parent = rootPath;
1343                                index = model.getChildCount(root);
1344                                location = new DropLocation(p, parent, index);
1345                            }
1346
1347                            break;
1348                        }
1349
1350                        child = getPathForRow(index);
1351                        parent = child.getParentPath();
1352                    } else {
1353                        assert checkOn;
1354                        location = new DropLocation(p, getPathForRow(row), -1);
1355                        break;
1356                    }
1357
1358                    if (parent != null) {
1359                        location = new DropLocation(p, parent, model
1360                                .getIndexOfChild(parent.getLastPathComponent(),
1361                                        child.getLastPathComponent()));
1362                    } else if (checkOn || !model.isLeaf(root)) {
1363                        location = new DropLocation(p, rootPath, -1);
1364                    } else {
1365                        location = new DropLocation(p, null, -1);
1366                    }
1367
1368                    break;
1369                default:
1370                    assert false : "Unexpected drop mode";
1371                }
1372
1373                if (outside || row != expandRow) {
1374                    cancelDropTimer();
1375                }
1376
1377                if (!outside && row != expandRow) {
1378                    if (isCollapsed(row)) {
1379                        expandRow = row;
1380                        startDropTimer();
1381                    }
1382                }
1383
1384                return location;
1385            }
1386
1387            /**
1388             * Called to set or clear the drop location during a DnD operation.
1389             * In some cases, the component may need to use it's internal selection
1390             * temporarily to indicate the drop location. To help facilitate this,
1391             * this method returns and accepts as a parameter a state object.
1392             * This state object can be used to store, and later restore, the selection
1393             * state. Whatever this method returns will be passed back to it in
1394             * future calls, as the state parameter. If it wants the DnD system to
1395             * continue storing the same state, it must pass it back every time.
1396             * Here's how this is used:
1397             * <p>
1398             * Let's say that on the first call to this method the component decides
1399             * to save some state (because it is about to use the selection to show
1400             * a drop index). It can return a state object to the caller encapsulating
1401             * any saved selection state. On a second call, let's say the drop location
1402             * is being changed to something else. The component doesn't need to
1403             * restore anything yet, so it simply passes back the same state object
1404             * to have the DnD system continue storing it. Finally, let's say this
1405             * method is messaged with <code>null</code>. This means DnD
1406             * is finished with this component for now, meaning it should restore
1407             * state. At this point, it can use the state parameter to restore
1408             * said state, and of course return <code>null</code> since there's
1409             * no longer anything to store.
1410             *
1411             * @param location the drop location (as calculated by
1412             *        <code>dropLocationForPoint</code>) or <code>null</code>
1413             *        if there's no longer a valid drop location
1414             * @param state the state object saved earlier for this component,
1415             *        or <code>null</code>
1416             * @param forDrop whether or not the method is being called because an
1417             *        actual drop occurred
1418             * @return any saved state for this component, or <code>null</code> if none
1419             */
1420            Object setDropLocation(TransferHandler.DropLocation location,
1421                    Object state, boolean forDrop) {
1422
1423                Object retVal = null;
1424                DropLocation treeLocation = (DropLocation) location;
1425
1426                if (dropMode == DropMode.USE_SELECTION) {
1427                    if (treeLocation == null) {
1428                        if (!forDrop && state != null) {
1429                            setSelectionPaths(((TreePath[][]) state)[0]);
1430                            setAnchorSelectionPath(((TreePath[][]) state)[1][0]);
1431                            setLeadSelectionPath(((TreePath[][]) state)[1][1]);
1432                        }
1433                    } else {
1434                        if (dropLocation == null) {
1435                            TreePath[] paths = getSelectionPaths();
1436                            if (paths == null) {
1437                                paths = new TreePath[0];
1438                            }
1439
1440                            retVal = new TreePath[][] {
1441                                    paths,
1442                                    { getAnchorSelectionPath(),
1443                                            getLeadSelectionPath() } };
1444                        } else {
1445                            retVal = state;
1446                        }
1447
1448                        setSelectionPath(treeLocation.getPath());
1449                    }
1450                }
1451
1452                DropLocation old = dropLocation;
1453                dropLocation = treeLocation;
1454                firePropertyChange("dropLocation", old, dropLocation);
1455
1456                return retVal;
1457            }
1458
1459            /**
1460             * Called to indicate to this component that DnD is done.
1461             * Allows for us to cancel the expand timer.
1462             */
1463            void dndDone() {
1464                cancelDropTimer();
1465                dropTimer = null;
1466            }
1467
1468            /**
1469             * Returns the location that this component should visually indicate
1470             * as the drop location during a DnD operation over the component,
1471             * or {@code null} if no location is to currently be shown.
1472             * <p>
1473             * This method is not meant for querying the drop location
1474             * from a {@code TransferHandler}, as the drop location is only
1475             * set after the {@code TransferHandler}'s <code>canImport</code>
1476             * has returned and has allowed for the location to be shown.
1477             * <p>
1478             * When this property changes, a property change event with
1479             * name "dropLocation" is fired by the component.
1480             *
1481             * @return the drop location
1482             * @see #setDropMode
1483             * @see TransferHandler#canImport(TransferHandler.TransferSupport)
1484             * @since 1.6
1485             */
1486            public final DropLocation getDropLocation() {
1487                return dropLocation;
1488            }
1489
1490            private void startDropTimer() {
1491                if (dropTimer == null) {
1492                    dropTimer = new TreeTimer();
1493                }
1494                dropTimer.start();
1495            }
1496
1497            private void cancelDropTimer() {
1498                if (dropTimer != null && dropTimer.isRunning()) {
1499                    expandRow = -1;
1500                    dropTimer.stop();
1501                }
1502            }
1503
1504            /**
1505             * Returns <code>isEditable</code>. This is invoked from the UI before
1506             * editing begins to insure that the given path can be edited. This
1507             * is provided as an entry point for subclassers to add filtered
1508             * editing without having to resort to creating a new editor.
1509             *
1510             * @return true if every parent node and the node itself is editable
1511             * @see #isEditable
1512             */
1513            public boolean isPathEditable(TreePath path) {
1514                return isEditable();
1515            }
1516
1517            /**
1518             * Overrides <code>JComponent</code>'s <code>getToolTipText</code>
1519             * method in order to allow 
1520             * renderer's tips to be used if it has text set.
1521             * <p>
1522             * NOTE: For <code>JTree</code> to properly display tooltips of its
1523             * renderers, <code>JTree</code> must be a registered component with the
1524             * <code>ToolTipManager</code>.  This can be done by invoking
1525             * <code>ToolTipManager.sharedInstance().registerComponent(tree)</code>.
1526             * This is not done automatically!
1527             *
1528             * @param event the <code>MouseEvent</code> that initiated the 
1529             *		<code>ToolTip</code> display
1530             * @return a string containing the  tooltip or <code>null</code>
1531             *		if <code>event</code> is null
1532             */
1533            public String getToolTipText(MouseEvent event) {
1534                String tip = null;
1535
1536                if (event != null) {
1537                    Point p = event.getPoint();
1538                    int selRow = getRowForLocation(p.x, p.y);
1539                    TreeCellRenderer r = getCellRenderer();
1540
1541                    if (selRow != -1 && r != null) {
1542                        TreePath path = getPathForRow(selRow);
1543                        Object lastPath = path.getLastPathComponent();
1544                        Component rComponent = r.getTreeCellRendererComponent(
1545                                this , lastPath, isRowSelected(selRow),
1546                                isExpanded(selRow),
1547                                getModel().isLeaf(lastPath), selRow, true);
1548
1549                        if (rComponent instanceof  JComponent) {
1550                            MouseEvent newEvent;
1551                            Rectangle pathBounds = getPathBounds(path);
1552
1553                            p.translate(-pathBounds.x, -pathBounds.y);
1554                            newEvent = new MouseEvent(rComponent,
1555                                    event.getID(), event.getWhen(), event
1556                                            .getModifiers(), p.x, p.y, event
1557                                            .getXOnScreen(), event
1558                                            .getYOnScreen(), event
1559                                            .getClickCount(), event
1560                                            .isPopupTrigger(),
1561                                    MouseEvent.NOBUTTON);
1562
1563                            tip = ((JComponent) rComponent)
1564                                    .getToolTipText(newEvent);
1565                        }
1566                    }
1567                }
1568                // No tip from the renderer get our own tip
1569                if (tip == null) {
1570                    tip = getToolTipText();
1571                }
1572                return tip;
1573            }
1574
1575            /**
1576             * Called by the renderers to convert the specified value to
1577             * text. This implementation returns <code>value.toString</code>, ignoring
1578             * all other arguments. To control the conversion, subclass this 
1579             * method and use any of the arguments you need.
1580             * 
1581             * @param value the <code>Object</code> to convert to text
1582             * @param selected true if the node is selected
1583             * @param expanded true if the node is expanded
1584             * @param leaf  true if the node is a leaf node
1585             * @param row  an integer specifying the node's display row, where 0 is 
1586             *             the first row in the display
1587             * @param hasFocus true if the node has the focus
1588             * @return the <code>String</code> representation of the node's value
1589             */
1590            public String convertValueToText(Object value, boolean selected,
1591                    boolean expanded, boolean leaf, int row, boolean hasFocus) {
1592                if (value != null) {
1593                    String sValue = value.toString();
1594                    if (sValue != null) {
1595                        return sValue;
1596                    }
1597                }
1598                return "";
1599            }
1600
1601            //
1602            // The following are convenience methods that get forwarded to the
1603            // current TreeUI.
1604            //
1605
1606            /**
1607             * Returns the number of viewable nodes. A node is viewable if all of its
1608             * parents are expanded. The root is only included in this count if
1609             * {@code isRootVisible()} is {@code true}. This returns {@code 0} if
1610             * the UI has not been set.
1611             *
1612             * @return the number of viewable nodes
1613             */
1614            public int getRowCount() {
1615                TreeUI tree = getUI();
1616
1617                if (tree != null)
1618                    return tree.getRowCount(this );
1619                return 0;
1620            }
1621
1622            /** 
1623             * Selects the node identified by the specified path. If any
1624             * component of the path is hidden (under a collapsed node), and
1625             * <code>getExpandsSelectedPaths</code> is true it is 
1626             * exposed (made viewable).
1627             *
1628             * @param path the <code>TreePath</code> specifying the node to select
1629             */
1630            public void setSelectionPath(TreePath path) {
1631                getSelectionModel().setSelectionPath(path);
1632            }
1633
1634            /** 
1635             * Selects the nodes identified by the specified array of paths.
1636             * If any component in any of the paths is hidden (under a collapsed
1637             * node), and <code>getExpandsSelectedPaths</code> is true
1638             * it is exposed (made viewable).
1639             *
1640             * @param paths an array of <code>TreePath</code> objects that specifies
1641             *		the nodes to select
1642             */
1643            public void setSelectionPaths(TreePath[] paths) {
1644                getSelectionModel().setSelectionPaths(paths);
1645            }
1646
1647            /**
1648             * Sets the path identifies as the lead. The lead may not be selected.
1649             * The lead is not maintained by <code>JTree</code>,
1650             * rather the UI will update it.
1651             * <p>
1652             * This is a bound property.  
1653             *
1654             * @param newPath  the new lead path
1655             * @since 1.3
1656             * @beaninfo
1657             *        bound: true
1658             *  description: Lead selection path
1659             */
1660            public void setLeadSelectionPath(TreePath newPath) {
1661                TreePath oldValue = leadPath;
1662
1663                leadPath = newPath;
1664                firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue,
1665                        newPath);
1666            }
1667
1668            /**
1669             * Sets the path identified as the anchor.
1670             * The anchor is not maintained by <code>JTree</code>, rather the UI will 
1671             * update it.
1672             * <p>
1673             * This is a bound property.  
1674             *
1675             * @param newPath  the new anchor path
1676             * @since 1.3
1677             * @beaninfo
1678             *        bound: true
1679             *  description: Anchor selection path
1680             */
1681            public void setAnchorSelectionPath(TreePath newPath) {
1682                TreePath oldValue = anchorPath;
1683
1684                anchorPath = newPath;
1685                firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue,
1686                        newPath);
1687            }
1688
1689            /**
1690             * Selects the node at the specified row in the display.
1691             *
1692             * @param row  the row to select, where 0 is the first row in
1693             *             the display
1694             */
1695            public void setSelectionRow(int row) {
1696                int[] rows = { row };
1697
1698                setSelectionRows(rows);
1699            }
1700
1701            /**
1702             * Selects the nodes corresponding to each of the specified rows
1703             * in the display. If a particular element of <code>rows</code> is
1704             * < 0 or >= <code>getRowCount</code>, it will be ignored.
1705             * If none of the elements
1706             * in <code>rows</code> are valid rows, the selection will
1707             * be cleared. That is it will be as if <code>clearSelection</code>
1708             * was invoked.
1709             * 
1710             * @param rows  an array of ints specifying the rows to select,
1711             *              where 0 indicates the first row in the display
1712             */
1713            public void setSelectionRows(int[] rows) {
1714                TreeUI ui = getUI();
1715
1716                if (ui != null && rows != null) {
1717                    int numRows = rows.length;
1718                    TreePath[] paths = new TreePath[numRows];
1719
1720                    for (int counter = 0; counter < numRows; counter++) {
1721                        paths[counter] = ui.getPathForRow(this , rows[counter]);
1722                    }
1723                    setSelectionPaths(paths);
1724                }
1725            }
1726
1727            /**
1728             * Adds the node identified by the specified <code>TreePath</code>
1729             * to the current selection. If any component of the path isn't
1730             * viewable, and <code>getExpandsSelectedPaths</code> is true it is 
1731             * made viewable.
1732             * <p>
1733             * Note that <code>JTree</code> does not allow duplicate nodes to
1734             * exist as children under the same parent -- each sibling must be
1735             * a unique object.
1736             *
1737             * @param path the <code>TreePath</code> to add
1738             */
1739            public void addSelectionPath(TreePath path) {
1740                getSelectionModel().addSelectionPath(path);
1741            }
1742
1743            /**
1744             * Adds each path in the array of paths to the current selection. If
1745             * any component of any of the paths isn't viewable and
1746             * <code>getExpandsSelectedPaths</code> is true, it is
1747             * made viewable.
1748             * <p>
1749             * Note that <code>JTree</code> does not allow duplicate nodes to
1750             * exist as children under the same parent -- each sibling must be
1751             * a unique object.
1752             *
1753             * @param paths an array of <code>TreePath</code> objects that specifies
1754             *		the nodes to add
1755             */
1756            public void addSelectionPaths(TreePath[] paths) {
1757                getSelectionModel().addSelectionPaths(paths);
1758            }
1759
1760            /**
1761             * Adds the path at the specified row to the current selection.
1762             *
1763             * @param row  an integer specifying the row of the node to add,
1764             *             where 0 is the first row in the display
1765             */
1766            public void addSelectionRow(int row) {
1767                int[] rows = { row };
1768
1769                addSelectionRows(rows);
1770            }
1771
1772            /**
1773             * Adds the paths at each of the specified rows to the current selection.
1774             * 
1775             * @param rows  an array of ints specifying the rows to add,
1776             *              where 0 indicates the first row in the display
1777             */
1778            public void addSelectionRows(int[] rows) {
1779                TreeUI ui = getUI();
1780
1781                if (ui != null && rows != null) {
1782                    int numRows = rows.length;
1783                    TreePath[] paths = new TreePath[numRows];
1784
1785                    for (int counter = 0; counter < numRows; counter++)
1786                        paths[counter] = ui.getPathForRow(this , rows[counter]);
1787                    addSelectionPaths(paths);
1788                }
1789            }
1790
1791            /**
1792             * Returns the last path component of the selected path. This is
1793             * a convenience method for
1794             * {@code getSelectionModel().getSelectionPath().getLastPathComponent()}.
1795             * This is typically only useful if the selection has one path.
1796             *
1797             * @return the last path component of the selected path, or
1798             *         <code>null</code> if nothing is selected
1799             * @see TreePath#getLastPathComponent
1800             */
1801            public Object getLastSelectedPathComponent() {
1802                TreePath selPath = getSelectionModel().getSelectionPath();
1803
1804                if (selPath != null)
1805                    return selPath.getLastPathComponent();
1806                return null;
1807            }
1808
1809            /**
1810             * Returns the path identified as the lead.
1811             * @return path identified as the lead
1812             */
1813            public TreePath getLeadSelectionPath() {
1814                return leadPath;
1815            }
1816
1817            /**
1818             * Returns the path identified as the anchor.
1819             * @return path identified as the anchor
1820             * @since 1.3
1821             */
1822            public TreePath getAnchorSelectionPath() {
1823                return anchorPath;
1824            }
1825
1826            /**
1827             * Returns the path to the first selected node.
1828             *
1829             * @return the <code>TreePath</code> for the first selected node,
1830             *		or <code>null</code> if nothing is currently selected
1831             */
1832            public TreePath getSelectionPath() {
1833                return getSelectionModel().getSelectionPath();
1834            }
1835
1836            /**
1837             * Returns the paths of all selected values.
1838             *
1839             * @return an array of <code>TreePath</code> objects indicating the selected
1840             *         nodes, or <code>null</code> if nothing is currently selected
1841             */
1842            public TreePath[] getSelectionPaths() {
1843                return getSelectionModel().getSelectionPaths();
1844            }
1845
1846            /**
1847             * Returns all of the currently selected rows. This method is simply
1848             * forwarded to the <code>TreeSelectionModel</code>.
1849             * If nothing is selected <code>null</code> or an empty array will
1850             * be returned, based on the <code>TreeSelectionModel</code>
1851             * implementation.
1852             *
1853             * @return an array of integers that identifies all currently selected rows
1854             *         where 0 is the first row in the display
1855             */
1856            public int[] getSelectionRows() {
1857                return getSelectionModel().getSelectionRows();
1858            }
1859
1860            /**
1861             * Returns the number of nodes selected.
1862             *
1863             * @return the number of nodes selected
1864             */
1865            public int getSelectionCount() {
1866                return selectionModel.getSelectionCount();
1867            }
1868
1869            /**
1870             * Returns the smallest selected row. If the selection is empty, or
1871             * none of the selected paths are viewable, {@code -1} is returned.
1872             *
1873             * @return the smallest selected row
1874             */
1875            public int getMinSelectionRow() {
1876                return getSelectionModel().getMinSelectionRow();
1877            }
1878
1879            /**
1880             * Returns the largest selected row. If the selection is empty, or
1881             * none of the selected paths are viewable, {@code -1} is returned.
1882             *
1883             * @return the largest selected row
1884             */
1885            public int getMaxSelectionRow() {
1886                return getSelectionModel().getMaxSelectionRow();
1887            }
1888
1889            /**
1890             * Returns the row index corresponding to the lead path.
1891             *
1892             * @return an integer giving the row index of the lead path,
1893             *		where 0 is the first row in the display; or -1
1894             *		if <code>leadPath</code> is <code>null</code>
1895             */
1896            public int getLeadSelectionRow() {
1897                TreePath leadPath = getLeadSelectionPath();
1898
1899                if (leadPath != null) {
1900                    return getRowForPath(leadPath);
1901                }
1902                return -1;
1903            }
1904
1905            /**
1906             * Returns true if the item identified by the path is currently selected.
1907             *
1908             * @param path a <code>TreePath</code> identifying a node
1909             * @return true if the node is selected
1910             */
1911            public boolean isPathSelected(TreePath path) {
1912                return getSelectionModel().isPathSelected(path);
1913            }
1914
1915            /**
1916             * Returns true if the node identified by row is selected.
1917             *
1918             * @param row  an integer specifying a display row, where 0 is the first
1919             *             row in the display
1920             * @return true if the node is selected
1921             */
1922            public boolean isRowSelected(int row) {
1923                return getSelectionModel().isRowSelected(row);
1924            }
1925
1926            /**
1927             * Returns an <code>Enumeration</code> of the descendants of the
1928             * path <code>parent</code> that
1929             * are currently expanded. If <code>parent</code> is not currently
1930             * expanded, this will return <code>null</code>.
1931             * If you expand/collapse nodes while
1932             * iterating over the returned <code>Enumeration</code>
1933             * this may not return all
1934             * the expanded paths, or may return paths that are no longer expanded.
1935             *
1936             * @param parent  the path which is to be examined
1937             * @return an <code>Enumeration</code> of the descendents of 
1938             *		<code>parent</code>, or <code>null</code> if
1939             *		<code>parent</code> is not currently expanded
1940             */
1941            public Enumeration<TreePath> getExpandedDescendants(TreePath parent) {
1942                if (!isExpanded(parent))
1943                    return null;
1944
1945                Enumeration toggledPaths = expandedState.keys();
1946                Vector elements = null;
1947                TreePath path;
1948                Object value;
1949
1950                if (toggledPaths != null) {
1951                    while (toggledPaths.hasMoreElements()) {
1952                        path = (TreePath) toggledPaths.nextElement();
1953                        value = expandedState.get(path);
1954                        // Add the path if it is expanded, a descendant of parent,
1955                        // and it is visible (all parents expanded). This is rather
1956                        // expensive!
1957                        if (path != parent && value != null
1958                                && ((Boolean) value).booleanValue()
1959                                && parent.isDescendant(path) && isVisible(path)) {
1960                            if (elements == null) {
1961                                elements = new Vector();
1962                            }
1963                            elements.addElement(path);
1964                        }
1965                    }
1966                }
1967                if (elements == null) {
1968                    Set<TreePath> empty = Collections.emptySet();
1969                    return Collections.enumeration(empty);
1970                }
1971                return elements.elements();
1972            }
1973
1974            /**
1975             * Returns true if the node identified by the path has ever been
1976             * expanded.
1977             * @return true if the <code>path</code> has ever been expanded
1978             */
1979            public boolean hasBeenExpanded(TreePath path) {
1980                return (path != null && expandedState.get(path) != null);
1981            }
1982
1983            /**
1984             * Returns true if the node identified by the path is currently expanded,
1985             * 
1986             * @param path  the <code>TreePath</code> specifying the node to check
1987             * @return false if any of the nodes in the node's path are collapsed, 
1988             *               true if all nodes in the path are expanded
1989             */
1990            public boolean isExpanded(TreePath path) {
1991                if (path == null)
1992                    return false;
1993
1994                // Is this node expanded?
1995                Object value = expandedState.get(path);
1996
1997                if (value == null || !((Boolean) value).booleanValue())
1998                    return false;
1999
2000                // It is, make sure its parent is also expanded.
2001                TreePath parentPath = path.getParentPath();
2002
2003                if (parentPath != null)
2004                    return isExpanded(parentPath);
2005                return true;
2006            }
2007
2008            /**
2009             * Returns true if the node at the specified display row is currently
2010             * expanded.
2011             * 
2012             * @param row  the row to check, where 0 is the first row in the 
2013             *             display
2014             * @return true if the node is currently expanded, otherwise false
2015             */
2016            public boolean isExpanded(int row) {
2017                TreeUI tree = getUI();
2018
2019                if (tree != null) {
2020                    TreePath path = tree.getPathForRow(this , row);
2021
2022                    if (path != null) {
2023                        Boolean value = (Boolean) expandedState.get(path);
2024
2025                        return (value != null && value.booleanValue());
2026                    }
2027                }
2028                return false;
2029            }
2030
2031            /**
2032             * Returns true if the value identified by path is currently collapsed,
2033             * this will return false if any of the values in path are currently
2034             * not being displayed.
2035             * 
2036             * @param path  the <code>TreePath</code> to check
2037             * @return true if any of the nodes in the node's path are collapsed, 
2038             *               false if all nodes in the path are expanded
2039             */
2040            public boolean isCollapsed(TreePath path) {
2041                return !isExpanded(path);
2042            }
2043
2044            /**
2045             * Returns true if the node at the specified display row is collapsed.
2046             * 
2047             * @param row  the row to check, where 0 is the first row in the 
2048             *             display
2049             * @return true if the node is currently collapsed, otherwise false
2050             */
2051            public boolean isCollapsed(int row) {
2052                return !isExpanded(row);
2053            }
2054
2055            /**
2056             * Ensures that the node identified by path is currently viewable.
2057             *
2058             * @param path  the <code>TreePath</code> to make visible
2059             */
2060            public void makeVisible(TreePath path) {
2061                if (path != null) {
2062                    TreePath parentPath = path.getParentPath();
2063
2064                    if (parentPath != null) {
2065                        expandPath(parentPath);
2066                    }
2067                }
2068            }
2069
2070            /**
2071             * Returns true if the value identified by path is currently viewable,
2072             * which means it is either the root or all of its parents are expanded.
2073             * Otherwise, this method returns false. 
2074             *
2075             * @return true if the node is viewable, otherwise false
2076             */
2077            public boolean isVisible(TreePath path) {
2078                if (path != null) {
2079                    TreePath parentPath = path.getParentPath();
2080
2081                    if (parentPath != null)
2082                        return isExpanded(parentPath);
2083                    // Root.
2084                    return true;
2085                }
2086                return false;
2087            }
2088
2089            /**
2090             * Returns the <code>Rectangle</code> that the specified node will be drawn
2091             * into. Returns <code>null</code> if any component in the path is hidden
2092             * (under a collapsed parent).
2093             * <p>
2094             * Note:<br>
2095             * This method returns a valid rectangle, even if the specified
2096             * node is not currently displayed.
2097             *
2098             * @param path the <code>TreePath</code> identifying the node
2099             * @return the <code>Rectangle</code> the node is drawn in,
2100             *		or <code>null</code> 
2101             */
2102            public Rectangle getPathBounds(TreePath path) {
2103                TreeUI tree = getUI();
2104
2105                if (tree != null)
2106                    return tree.getPathBounds(this , path);
2107                return null;
2108            }
2109
2110            /**
2111             * Returns the <code>Rectangle</code> that the node at the specified row is
2112             * drawn in.
2113             *
2114             * @param row  the row to be drawn, where 0 is the first row in the 
2115             *             display
2116             * @return the <code>Rectangle</code> the node is drawn in 
2117             */
2118            public Rectangle getRowBounds(int row) {
2119                return getPathBounds(getPathForRow(row));
2120            }
2121
2122            /**
2123             * Makes sure all the path components in path are expanded (except
2124             * for the last path component) and scrolls so that the 
2125             * node identified by the path is displayed. Only works when this
2126             * <code>JTree</code> is contained in a <code>JScrollPane</code>.
2127             * 
2128             * @param path  the <code>TreePath</code> identifying the node to
2129             * 		bring into view
2130             */
2131            public void scrollPathToVisible(TreePath path) {
2132                if (path != null) {
2133                    makeVisible(path);
2134
2135                    Rectangle bounds = getPathBounds(path);
2136
2137                    if (bounds != null) {
2138                        scrollRectToVisible(bounds);
2139                        if (accessibleContext != null) {
2140                            ((AccessibleJTree) accessibleContext)
2141                                    .fireVisibleDataPropertyChange();
2142                        }
2143                    }
2144                }
2145            }
2146
2147            /**
2148             * Scrolls the item identified by row until it is displayed. The minimum
2149             * of amount of scrolling necessary to bring the row into view
2150             * is performed. Only works when this <code>JTree</code> is contained in a
2151             * <code>JScrollPane</code>.
2152             *
2153             * @param row  an integer specifying the row to scroll, where 0 is the
2154             *             first row in the display
2155             */
2156            public void scrollRowToVisible(int row) {
2157                scrollPathToVisible(getPathForRow(row));
2158            }
2159
2160            /**
2161             * Returns the path for the specified row.  If <code>row</code> is
2162             * not visible, or a {@code TreeUI} has not been set, <code>null</code>
2163             * is returned.
2164             *
2165             * @param row  an integer specifying a row
2166             * @return the <code>TreePath</code> to the specified node,
2167             *		<code>null</code> if <code>row < 0</code>
2168             *          or <code>row >= getRowCount()</code>
2169             */
2170            public TreePath getPathForRow(int row) {
2171                TreeUI tree = getUI();
2172
2173                if (tree != null)
2174                    return tree.getPathForRow(this , row);
2175                return null;
2176            }
2177
2178            /**
2179             * Returns the row that displays the node identified by the specified
2180             * path. 
2181             * 
2182             * @param path  the <code>TreePath</code> identifying a node
2183             * @return an integer specifying the display row, where 0 is the first
2184             *         row in the display, or -1 if any of the elements in path
2185             *         are hidden under a collapsed parent.
2186             */
2187            public int getRowForPath(TreePath path) {
2188                TreeUI tree = getUI();
2189
2190                if (tree != null)
2191                    return tree.getRowForPath(this , path);
2192                return -1;
2193            }
2194
2195            /**
2196             * Ensures that the node identified by the specified path is 
2197             * expanded and viewable. If the last item in the path is a
2198             * leaf, this will have no effect.
2199             * 
2200             * @param path  the <code>TreePath</code> identifying a node
2201             */
2202            public void expandPath(TreePath path) {
2203                // Only expand if not leaf!
2204                TreeModel model = getModel();
2205
2206                if (path != null && model != null
2207                        && !model.isLeaf(path.getLastPathComponent())) {
2208                    setExpandedState(path, true);
2209                }
2210            }
2211
2212            /**
2213             * Ensures that the node in the specified row is expanded and
2214             * viewable.
2215             * <p>
2216             * If <code>row</code> is < 0 or >= <code>getRowCount</code> this
2217             * will have no effect.
2218             *
2219             * @param row  an integer specifying a display row, where 0 is the
2220             *             first row in the display
2221             */
2222            public void expandRow(int row) {
2223                expandPath(getPathForRow(row));
2224            }
2225
2226            /**
2227             * Ensures that the node identified by the specified path is 
2228             * collapsed and viewable.
2229             * 
2230             * @param path  the <code>TreePath</code> identifying a node
2231             */
2232            public void collapsePath(TreePath path) {
2233                setExpandedState(path, false);
2234            }
2235
2236            /**
2237             * Ensures that the node in the specified row is collapsed.
2238             * <p>
2239             * If <code>row</code> is < 0 or >= <code>getRowCount</code> this
2240             * will have no effect.
2241             *
2242             * @param row  an integer specifying a display row, where 0 is the
2243             *             first row in the display
2244             */
2245            public void collapseRow(int row) {
2246                collapsePath(getPathForRow(row));
2247            }
2248
2249            /**
2250             * Returns the path for the node at the specified location.
2251             *
2252             * @param x an integer giving the number of pixels horizontally from
2253             *          the left edge of the display area, minus any left margin
2254             * @param y an integer giving the number of pixels vertically from
2255             *          the top of the display area, minus any top margin
2256             * @return  the <code>TreePath</code> for the node at that location
2257             */
2258            public TreePath getPathForLocation(int x, int y) {
2259                TreePath closestPath = getClosestPathForLocation(x, y);
2260
2261                if (closestPath != null) {
2262                    Rectangle pathBounds = getPathBounds(closestPath);
2263
2264                    if (pathBounds != null && x >= pathBounds.x
2265                            && x < (pathBounds.x + pathBounds.width)
2266                            && y >= pathBounds.y
2267                            && y < (pathBounds.y + pathBounds.height))
2268                        return closestPath;
2269                }
2270                return null;
2271            }
2272
2273            /**
2274             * Returns the row for the specified location. 
2275             *
2276             * @param x an integer giving the number of pixels horizontally from
2277             *          the left edge of the display area, minus any left margin
2278             * @param y an integer giving the number of pixels vertically from
2279             *          the top of the display area, minus any top margin
2280             * @return the row corresponding to the location, or -1 if the
2281             *         location is not within the bounds of a displayed cell
2282             * @see #getClosestRowForLocation
2283             */
2284            public int getRowForLocation(int x, int y) {
2285                return getRowForPath(getPathForLocation(x, y));
2286            }
2287
2288            /**
2289             * Returns the path to the node that is closest to x,y.  If
2290             * no nodes are currently viewable, or there is no model, returns
2291             * <code>null</code>, otherwise it always returns a valid path.  To test if
2292             * the node is exactly at x, y, get the node's bounds and
2293             * test x, y against that.
2294             *
2295             * @param x an integer giving the number of pixels horizontally from
2296             *          the left edge of the display area, minus any left margin
2297             * @param y an integer giving the number of pixels vertically from
2298             *          the top of the display area, minus any top margin
2299             * @return  the <code>TreePath</code> for the node closest to that location,
2300             *          <code>null</code> if nothing is viewable or there is no model
2301             *
2302             * @see #getPathForLocation
2303             * @see #getPathBounds
2304             */
2305            public TreePath getClosestPathForLocation(int x, int y) {
2306                TreeUI tree = getUI();
2307
2308                if (tree != null)
2309                    return tree.getClosestPathForLocation(this , x, y);
2310                return null;
2311            }
2312
2313            /**
2314             * Returns the row to the node that is closest to x,y.  If no nodes
2315             * are viewable or there is no model, returns -1. Otherwise,
2316             * it always returns a valid row.  To test if the returned object is 
2317             * exactly at x, y, get the bounds for the node at the returned
2318             * row and test x, y against that.
2319             *
2320             * @param x an integer giving the number of pixels horizontally from
2321             *          the left edge of the display area, minus any left margin
2322             * @param y an integer giving the number of pixels vertically from
2323             *          the top of the display area, minus any top margin
2324             * @return the row closest to the location, -1 if nothing is
2325             *         viewable or there is no model
2326             *
2327             * @see #getRowForLocation
2328             * @see #getRowBounds
2329             */
2330            public int getClosestRowForLocation(int x, int y) {
2331                return getRowForPath(getClosestPathForLocation(x, y));
2332            }
2333
2334            /**
2335             * Returns true if the tree is being edited. The item that is being
2336             * edited can be obtained using <code>getSelectionPath</code>.
2337             *
2338             * @return true if the user is currently editing a node
2339             * @see #getSelectionPath
2340             */
2341            public boolean isEditing() {
2342                TreeUI tree = getUI();
2343
2344                if (tree != null)
2345                    return tree.isEditing(this );
2346                return false;
2347            }
2348
2349            /**
2350             * Ends the current editing session.
2351             * (The <code>DefaultTreeCellEditor</code> 
2352             * object saves any edits that are currently in progress on a cell.
2353             * Other implementations may operate differently.) 
2354             * Has no effect if the tree isn't being edited.
2355             * <blockquote>
2356             * <b>Note:</b><br>
2357             * To make edit-saves automatic whenever the user changes
2358             * their position in the tree, use {@link #setInvokesStopCellEditing}.
2359             * </blockquote>
2360             *
2361             * @return true if editing was in progress and is now stopped,
2362             *              false if editing was not in progress
2363             */
2364            public boolean stopEditing() {
2365                TreeUI tree = getUI();
2366
2367                if (tree != null)
2368                    return tree.stopEditing(this );
2369                return false;
2370            }
2371
2372            /**
2373             * Cancels the current editing session. Has no effect if the
2374             * tree isn't being edited.
2375             */
2376            public void cancelEditing() {
2377                TreeUI tree = getUI();
2378
2379                if (tree != null)
2380                    tree.cancelEditing(this );
2381            }
2382
2383            /**
2384             * Selects the node identified by the specified path and initiates
2385             * editing.  The edit-attempt fails if the <code>CellEditor</code>
2386             * does not allow
2387             * editing for the specified item.
2388             * 
2389             * @param path  the <code>TreePath</code> identifying a node
2390             */
2391            public void startEditingAtPath(TreePath path) {
2392                TreeUI tree = getUI();
2393
2394                if (tree != null)
2395                    tree.startEditingAtPath(this , path);
2396            }
2397
2398            /**
2399             * Returns the path to the element that is currently being edited.
2400             *
2401             * @return  the <code>TreePath</code> for the node being edited
2402             */
2403            public TreePath getEditingPath() {
2404                TreeUI tree = getUI();
2405
2406                if (tree != null)
2407                    return tree.getEditingPath(this );
2408                return null;
2409            }
2410
2411            //
2412            // Following are primarily convenience methods for mapping from
2413            // row based selections to path selections.  Sometimes it is
2414            // easier to deal with these than paths (mouse downs, key downs
2415            // usually just deal with index based selections).
2416            // Since row based selections require a UI many of these won't work
2417            // without one.
2418            //
2419
2420            /**
2421             * Sets the tree's selection model. When a <code>null</code> value is
2422             * specified an empty
2423             * <code>selectionModel</code> is used, which does not allow selections.
2424             * <p>
2425             * This is a bound property.  
2426             *
2427             * @param selectionModel the <code>TreeSelectionModel</code> to use,
2428             *		or <code>null</code> to disable selections
2429             * @see TreeSelectionModel
2430             * @beaninfo
2431             *        bound: true
2432             *  description: The tree's selection model.
2433             */
2434            public void setSelectionModel(TreeSelectionModel selectionModel) {
2435                if (selectionModel == null)
2436                    selectionModel = EmptySelectionModel.sharedInstance();
2437
2438                TreeSelectionModel oldValue = this .selectionModel;
2439
2440                if (this .selectionModel != null && selectionRedirector != null) {
2441                    this .selectionModel
2442                            .removeTreeSelectionListener(selectionRedirector);
2443                }
2444                if (accessibleContext != null) {
2445                    this .selectionModel
2446                            .removeTreeSelectionListener((TreeSelectionListener) accessibleContext);
2447                    selectionModel
2448                            .addTreeSelectionListener((TreeSelectionListener) accessibleContext);
2449                }
2450
2451                this .selectionModel = selectionModel;
2452                if (selectionRedirector != null) {
2453                    this .selectionModel
2454                            .addTreeSelectionListener(selectionRedirector);
2455                }
2456                firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue,
2457                        this .selectionModel);
2458
2459                if (accessibleContext != null) {
2460                    accessibleContext.firePropertyChange(
2461                            AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
2462                            Boolean.valueOf(false), Boolean.valueOf(true));
2463                }
2464            }
2465
2466            /**
2467             * Returns the model for selections. This should always return a 
2468             * non-<code>null</code> value. If you don't want to allow anything
2469             * to be selected
2470             * set the selection model to <code>null</code>, which forces an empty
2471             * selection model to be used.
2472             *
2473             * @see #setSelectionModel
2474             */
2475            public TreeSelectionModel getSelectionModel() {
2476                return selectionModel;
2477            }
2478
2479            /**
2480             * Returns the paths (inclusive) between the specified rows. If
2481             * the specified indices are within the viewable set of rows, or
2482             * bound the viewable set of rows, then the indices are
2483             * constrained by the viewable set of rows. If the specified
2484             * indices are not within the viewable set of rows, or do not
2485             * bound the viewable set of rows, then an empty array is
2486             * returned. For example, if the row count is {@code 10}, and this
2487             * method is invoked with {@code -1, 20}, then the specified
2488             * indices are constrained to the viewable set of rows, and this is
2489             * treated as if invoked with {@code 0, 9}. On the other hand, if
2490             * this were invoked with {@code -10, -1}, then the specified
2491             * indices do not bound the viewable set of rows, and an empty
2492             * array is returned.
2493             * <p>
2494             * The parameters are not order dependent. That is, {@code
2495             * getPathBetweenRows(x, y)} is equivalent to
2496             * {@code getPathBetweenRows(y, x)}.
2497             * <p>
2498             * An empty array is returned if the row count is {@code 0}, or
2499             * the specified indices do not bound the viewable set of rows.
2500             *
2501             * @param index0 the first index in the range
2502             * @param index1 the last index in the range
2503             * @return the paths (inclusive) between the specified row indices
2504             */
2505            protected TreePath[] getPathBetweenRows(int index0, int index1) {
2506                TreeUI tree = getUI();
2507                if (tree != null) {
2508                    int rowCount = getRowCount();
2509                    if (rowCount > 0
2510                            && !((index0 < 0 && index1 < 0) || (index0 >= rowCount && index1 >= rowCount))) {
2511                        index0 = Math.min(rowCount - 1, Math.max(index0, 0));
2512                        index1 = Math.min(rowCount - 1, Math.max(index1, 0));
2513                        int minIndex = Math.min(index0, index1);
2514                        int maxIndex = Math.max(index0, index1);
2515                        TreePath[] selection = new TreePath[maxIndex - minIndex
2516                                + 1];
2517                        for (int counter = minIndex; counter <= maxIndex; counter++) {
2518                            selection[counter - minIndex] = tree.getPathForRow(
2519                                    this , counter);
2520                        }
2521                        return selection;
2522                    }
2523                }
2524                return new TreePath[0];
2525            }
2526
2527            /**
2528             * Selects the rows in the specified interval (inclusive). If
2529             * the specified indices are within the viewable set of rows, or bound
2530             * the viewable set of rows, then the specified rows are constrained by
2531             * the viewable set of rows. If the specified indices are not within the
2532             * viewable set of rows, or do not bound the viewable set of rows, then
2533             * the selection is cleared. For example, if the row count is {@code
2534             * 10}, and this method is invoked with {@code -1, 20}, then the
2535             * specified indices bounds the viewable range, and this is treated as
2536             * if invoked with {@code 0, 9}. On the other hand, if this were
2537             * invoked with {@code -10, -1}, then the specified indices do not
2538             * bound the viewable set of rows, and the selection is cleared.
2539             * <p>
2540             * The parameters are not order dependent. That is, {@code
2541             * setSelectionInterval(x, y)} is equivalent to
2542             * {@code setSelectionInterval(y, x)}.
2543             *
2544             * @param index0 the first index in the range to select
2545             * @param index1 the last index in the range to select
2546             */
2547            public void setSelectionInterval(int index0, int index1) {
2548                TreePath[] paths = getPathBetweenRows(index0, index1);
2549
2550                this .getSelectionModel().setSelectionPaths(paths);
2551            }
2552
2553            /**
2554             * Adds the specified rows (inclusive) to the selection. If the
2555             * specified indices are within the viewable set of rows, or bound
2556             * the viewable set of rows, then the specified indices are
2557             * constrained by the viewable set of rows. If the indices are not
2558             * within the viewable set of rows, or do not bound the viewable
2559             * set of rows, then the selection is unchanged. For example, if
2560             * the row count is {@code 10}, and this method is invoked with
2561             * {@code -1, 20}, then the specified indices bounds the viewable
2562             * range, and this is treated as if invoked with {@code 0, 9}. On
2563             * the other hand, if this were invoked with {@code -10, -1}, then
2564             * the specified indices do not bound the viewable set of rows,
2565             * and the selection is unchanged.
2566             * <p>
2567             * The parameters are not order dependent. That is, {@code
2568             * addSelectionInterval(x, y)} is equivalent to
2569             * {@code addSelectionInterval(y, x)}.
2570             *
2571             * @param index0 the first index in the range to add to the selection
2572             * @param index1 the last index in the range to add to the selection
2573             */
2574            public void addSelectionInterval(int index0, int index1) {
2575                TreePath[] paths = getPathBetweenRows(index0, index1);
2576
2577                if (paths != null && paths.length > 0) {
2578                    this .getSelectionModel().addSelectionPaths(paths);
2579                }
2580            }
2581
2582            /**
2583             * Removes the specified rows (inclusive) from the selection. If
2584             * the specified indices are within the viewable set of rows, or bound
2585             * the viewable set of rows, then the specified indices are constrained by
2586             * the viewable set of rows. If the specified indices are not within the
2587             * viewable set of rows, or do not bound the viewable set of rows, then
2588             * the selection is unchanged. For example, if the row count is {@code
2589             * 10}, and this method is invoked with {@code -1, 20}, then the
2590             * specified range bounds the viewable range, and this is treated as
2591             * if invoked with {@code 0, 9}. On the other hand, if this were
2592             * invoked with {@code -10, -1}, then the specified range does not
2593             * bound the viewable set of rows, and the selection is unchanged.
2594             * <p>
2595             * The parameters are not order dependent. That is, {@code
2596             * removeSelectionInterval(x, y)} is equivalent to
2597             * {@code removeSelectionInterval(y, x)}.
2598             *
2599             * @param index0 the first row to remove from the selection
2600             * @param index1 the last row to remove from the selection
2601             */
2602            public void removeSelectionInterval(int index0, int index1) {
2603                TreePath[] paths = getPathBetweenRows(index0, index1);
2604
2605                if (paths != null && paths.length > 0) {
2606                    this .getSelectionModel().removeSelectionPaths(paths);
2607                }
2608            }
2609
2610            /**
2611             * Removes the node identified by the specified path from the current
2612             * selection.
2613             * 
2614             * @param path  the <code>TreePath</code> identifying a node
2615             */
2616            public void removeSelectionPath(TreePath path) {
2617                this .getSelectionModel().removeSelectionPath(path);
2618            }
2619
2620            /**
2621             * Removes the nodes identified by the specified paths from the 
2622             * current selection.
2623             *
2624             * @param paths an array of <code>TreePath</code> objects that
2625             *              specifies the nodes to remove
2626             */
2627            public void removeSelectionPaths(TreePath[] paths) {
2628                this .getSelectionModel().removeSelectionPaths(paths);
2629            }
2630
2631            /**
2632             * Removes the row at the index <code>row</code> from the current
2633             * selection.
2634             * 
2635             * @param row  the row to remove
2636             */
2637            public void removeSelectionRow(int row) {
2638                int[] rows = { row };
2639
2640                removeSelectionRows(rows);
2641            }
2642
2643            /**
2644             * Removes the rows that are selected at each of the specified
2645             * rows.
2646             *
2647             * @param rows  an array of ints specifying display rows, where 0 is 
2648             *             the first row in the display
2649             */
2650            public void removeSelectionRows(int[] rows) {
2651                TreeUI ui = getUI();
2652
2653                if (ui != null && rows != null) {
2654                    int numRows = rows.length;
2655                    TreePath[] paths = new TreePath[numRows];
2656
2657                    for (int counter = 0; counter < numRows; counter++)
2658                        paths[counter] = ui.getPathForRow(this , rows[counter]);
2659                    removeSelectionPaths(paths);
2660                }
2661            }
2662
2663            /**
2664             * Clears the selection.
2665             */
2666            public void clearSelection() {
2667                getSelectionModel().clearSelection();
2668            }
2669
2670            /**
2671             * Returns true if the selection is currently empty.
2672             *
2673             * @return true if the selection is currently empty
2674             */
2675            public boolean isSelectionEmpty() {
2676                return getSelectionModel().isSelectionEmpty();
2677            }
2678
2679            /**
2680             * Adds a listener for <code>TreeExpansion</code> events.
2681             *
2682             * @param tel a TreeExpansionListener that will be notified when
2683             *            a tree node is expanded or collapsed (a "negative
2684             *            expansion")
2685             */
2686            public void addTreeExpansionListener(TreeExpansionListener tel) {
2687                if (settingUI) {
2688                    uiTreeExpansionListener = tel;
2689                }
2690                listenerList.add(TreeExpansionListener.class, tel);
2691            }
2692
2693            /**
2694             * Removes a listener for <code>TreeExpansion</code> events.
2695             *
2696             * @param tel the <code>TreeExpansionListener</code> to remove
2697             */
2698            public void removeTreeExpansionListener(TreeExpansionListener tel) {
2699                listenerList.remove(TreeExpansionListener.class, tel);
2700                if (uiTreeExpansionListener == tel) {
2701                    uiTreeExpansionListener = null;
2702                }
2703            }
2704
2705            /**
2706             * Returns an array of all the <code>TreeExpansionListener</code>s added
2707             * to this JTree with addTreeExpansionListener().
2708             *
2709             * @return all of the <code>TreeExpansionListener</code>s added or an empty
2710             *         array if no listeners have been added
2711             * @since 1.4
2712             */
2713            public TreeExpansionListener[] getTreeExpansionListeners() {
2714                return (TreeExpansionListener[]) listenerList
2715                        .getListeners(TreeExpansionListener.class);
2716            }
2717
2718            /**
2719             * Adds a listener for <code>TreeWillExpand</code> events.
2720             *
2721             * @param tel a <code>TreeWillExpandListener</code> that will be notified 
2722             *            when a tree node will be expanded or collapsed (a "negative
2723             *            expansion")
2724             */
2725            public void addTreeWillExpandListener(TreeWillExpandListener tel) {
2726                listenerList.add(TreeWillExpandListener.class, tel);
2727            }
2728
2729            /**
2730             * Removes a listener for <code>TreeWillExpand</code> events.
2731             *
2732             * @param tel the <code>TreeWillExpandListener</code> to remove
2733             */
2734            public void removeTreeWillExpandListener(TreeWillExpandListener tel) {
2735                listenerList.remove(TreeWillExpandListener.class, tel);
2736            }
2737
2738            /**
2739             * Returns an array of all the <code>TreeWillExpandListener</code>s added
2740             * to this JTree with addTreeWillExpandListener().
2741             *
2742             * @return all of the <code>TreeWillExpandListener</code>s added or an empty
2743             *         array if no listeners have been added
2744             * @since 1.4
2745             */
2746            public TreeWillExpandListener[] getTreeWillExpandListeners() {
2747                return (TreeWillExpandListener[]) listenerList
2748                        .getListeners(TreeWillExpandListener.class);
2749            }
2750
2751            /**
2752             * Notifies all listeners that have registered interest for
2753             * notification on this event type.  The event instance 
2754             * is lazily created using the <code>path</code> parameter.
2755             *
2756             * @param path the <code>TreePath</code> indicating the node that was
2757             *		expanded
2758             * @see EventListenerList
2759             */
2760            public void fireTreeExpanded(TreePath path) {
2761                // Guaranteed to return a non-null array
2762                Object[] listeners = listenerList.getListenerList();
2763                TreeExpansionEvent e = null;
2764                if (uiTreeExpansionListener != null) {
2765                    e = new TreeExpansionEvent(this , path);
2766                    uiTreeExpansionListener.treeExpanded(e);
2767                }
2768                // Process the listeners last to first, notifying
2769                // those that are interested in this event
2770                for (int i = listeners.length - 2; i >= 0; i -= 2) {
2771                    if (listeners[i] == TreeExpansionListener.class
2772                            && listeners[i + 1] != uiTreeExpansionListener) {
2773                        // Lazily create the event:
2774                        if (e == null)
2775                            e = new TreeExpansionEvent(this , path);
2776                        ((TreeExpansionListener) listeners[i + 1])
2777                                .treeExpanded(e);
2778                    }
2779                }
2780            }
2781
2782            /**
2783             * Notifies all listeners that have registered interest for
2784             * notification on this event type.  The event instance 
2785             * is lazily created using the <code>path</code> parameter.
2786             *
2787             * @param path the <code>TreePath</code> indicating the node that was
2788             *		collapsed
2789             * @see EventListenerList
2790             */
2791            public void fireTreeCollapsed(TreePath path) {
2792                // Guaranteed to return a non-null array
2793                Object[] listeners = listenerList.getListenerList();
2794                TreeExpansionEvent e = null;
2795                if (uiTreeExpansionListener != null) {
2796                    e = new TreeExpansionEvent(this , path);
2797                    uiTreeExpansionListener.treeCollapsed(e);
2798                }
2799                // Process the listeners last to first, notifying
2800                // those that are interested in this event
2801                for (int i = listeners.length - 2; i >= 0; i -= 2) {
2802                    if (listeners[i] == TreeExpansionListener.class
2803                            && listeners[i + 1] != uiTreeExpansionListener) {
2804                        // Lazily create the event:
2805                        if (e == null)
2806                            e = new TreeExpansionEvent(this , path);
2807                        ((TreeExpansionListener) listeners[i + 1])
2808                                .treeCollapsed(e);
2809                    }
2810                }
2811            }
2812
2813            /**
2814             * Notifies all listeners that have registered interest for
2815             * notification on this event type.  The event instance 
2816             * is lazily created using the <code>path</code> parameter.
2817             *
2818             * @param path the <code>TreePath</code> indicating the node that was
2819             *		expanded
2820             * @see EventListenerList
2821             */
2822            public void fireTreeWillExpand(TreePath path)
2823                    throws ExpandVetoException {
2824                // Guaranteed to return a non-null array
2825                Object[] listeners = listenerList.getListenerList();
2826                TreeExpansionEvent e = null;
2827                // Process the listeners last to first, notifying
2828                // those that are interested in this event
2829                for (int i = listeners.length - 2; i >= 0; i -= 2) {
2830                    if (listeners[i] == TreeWillExpandListener.class) {
2831                        // Lazily create the event:
2832                        if (e == null)
2833                            e = new TreeExpansionEvent(this , path);
2834                        ((TreeWillExpandListener) listeners[i + 1])
2835                                .treeWillExpand(e);
2836                    }
2837                }
2838            }
2839
2840            /**
2841             * Notifies all listeners that have registered interest for
2842             * notification on this event type.  The event instance 
2843             * is lazily created using the <code>path</code> parameter.
2844             *
2845             * @param path the <code>TreePath</code> indicating the node that was
2846             *		expanded
2847             * @see EventListenerList
2848             */
2849            public void fireTreeWillCollapse(TreePath path)
2850                    throws ExpandVetoException {
2851                // Guaranteed to return a non-null array
2852                Object[] listeners = listenerList.getListenerList();
2853                TreeExpansionEvent e = null;
2854                // Process the listeners last to first, notifying
2855                // those that are interested in this event
2856                for (int i = listeners.length - 2; i >= 0; i -= 2) {
2857                    if (listeners[i] == TreeWillExpandListener.class) {
2858                        // Lazily create the event:
2859                        if (e == null)
2860                            e = new TreeExpansionEvent(this , path);
2861                        ((TreeWillExpandListener) listeners[i + 1])
2862                                .treeWillCollapse(e);
2863                    }
2864                }
2865            }
2866
2867            /**
2868             * Adds a listener for <code>TreeSelection</code> events.
2869             *
2870             * @param tsl the <code>TreeSelectionListener</code> that will be notified 
2871             *            when a node is selected or deselected (a "negative
2872             *            selection")
2873             */
2874            public void addTreeSelectionListener(TreeSelectionListener tsl) {
2875                listenerList.add(TreeSelectionListener.class, tsl);
2876                if (listenerList.getListenerCount(TreeSelectionListener.class) != 0
2877                        && selectionRedirector == null) {
2878                    selectionRedirector = new TreeSelectionRedirector();
2879                    selectionModel
2880                            .addTreeSelectionListener(selectionRedirector);
2881                }
2882            }
2883
2884            /**
2885             * Removes a <code>TreeSelection</code> listener.
2886             *
2887             * @param tsl the <code>TreeSelectionListener</code> to remove
2888             */
2889            public void removeTreeSelectionListener(TreeSelectionListener tsl) {
2890                listenerList.remove(TreeSelectionListener.class, tsl);
2891                if (listenerList.getListenerCount(TreeSelectionListener.class) == 0
2892                        && selectionRedirector != null) {
2893                    selectionModel
2894                            .removeTreeSelectionListener(selectionRedirector);
2895                    selectionRedirector = null;
2896                }
2897            }
2898
2899            /**
2900             * Returns an array of all the <code>TreeSelectionListener</code>s added
2901             * to this JTree with addTreeSelectionListener().
2902             *
2903             * @return all of the <code>TreeSelectionListener</code>s added or an empty
2904             *         array if no listeners have been added
2905             * @since 1.4
2906             */
2907            public TreeSelectionListener[] getTreeSelectionListeners() {
2908                return (TreeSelectionListener[]) listenerList
2909                        .getListeners(TreeSelectionListener.class);
2910            }
2911
2912            /**
2913             * Notifies all listeners that have registered interest for
2914             * notification on this event type.  
2915             *
2916             * @param e the <code>TreeSelectionEvent</code> to be fired;
2917             *          generated by the
2918             *		<code>TreeSelectionModel</code>
2919             *          when a node is selected or deselected
2920             * @see EventListenerList
2921             */
2922            protected void fireValueChanged(TreeSelectionEvent e) {
2923                // Guaranteed to return a non-null array
2924                Object[] listeners = listenerList.getListenerList();
2925                // Process the listeners last to first, notifying
2926                // those that are interested in this event
2927                for (int i = listeners.length - 2; i >= 0; i -= 2) {
2928                    // TreeSelectionEvent e = null;
2929                    if (listeners[i] == TreeSelectionListener.class) {
2930                        // Lazily create the event:
2931                        // if (e == null)
2932                        // e = new ListSelectionEvent(this, firstIndex, lastIndex);
2933                        ((TreeSelectionListener) listeners[i + 1])
2934                                .valueChanged(e);
2935                    }
2936                }
2937            }
2938
2939            /**
2940             * Sent when the tree has changed enough that we need to resize
2941             * the bounds, but not enough that we need to remove the
2942             * expanded node set (e.g nodes were expanded or collapsed, or
2943             * nodes were inserted into the tree). You should never have to
2944             * invoke this, the UI will invoke this as it needs to.
2945             */
2946            public void treeDidChange() {
2947                revalidate();
2948                repaint();
2949            }
2950
2951            /**
2952             * Sets the number of rows that are to be displayed.
2953             * This will only work if the tree is contained in a 
2954             * <code>JScrollPane</code>,
2955             * and will adjust the preferred size and size of that scrollpane.
2956             * <p>
2957             * This is a bound property.  
2958             *
2959             * @param newCount the number of rows to display
2960             * @beaninfo
2961             *        bound: true
2962             *  description: The number of rows that are to be displayed.
2963             */
2964            public void setVisibleRowCount(int newCount) {
2965                int oldCount = visibleRowCount;
2966
2967                visibleRowCount = newCount;
2968                firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldCount,
2969                        visibleRowCount);
2970                invalidate();
2971                if (accessibleContext != null) {
2972                    ((AccessibleJTree) accessibleContext)
2973                            .fireVisibleDataPropertyChange();
2974                }
2975            }
2976
2977            /**
2978             * Returns the number of rows that are displayed in the display area.
2979             *
2980             * @return the number of rows displayed
2981             */
2982            public int getVisibleRowCount() {
2983                return visibleRowCount;
2984            }
2985
2986            /**
2987             * Expands the root path, assuming the current TreeModel has been set.
2988             */
2989            private void expandRoot() {
2990                TreeModel model = getModel();
2991
2992                if (model != null && model.getRoot() != null) {
2993                    expandPath(new TreePath(model.getRoot()));
2994                }
2995            }
2996
2997            /**
2998             * Returns the TreePath to the next tree element that 
2999             * begins with a prefix. To handle the conversion of a
3000             * <code>TreePath</code> into a String, <code>convertValueToText</code>
3001             * is used.
3002             *
3003             * @param prefix the string to test for a match
3004             * @param startingRow the row for starting the search
3005             * @param bias the search direction, either 
3006             * Position.Bias.Forward or Position.Bias.Backward.
3007             * @return the TreePath of the next tree element that
3008             * starts with the prefix; otherwise null
3009             * @exception IllegalArgumentException if prefix is null
3010             * or startingRow is out of bounds 
3011             * @since 1.4
3012             */
3013            public TreePath getNextMatch(String prefix, int startingRow,
3014                    Position.Bias bias) {
3015
3016                int max = getRowCount();
3017                if (prefix == null) {
3018                    throw new IllegalArgumentException();
3019                }
3020                if (startingRow < 0 || startingRow >= max) {
3021                    throw new IllegalArgumentException();
3022                }
3023                prefix = prefix.toUpperCase();
3024
3025                // start search from the next/previous element froom the 
3026                // selected element
3027                int increment = (bias == Position.Bias.Forward) ? 1 : -1;
3028                int row = startingRow;
3029                do {
3030                    TreePath path = getPathForRow(row);
3031                    String text = convertValueToText(path
3032                            .getLastPathComponent(), isRowSelected(row),
3033                            isExpanded(row), true, row, false);
3034
3035                    if (text.toUpperCase().startsWith(prefix)) {
3036                        return path;
3037                    }
3038                    row = (row + increment + max) % max;
3039                } while (row != startingRow);
3040                return null;
3041            }
3042
3043            // Serialization support.  
3044            private void writeObject(ObjectOutputStream s) throws IOException {
3045                Vector values = new Vector();
3046
3047                s.defaultWriteObject();
3048                // Save the cellRenderer, if its Serializable.
3049                if (cellRenderer != null
3050                        && cellRenderer instanceof  Serializable) {
3051                    values.addElement("cellRenderer");
3052                    values.addElement(cellRenderer);
3053                }
3054                // Save the cellEditor, if its Serializable.
3055                if (cellEditor != null && cellEditor instanceof  Serializable) {
3056                    values.addElement("cellEditor");
3057                    values.addElement(cellEditor);
3058                }
3059                // Save the treeModel, if its Serializable.
3060                if (treeModel != null && treeModel instanceof  Serializable) {
3061                    values.addElement("treeModel");
3062                    values.addElement(treeModel);
3063                }
3064                // Save the selectionModel, if its Serializable.
3065                if (selectionModel != null
3066                        && selectionModel instanceof  Serializable) {
3067                    values.addElement("selectionModel");
3068                    values.addElement(selectionModel);
3069                }
3070
3071                Object expandedData = getArchivableExpandedState();
3072
3073                if (expandedData != null) {
3074                    values.addElement("expandedState");
3075                    values.addElement(expandedData);
3076                }
3077
3078                s.writeObject(values);
3079                if (getUIClassID().equals(uiClassID)) {
3080                    byte count = JComponent.getWriteObjCounter(this );
3081                    JComponent.setWriteObjCounter(this , --count);
3082                    if (count == 0 && ui != null) {
3083                        ui.installUI(this );
3084                    }
3085                }
3086            }
3087
3088            private void readObject(ObjectInputStream s) throws IOException,
3089                    ClassNotFoundException {
3090                s.defaultReadObject();
3091
3092                // Create an instance of expanded state.
3093
3094                expandedState = new Hashtable();
3095
3096                expandedStack = new Stack();
3097
3098                Vector values = (Vector) s.readObject();
3099                int indexCounter = 0;
3100                int maxCounter = values.size();
3101
3102                if (indexCounter < maxCounter
3103                        && values.elementAt(indexCounter)
3104                                .equals("cellRenderer")) {
3105                    cellRenderer = (TreeCellRenderer) values
3106                            .elementAt(++indexCounter);
3107                    indexCounter++;
3108                }
3109                if (indexCounter < maxCounter
3110                        && values.elementAt(indexCounter).equals("cellEditor")) {
3111                    cellEditor = (TreeCellEditor) values
3112                            .elementAt(++indexCounter);
3113                    indexCounter++;
3114                }
3115                if (indexCounter < maxCounter
3116                        && values.elementAt(indexCounter).equals("treeModel")) {
3117                    treeModel = (TreeModel) values.elementAt(++indexCounter);
3118                    indexCounter++;
3119                }
3120                if (indexCounter < maxCounter
3121                        && values.elementAt(indexCounter).equals(
3122                                "selectionModel")) {
3123                    selectionModel = (TreeSelectionModel) values
3124                            .elementAt(++indexCounter);
3125                    indexCounter++;
3126                }
3127                if (indexCounter < maxCounter
3128                        && values.elementAt(indexCounter).equals(
3129                                "expandedState")) {
3130                    unarchiveExpandedState(values.elementAt(++indexCounter));
3131                    indexCounter++;
3132                }
3133                // Reinstall the redirector.
3134                if (listenerList.getListenerCount(TreeSelectionListener.class) != 0) {
3135                    selectionRedirector = new TreeSelectionRedirector();
3136                    selectionModel
3137                            .addTreeSelectionListener(selectionRedirector);
3138                }
3139                // Listener to TreeModel.
3140                if (treeModel != null) {
3141                    treeModelListener = createTreeModelListener();
3142                    if (treeModelListener != null)
3143                        treeModel.addTreeModelListener(treeModelListener);
3144                }
3145            }
3146
3147            /**
3148             * Returns an object that can be archived indicating what nodes are
3149             * expanded and what aren't. The objects from the model are NOT
3150             * written out.
3151             */
3152            private Object getArchivableExpandedState() {
3153                TreeModel model = getModel();
3154
3155                if (model != null) {
3156                    Enumeration paths = expandedState.keys();
3157
3158                    if (paths != null) {
3159                        Vector state = new Vector();
3160
3161                        while (paths.hasMoreElements()) {
3162                            TreePath path = (TreePath) paths.nextElement();
3163                            Object archivePath;
3164
3165                            try {
3166                                archivePath = getModelIndexsForPath(path);
3167                            } catch (Error error) {
3168                                archivePath = null;
3169                            }
3170                            if (archivePath != null) {
3171                                state.addElement(archivePath);
3172                                state.addElement(expandedState.get(path));
3173                            }
3174                        }
3175                        return state;
3176                    }
3177                }
3178                return null;
3179            }
3180
3181            /**
3182             * Updates the expanded state of nodes in the tree based on the 
3183             * previously archived state <code>state</code>.
3184             */
3185            private void unarchiveExpandedState(Object state) {
3186                if (state instanceof  Vector) {
3187                    Vector paths = (Vector) state;
3188
3189                    for (int counter = paths.size() - 1; counter >= 0; counter--) {
3190                        Boolean eState = (Boolean) paths.elementAt(counter--);
3191                        TreePath path;
3192
3193                        try {
3194                            path = getPathForIndexs((int[]) paths
3195                                    .elementAt(counter));
3196                            if (path != null)
3197                                expandedState.put(path, eState);
3198                        } catch (Error error) {
3199                        }
3200                    }
3201                }
3202            }
3203
3204            /**
3205             * Returns an array of integers specifying the indexs of the
3206             * components in the <code>path</code>. If <code>path</code> is
3207             * the root, this will return an empty array.  If <code>path</code>
3208             * is <code>null</code>, <code>null</code> will be returned.
3209             */
3210            private int[] getModelIndexsForPath(TreePath path) {
3211                if (path != null) {
3212                    TreeModel model = getModel();
3213                    int count = path.getPathCount();
3214                    int[] indexs = new int[count - 1];
3215                    Object parent = model.getRoot();
3216
3217                    for (int counter = 1; counter < count; counter++) {
3218                        indexs[counter - 1] = model.getIndexOfChild(parent,
3219                                path.getPathComponent(counter));
3220                        parent = path.getPathComponent(counter);
3221                        if (indexs[counter - 1] < 0)
3222                            return null;
3223                    }
3224                    return indexs;
3225                }
3226                return null;
3227            }
3228
3229            /**
3230             * Returns a <code>TreePath</code> created by obtaining the children
3231             * for each of the indices in <code>indexs</code>. If <code>indexs</code>
3232             * or the <code>TreeModel</code> is <code>null</code>, it will return
3233             * <code>null</code>.
3234             */
3235            private TreePath getPathForIndexs(int[] indexs) {
3236                if (indexs == null)
3237                    return null;
3238
3239                TreeModel model = getModel();
3240
3241                if (model == null)
3242                    return null;
3243
3244                int count = indexs.length;
3245                Object parent = model.getRoot();
3246                TreePath parentPath = new TreePath(parent);
3247
3248                for (int counter = 0; counter < count; counter++) {
3249                    parent = model.getChild(parent, indexs[counter]);
3250                    if (parent == null)
3251                        return null;
3252                    parentPath = parentPath.pathByAddingChild(parent);
3253                }
3254                return parentPath;
3255            }
3256
3257            /**
3258             * <code>EmptySelectionModel</code> is a <code>TreeSelectionModel</code>
3259             * that does not allow anything to be selected.
3260             * <p>
3261             * <strong>Warning:</strong>
3262             * Serialized objects of this class will not be compatible with
3263             * future Swing releases. The current serialization support is
3264             * appropriate for short term storage or RMI between applications running
3265             * the same version of Swing.  As of 1.4, support for long term storage
3266             * of all JavaBeans<sup><font size="-2">TM</font></sup>
3267             * has been added to the <code>java.beans</code> package.
3268             * Please see {@link java.beans.XMLEncoder}.
3269             */
3270            protected static class EmptySelectionModel extends
3271                    DefaultTreeSelectionModel {
3272                /**
3273                 * The single instance of {@code EmptySelectionModel}.
3274                 */
3275                protected static final EmptySelectionModel sharedInstance = new EmptySelectionModel();
3276
3277                /**
3278                 * Returns the single instance of {@code EmptySelectionModel}.
3279                 *
3280                 * @return single instance of {@code EmptySelectionModel}
3281                 */
3282                static public EmptySelectionModel sharedInstance() {
3283                    return sharedInstance;
3284                }
3285
3286                /**
3287                 * This is overriden to do nothing; {@code EmptySelectionModel}
3288                 * does not allow a selection.
3289                 *
3290                 * @param paths the paths to select; this is ignored
3291                 */
3292                public void setSelectionPaths(TreePath[] paths) {
3293                }
3294
3295                /**
3296                 * This is overriden to do nothing; {@code EmptySelectionModel}
3297                 * does not allow a selection.
3298                 *
3299                 * @param paths the paths to add to the selection; this is ignored
3300                 */
3301                public void addSelectionPaths(TreePath[] paths) {
3302                }
3303
3304                /**
3305                 * This is overriden to do nothing; {@code EmptySelectionModel}
3306                 * does not allow a selection.
3307                 *
3308                 * @param paths the paths to remove; this is ignored
3309                 */
3310                public void removeSelectionPaths(TreePath[] paths) {
3311                }
3312
3313                /**
3314                 * This is overriden to do nothing; {@code EmptySelectionModel}
3315                 * does not allow a selection.
3316                 *
3317                 * @param mode the selection mode; this is ignored
3318                 * @since 1.7
3319                 */
3320                public void setSelectionMode(int mode) {
3321                }
3322
3323                /**
3324                 * This is overriden to do nothing; {@code EmptySelectionModel}
3325                 * does not allow a selection.
3326                 *
3327                 * @param mapper the {@code RowMapper} instance; this is ignored
3328                 * @since 1.7
3329                 */
3330                public void setRowMapper(RowMapper mapper) {
3331                }
3332
3333                /**
3334                 * This is overriden to do nothing; {@code EmptySelectionModel}
3335                 * does not allow a selection.
3336                 *
3337                 * @param listener the listener to add; this is ignored
3338                 * @since 1.7
3339                 */
3340                public void addTreeSelectionListener(
3341                        TreeSelectionListener listener) {
3342                }
3343
3344                /**
3345                 * This is overriden to do nothing; {@code EmptySelectionModel}
3346                 * does not allow a selection.
3347                 *
3348                 * @param listener the listener to remove; this is ignored
3349                 * @since 1.7
3350                 */
3351                public void removeTreeSelectionListener(
3352                        TreeSelectionListener listener) {
3353                }
3354
3355                /**
3356                 * This is overriden to do nothing; {@code EmptySelectionModel}
3357                 * does not allow a selection.
3358                 *
3359                 * @param listener the listener to add; this is ignored
3360                 * @since 1.7
3361                 */
3362                public void addPropertyChangeListener(
3363                        PropertyChangeListener listener) {
3364                }
3365
3366                /**
3367                 * This is overriden to do nothing; {@code EmptySelectionModel}
3368                 * does not allow a selection.
3369                 *
3370                 * @param listener the listener to remove; this is ignored
3371                 * @since 1.7
3372                 */
3373                public void removePropertyChangeListener(
3374                        PropertyChangeListener listener) {
3375                }
3376            }
3377
3378            /**
3379             * Handles creating a new <code>TreeSelectionEvent</code> with the 
3380             * <code>JTree</code> as the
3381             * source and passing it off to all the listeners.
3382             * <p>
3383             * <strong>Warning:</strong>
3384             * Serialized objects of this class will not be compatible with
3385             * future Swing releases. The current serialization support is
3386             * appropriate for short term storage or RMI between applications running
3387             * the same version of Swing.  As of 1.4, support for long term storage
3388             * of all JavaBeans<sup><font size="-2">TM</font></sup>
3389             * has been added to the <code>java.beans</code> package.
3390             * Please see {@link java.beans.XMLEncoder}.
3391             */
3392            protected class TreeSelectionRedirector implements  Serializable,
3393                    TreeSelectionListener {
3394                /**
3395                 * Invoked by the <code>TreeSelectionModel</code> when the
3396                 * selection changes.
3397                 * 
3398                 * @param e the <code>TreeSelectionEvent</code> generated by the
3399                 *		<code>TreeSelectionModel</code>
3400                 */
3401                public void valueChanged(TreeSelectionEvent e) {
3402                    TreeSelectionEvent newE;
3403
3404                    newE = (TreeSelectionEvent) e.cloneWithSource(JTree.this );
3405                    fireValueChanged(newE);
3406                }
3407            } // End of class JTree.TreeSelectionRedirector
3408
3409            //
3410            // Scrollable interface
3411            //
3412
3413            /**
3414             * Returns the preferred display size of a <code>JTree</code>. The height is
3415             * determined from <code>getVisibleRowCount</code> and the width
3416             * is the current preferred width.
3417             *
3418             * @return a <code>Dimension</code> object containing the preferred size
3419             */
3420            public Dimension getPreferredScrollableViewportSize() {
3421                int width = getPreferredSize().width;
3422                int visRows = getVisibleRowCount();
3423                int height = -1;
3424
3425                if (isFixedRowHeight())
3426                    height = visRows * getRowHeight();
3427                else {
3428                    TreeUI ui = getUI();
3429
3430                    if (ui != null && visRows > 0) {
3431                        int rc = ui.getRowCount(this );
3432
3433                        if (rc >= visRows) {
3434                            Rectangle bounds = getRowBounds(visRows - 1);
3435                            if (bounds != null) {
3436                                height = bounds.y + bounds.height;
3437                            }
3438                        } else if (rc > 0) {
3439                            Rectangle bounds = getRowBounds(0);
3440                            if (bounds != null) {
3441                                height = bounds.height * visRows;
3442                            }
3443                        }
3444                    }
3445                    if (height == -1) {
3446                        height = 16 * visRows;
3447                    }
3448                }
3449                return new Dimension(width, height);
3450            }
3451
3452            /**
3453             * Returns the amount to increment when scrolling. The amount is
3454             * the height of the first displayed row that isn't completely in view
3455             * or, if it is totally displayed, the height of the next row in the
3456             * scrolling direction.
3457             * 
3458             * @param visibleRect the view area visible within the viewport
3459             * @param orientation either <code>SwingConstants.VERTICAL</code>
3460             *		or <code>SwingConstants.HORIZONTAL</code>
3461             * @param direction less than zero to scroll up/left,
3462             *		greater than zero for down/right
3463             * @return the "unit" increment for scrolling in the specified direction
3464             * @see JScrollBar#setUnitIncrement(int)
3465             */
3466            public int getScrollableUnitIncrement(Rectangle visibleRect,
3467                    int orientation, int direction) {
3468                if (orientation == SwingConstants.VERTICAL) {
3469                    Rectangle rowBounds;
3470                    int firstIndex = getClosestRowForLocation(0, visibleRect.y);
3471
3472                    if (firstIndex != -1) {
3473                        rowBounds = getRowBounds(firstIndex);
3474                        if (rowBounds.y != visibleRect.y) {
3475                            if (direction < 0) {
3476                                // UP
3477                                return Math.max(0,
3478                                        (visibleRect.y - rowBounds.y));
3479                            }
3480                            return (rowBounds.y + rowBounds.height - visibleRect.y);
3481                        }
3482                        if (direction < 0) { // UP
3483                            if (firstIndex != 0) {
3484                                rowBounds = getRowBounds(firstIndex - 1);
3485                                return rowBounds.height;
3486                            }
3487                        } else {
3488                            return rowBounds.height;
3489                        }
3490                    }
3491                    return 0;
3492                }
3493                return 4;
3494            }
3495
3496            /**
3497             * Returns the amount for a block increment, which is the height or
3498             * width of <code>visibleRect</code>, based on <code>orientation</code>.
3499             * 
3500             * @param visibleRect the view area visible within the viewport
3501             * @param orientation either <code>SwingConstants.VERTICAL</code>
3502             *		or <code>SwingConstants.HORIZONTAL</code>
3503             * @param direction less than zero to scroll up/left,
3504             *		greater than zero for down/right.
3505             * @return the "block" increment for scrolling in the specified direction
3506             * @see JScrollBar#setBlockIncrement(int)
3507             */
3508            public int getScrollableBlockIncrement(Rectangle visibleRect,
3509                    int orientation, int direction) {
3510                return (orientation == SwingConstants.VERTICAL) ? visibleRect.height
3511                        : visibleRect.width;
3512            }
3513
3514            /**
3515             * Returns false to indicate that the width of the viewport does not 
3516             * determine the width of the table, unless the preferred width of 
3517             * the tree is smaller than the viewports width.  In other words: 
3518             * ensure that the tree is never smaller than its viewport.
3519             * 
3520             * @return whether the tree should track the width of the viewport
3521             * @see Scrollable#getScrollableTracksViewportWidth
3522             */
3523            public boolean getScrollableTracksViewportWidth() {
3524                if (getParent() instanceof  JViewport) {
3525                    return (((JViewport) getParent()).getWidth() > getPreferredSize().width);
3526                }
3527                return false;
3528            }
3529
3530            /**
3531             * Returns false to indicate that the height of the viewport does not 
3532             * determine the height of the table, unless the preferred height
3533             * of the tree is smaller than the viewports height.  In other words: 
3534             * ensure that the tree is never smaller than its viewport.
3535             * 
3536             * @return whether the tree should track the height of the viewport
3537             * @see Scrollable#getScrollableTracksViewportHeight
3538             */
3539            public boolean getScrollableTracksViewportHeight() {
3540                if (getParent() instanceof  JViewport) {
3541                    return (((JViewport) getParent()).getHeight() > getPreferredSize().height);
3542                }
3543                return false;
3544            }
3545
3546            /**
3547             * Sets the expanded state of this <code>JTree</code>.
3548             * If <code>state</code> is
3549             * true, all parents of <code>path</code> and path are marked as
3550             * expanded. If <code>state</code> is false, all parents of 
3551             * <code>path</code> are marked EXPANDED, but <code>path</code> itself
3552             * is marked collapsed.<p>
3553             * This will fail if a <code>TreeWillExpandListener</code> vetos it.
3554             */
3555            protected void setExpandedState(TreePath path, boolean state) {
3556                if (path != null) {
3557                    // Make sure all parents of path are expanded.
3558                    Stack stack;
3559                    TreePath parentPath = path.getParentPath();
3560
3561                    if (expandedStack.size() == 0) {
3562                        stack = new Stack();
3563                    } else {
3564                        stack = (Stack) expandedStack.pop();
3565                    }
3566
3567                    try {
3568                        while (parentPath != null) {
3569                            if (isExpanded(parentPath)) {
3570                                parentPath = null;
3571                            } else {
3572                                stack.push(parentPath);
3573                                parentPath = parentPath.getParentPath();
3574                            }
3575                        }
3576                        for (int counter = stack.size() - 1; counter >= 0; counter--) {
3577                            parentPath = (TreePath) stack.pop();
3578                            if (!isExpanded(parentPath)) {
3579                                try {
3580                                    fireTreeWillExpand(parentPath);
3581                                } catch (ExpandVetoException eve) {
3582                                    // Expand vetoed!
3583                                    return;
3584                                }
3585                                expandedState.put(parentPath, Boolean.TRUE);
3586                                fireTreeExpanded(parentPath);
3587                                if (accessibleContext != null) {
3588                                    ((AccessibleJTree) accessibleContext)
3589                                            .fireVisibleDataPropertyChange();
3590                                }
3591                            }
3592                        }
3593                    } finally {
3594                        if (expandedStack.size() < TEMP_STACK_SIZE) {
3595                            stack.removeAllElements();
3596                            expandedStack.push(stack);
3597                        }
3598                    }
3599                    if (!state) {
3600                        // collapse last path.
3601                        Object cValue = expandedState.get(path);
3602
3603                        if (cValue != null && ((Boolean) cValue).booleanValue()) {
3604                            try {
3605                                fireTreeWillCollapse(path);
3606                            } catch (ExpandVetoException eve) {
3607                                return;
3608                            }
3609                            expandedState.put(path, Boolean.FALSE);
3610                            fireTreeCollapsed(path);
3611                            if (removeDescendantSelectedPaths(path, false)
3612                                    && !isPathSelected(path)) {
3613                                // A descendant was selected, select the parent.
3614                                addSelectionPath(path);
3615                            }
3616                            if (accessibleContext != null) {
3617                                ((AccessibleJTree) accessibleContext)
3618                                        .fireVisibleDataPropertyChange();
3619                            }
3620                        }
3621                    } else {
3622                        // Expand last path.
3623                        Object cValue = expandedState.get(path);
3624
3625                        if (cValue == null
3626                                || !((Boolean) cValue).booleanValue()) {
3627                            try {
3628                                fireTreeWillExpand(path);
3629                            } catch (ExpandVetoException eve) {
3630                                return;
3631                            }
3632                            expandedState.put(path, Boolean.TRUE);
3633                            fireTreeExpanded(path);
3634                            if (accessibleContext != null) {
3635                                ((AccessibleJTree) accessibleContext)
3636                                        .fireVisibleDataPropertyChange();
3637                            }
3638                        }
3639                    }
3640                }
3641            }
3642
3643            /**
3644             * Returns an <code>Enumeration</code> of <code>TreePaths</code>
3645             * that have been expanded that
3646             * are descendants of <code>parent</code>.
3647             */
3648            protected Enumeration<TreePath> getDescendantToggledPaths(
3649                    TreePath parent) {
3650                if (parent == null)
3651                    return null;
3652
3653                Vector descendants = new Vector();
3654                Enumeration nodes = expandedState.keys();
3655                TreePath path;
3656
3657                while (nodes.hasMoreElements()) {
3658                    path = (TreePath) nodes.nextElement();
3659                    if (parent.isDescendant(path))
3660                        descendants.addElement(path);
3661                }
3662                return descendants.elements();
3663            }
3664
3665            /**
3666             * Removes any descendants of the <code>TreePaths</code> in
3667             * <code>toRemove</code>
3668             * that have been expanded.
3669             *
3670             * @param toRemove an enumeration of the paths to remove; a value of
3671             *        {@code null} is ignored
3672             * @throws ClassCastException if {@code toRemove} contains an
3673             *         element that is not a {@code TreePath}; {@code null}
3674             *         values are ignored
3675             */
3676            protected void removeDescendantToggledPaths(
3677                    Enumeration<TreePath> toRemove) {
3678                if (toRemove != null) {
3679                    while (toRemove.hasMoreElements()) {
3680                        Enumeration descendants = getDescendantToggledPaths((TreePath) toRemove
3681                                .nextElement());
3682
3683                        if (descendants != null) {
3684                            while (descendants.hasMoreElements()) {
3685                                expandedState.remove(descendants.nextElement());
3686                            }
3687                        }
3688                    }
3689                }
3690            }
3691
3692            /**
3693             * Clears the cache of toggled tree paths. This does NOT send out
3694             * any <code>TreeExpansionListener</code> events.
3695             */
3696            protected void clearToggledPaths() {
3697                expandedState.clear();
3698            }
3699
3700            /**
3701             * Creates and returns an instance of <code>TreeModelHandler</code>.
3702             * The returned
3703             * object is responsible for updating the expanded state when the
3704             * <code>TreeModel</code> changes.
3705             * <p>
3706             * For more information on what expanded state means, see the
3707             * <a href=#jtree_description>JTree description</a> above.
3708             */
3709            protected TreeModelListener createTreeModelListener() {
3710                return new TreeModelHandler();
3711            }
3712
3713            /**
3714             * Removes any paths in the selection that are descendants of
3715             * <code>path</code>. If <code>includePath</code> is true and
3716             * <code>path</code> is selected, it will be removed from the selection.
3717             *
3718             * @return true if a descendant was selected
3719             * @since 1.3
3720             */
3721            protected boolean removeDescendantSelectedPaths(TreePath path,
3722                    boolean includePath) {
3723                TreePath[] toRemove = getDescendantSelectedPaths(path,
3724                        includePath);
3725
3726                if (toRemove != null) {
3727                    getSelectionModel().removeSelectionPaths(toRemove);
3728                    return true;
3729                }
3730                return false;
3731            }
3732
3733            /**
3734             * Returns an array of paths in the selection that are descendants of
3735             * <code>path</code>. The returned array may contain <code>null</code>s.
3736             */
3737            private TreePath[] getDescendantSelectedPaths(TreePath path,
3738                    boolean includePath) {
3739                TreeSelectionModel sm = getSelectionModel();
3740                TreePath[] selPaths = (sm != null) ? sm.getSelectionPaths()
3741                        : null;
3742
3743                if (selPaths != null) {
3744                    boolean shouldRemove = false;
3745
3746                    for (int counter = selPaths.length - 1; counter >= 0; counter--) {
3747                        if (selPaths[counter] != null
3748                                && path.isDescendant(selPaths[counter])
3749                                && (!path.equals(selPaths[counter]) || includePath))
3750                            shouldRemove = true;
3751                        else
3752                            selPaths[counter] = null;
3753                    }
3754                    if (!shouldRemove) {
3755                        selPaths = null;
3756                    }
3757                    return selPaths;
3758                }
3759                return null;
3760            }
3761
3762            /**
3763             * Removes any paths from the selection model that are descendants of
3764             * the nodes identified by in <code>e</code>.
3765             */
3766            void removeDescendantSelectedPaths(TreeModelEvent e) {
3767                TreePath pPath = e.getTreePath();
3768                Object[] oldChildren = e.getChildren();
3769                TreeSelectionModel sm = getSelectionModel();
3770
3771                if (sm != null && pPath != null && oldChildren != null
3772                        && oldChildren.length > 0) {
3773                    for (int counter = oldChildren.length - 1; counter >= 0; counter--) {
3774                        // Might be better to call getDescendantSelectedPaths
3775                        // numerous times, then push to the model.
3776                        removeDescendantSelectedPaths(pPath
3777                                .pathByAddingChild(oldChildren[counter]), true);
3778                    }
3779                }
3780            }
3781
3782            /**
3783             * Listens to the model and updates the <code>expandedState</code>
3784             * accordingly when nodes are removed, or changed.
3785             */
3786            protected class TreeModelHandler implements  TreeModelListener {
3787                public void treeNodesChanged(TreeModelEvent e) {
3788                }
3789
3790                public void treeNodesInserted(TreeModelEvent e) {
3791                }
3792
3793                public void treeStructureChanged(TreeModelEvent e) {
3794                    if (e == null)
3795                        return;
3796
3797                    // NOTE: If I change this to NOT remove the descendants
3798                    // and update BasicTreeUIs treeStructureChanged method
3799                    // to update descendants in response to a treeStructureChanged
3800                    // event, all the children of the event won't collapse!
3801                    TreePath parent = e.getTreePath();
3802
3803                    if (parent == null)
3804                        return;
3805
3806                    if (parent.getPathCount() == 1) {
3807                        // New root, remove everything!
3808                        clearToggledPaths();
3809                        if (treeModel.getRoot() != null
3810                                && !treeModel.isLeaf(treeModel.getRoot())) {
3811                            // Mark the root as expanded, if it isn't a leaf.
3812                            expandedState.put(parent, Boolean.TRUE);
3813                        }
3814                    } else if (expandedState.get(parent) != null) {
3815                        Vector<TreePath> toRemove = new Vector<TreePath>(1);
3816                        boolean isExpanded = isExpanded(parent);
3817
3818                        toRemove.addElement(parent);
3819                        removeDescendantToggledPaths(toRemove.elements());
3820                        if (isExpanded) {
3821                            TreeModel model = getModel();
3822
3823                            if (model == null
3824                                    || model.isLeaf(parent
3825                                            .getLastPathComponent()))
3826                                collapsePath(parent);
3827                            else
3828                                expandedState.put(parent, Boolean.TRUE);
3829                        }
3830                    }
3831                    removeDescendantSelectedPaths(parent, false);
3832                }
3833
3834                public void treeNodesRemoved(TreeModelEvent e) {
3835                    if (e == null)
3836                        return;
3837
3838                    TreePath parent = e.getTreePath();
3839                    Object[] children = e.getChildren();
3840
3841                    if (children == null)
3842                        return;
3843
3844                    TreePath rPath;
3845                    Vector<TreePath> toRemove = new Vector<TreePath>(Math.max(
3846                            1, children.length));
3847
3848                    for (int counter = children.length - 1; counter >= 0; counter--) {
3849                        rPath = parent.pathByAddingChild(children[counter]);
3850                        if (expandedState.get(rPath) != null)
3851                            toRemove.addElement(rPath);
3852                    }
3853                    if (toRemove.size() > 0)
3854                        removeDescendantToggledPaths(toRemove.elements());
3855
3856                    TreeModel model = getModel();
3857
3858                    if (model == null
3859                            || model.isLeaf(parent.getLastPathComponent()))
3860                        expandedState.remove(parent);
3861
3862                    removeDescendantSelectedPaths(e);
3863                }
3864            }
3865
3866            /**
3867             * <code>DynamicUtilTreeNode</code> can wrap 
3868             * vectors/hashtables/arrays/strings and
3869             * create the appropriate children tree nodes as necessary. It is
3870             * dynamic in that it will only create the children as necessary.
3871             * <p>
3872             * <strong>Warning:</strong>
3873             * Serialized objects of this class will not be compatible with
3874             * future Swing releases. The current serialization support is
3875             * appropriate for short term storage or RMI between applications running
3876             * the same version of Swing.  As of 1.4, support for long term storage
3877             * of all JavaBeans<sup><font size="-2">TM</font></sup>
3878             * has been added to the <code>java.beans</code> package.
3879             * Please see {@link java.beans.XMLEncoder}.
3880             */
3881            public static class DynamicUtilTreeNode extends
3882                    DefaultMutableTreeNode {
3883                /**
3884                 * Does the this <code>JTree</code> have children?
3885                 * This property is currently not implemented.
3886                 */
3887                protected boolean hasChildren;
3888                /** Value to create children with. */
3889                protected Object childValue;
3890                /** Have the children been loaded yet? */
3891                protected boolean loadedChildren;
3892
3893                /**
3894                 * Adds to parent all the children in <code>children</code>.
3895                 * If <code>children</code> is an array or vector all of its
3896                 * elements are added is children, otherwise if <code>children</code>
3897                 * is a hashtable all the key/value pairs are added in the order
3898                 * <code>Enumeration</code> returns them.
3899                 */
3900                public static void createChildren(
3901                        DefaultMutableTreeNode parent, Object children) {
3902                    if (children instanceof  Vector) {
3903                        Vector childVector = (Vector) children;
3904
3905                        for (int counter = 0, maxCounter = childVector.size(); counter < maxCounter; counter++)
3906                            parent.add(new DynamicUtilTreeNode(childVector
3907                                    .elementAt(counter), childVector
3908                                    .elementAt(counter)));
3909                    } else if (children instanceof  Hashtable) {
3910                        Hashtable childHT = (Hashtable) children;
3911                        Enumeration keys = childHT.keys();
3912                        Object aKey;
3913
3914                        while (keys.hasMoreElements()) {
3915                            aKey = keys.nextElement();
3916                            parent.add(new DynamicUtilTreeNode(aKey, childHT
3917                                    .get(aKey)));
3918                        }
3919                    } else if (children instanceof  Object[]) {
3920                        Object[] childArray = (Object[]) children;
3921
3922                        for (int counter = 0, maxCounter = childArray.length; counter < maxCounter; counter++)
3923                            parent.add(new DynamicUtilTreeNode(
3924                                    childArray[counter], childArray[counter]));
3925                    }
3926                }
3927
3928                /**
3929                 * Creates a node with the specified object as its value and
3930                 * with the specified children. For the node to allow children,
3931                 * the children-object must be an array of objects, a
3932                 * <code>Vector</code>, or a <code>Hashtable</code> -- even
3933                 * if empty. Otherwise, the node is not
3934                 * allowed to have children.
3935                 *
3936                 * @param value  the <code>Object</code> that is the value for the
3937                 *		new node
3938                 * @param children an array of <code>Object</code>s, a
3939                 *		<code>Vector</code>, or a <code>Hashtable</code>
3940                 *		used to create the child nodes; if any other
3941                 *		object is specified, or if the value is
3942                 *		<code>null</code>,
3943                 *		then the node is not allowed to have children
3944                 */
3945                public DynamicUtilTreeNode(Object value, Object children) {
3946                    super (value);
3947                    loadedChildren = false;
3948                    childValue = children;
3949                    if (children != null) {
3950                        if (children instanceof  Vector)
3951                            setAllowsChildren(true);
3952                        else if (children instanceof  Hashtable)
3953                            setAllowsChildren(true);
3954                        else if (children instanceof  Object[])
3955                            setAllowsChildren(true);
3956                        else
3957                            setAllowsChildren(false);
3958                    } else
3959                        setAllowsChildren(false);
3960                }
3961
3962                /**
3963                 * Returns true if this node allows children. Whether the node
3964                 * allows children depends on how it was created.
3965                 *
3966                 * @return true if this node allows children, false otherwise
3967                 * @see #JTree.DynamicUtilTreeNode
3968                 */
3969                public boolean isLeaf() {
3970                    return !getAllowsChildren();
3971                }
3972
3973                /**
3974                 * Returns the number of child nodes.
3975                 *
3976                 * @return the number of child nodes
3977                 */
3978                public int getChildCount() {
3979                    if (!loadedChildren)
3980                        loadChildren();
3981                    return super .getChildCount();
3982                }
3983
3984                /**
3985                 * Loads the children based on <code>childValue</code>.
3986                 * If <code>childValue</code> is a <code>Vector</code>
3987                 * or array each element is added as a child,
3988                 * if <code>childValue</code> is a <code>Hashtable</code>
3989                 * each key/value pair is added in the order that
3990                 * <code>Enumeration</code> returns the keys.
3991                 */
3992                protected void loadChildren() {
3993                    loadedChildren = true;
3994                    createChildren(this , childValue);
3995                }
3996
3997                /**
3998                 * Subclassed to load the children, if necessary.
3999                 */
4000                public TreeNode getChildAt(int index) {
4001                    if (!loadedChildren)
4002                        loadChildren();
4003                    return super .getChildAt(index);
4004                }
4005
4006                /**
4007                 * Subclassed to load the children, if necessary.
4008                 */
4009                public Enumeration children() {
4010                    if (!loadedChildren)
4011                        loadChildren();
4012                    return super .children();
4013                }
4014            }
4015
4016            void setUIProperty(String propertyName, Object value) {
4017                if (propertyName == "rowHeight") {
4018                    if (!rowHeightSet) {
4019                        setRowHeight(((Number) value).intValue());
4020                        rowHeightSet = false;
4021                    }
4022                } else if (propertyName == "scrollsOnExpand") {
4023                    if (!scrollsOnExpandSet) {
4024                        setScrollsOnExpand(((Boolean) value).booleanValue());
4025                        scrollsOnExpandSet = false;
4026                    }
4027                } else if (propertyName == "showsRootHandles") {
4028                    if (!showsRootHandlesSet) {
4029                        setShowsRootHandles(((Boolean) value).booleanValue());
4030                        showsRootHandlesSet = false;
4031                    }
4032                } else {
4033                    super .setUIProperty(propertyName, value);
4034                }
4035            }
4036
4037            /**
4038             * Returns a string representation of this <code>JTree</code>.
4039             * This method 
4040             * is intended to be used only for debugging purposes, and the 
4041             * content and format of the returned string may vary between      
4042             * implementations. The returned string may be empty but may not 
4043             * be <code>null</code>.
4044             * 
4045             * @return  a string representation of this <code>JTree</code>.
4046             */
4047            protected String paramString() {
4048                String rootVisibleString = (rootVisible ? "true" : "false");
4049                String showsRootHandlesString = (showsRootHandles ? "true"
4050                        : "false");
4051                String editableString = (editable ? "true" : "false");
4052                String largeModelString = (largeModel ? "true" : "false");
4053                String invokesStopCellEditingString = (invokesStopCellEditing ? "true"
4054                        : "false");
4055                String scrollsOnExpandString = (scrollsOnExpand ? "true"
4056                        : "false");
4057
4058                return super .paramString() + ",editable=" + editableString
4059                        + ",invokesStopCellEditing="
4060                        + invokesStopCellEditingString + ",largeModel="
4061                        + largeModelString + ",rootVisible="
4062                        + rootVisibleString + ",rowHeight=" + rowHeight
4063                        + ",scrollsOnExpand=" + scrollsOnExpandString
4064                        + ",showsRootHandles=" + showsRootHandlesString
4065                        + ",toggleClickCount=" + toggleClickCount
4066                        + ",visibleRowCount=" + visibleRowCount;
4067            }
4068
4069            /////////////////
4070            // Accessibility support
4071            ////////////////
4072
4073            /**
4074             * Gets the AccessibleContext associated with this JTree. 
4075             * For JTrees, the AccessibleContext takes the form of an 
4076             * AccessibleJTree. 
4077             * A new AccessibleJTree instance is created if necessary.
4078             *
4079             * @return an AccessibleJTree that serves as the 
4080             *         AccessibleContext of this JTree
4081             */
4082            public AccessibleContext getAccessibleContext() {
4083                if (accessibleContext == null) {
4084                    accessibleContext = new AccessibleJTree();
4085                }
4086                return accessibleContext;
4087            }
4088
4089            /**
4090             * This class implements accessibility support for the 
4091             * <code>JTree</code> class.  It provides an implementation of the 
4092             * Java Accessibility API appropriate to tree user-interface elements.
4093             * <p>
4094             * <strong>Warning:</strong>
4095             * Serialized objects of this class will not be compatible with
4096             * future Swing releases. The current serialization support is
4097             * appropriate for short term storage or RMI between applications running
4098             * the same version of Swing.  As of 1.4, support for long term storage
4099             * of all JavaBeans<sup><font size="-2">TM</font></sup>
4100             * has been added to the <code>java.beans</code> package.
4101             * Please see {@link java.beans.XMLEncoder}.
4102             */
4103            protected class AccessibleJTree extends AccessibleJComponent
4104                    implements  AccessibleSelection, TreeSelectionListener,
4105                    TreeModelListener, TreeExpansionListener {
4106
4107                TreePath leadSelectionPath;
4108                Accessible leadSelectionAccessible;
4109
4110                public AccessibleJTree() {
4111                    // Add a tree model listener for JTree
4112                    TreeModel model = JTree.this .getModel();
4113                    if (model != null) {
4114                        model.addTreeModelListener(this );
4115                    }
4116                    JTree.this .addTreeExpansionListener(this );
4117                    JTree.this .addTreeSelectionListener(this );
4118                    leadSelectionPath = JTree.this .getLeadSelectionPath();
4119                    leadSelectionAccessible = (leadSelectionPath != null) ? new AccessibleJTreeNode(
4120                            JTree.this , leadSelectionPath, JTree.this )
4121                            : null;
4122                }
4123
4124                /**
4125                 * Tree Selection Listener value change method. Used to fire the 
4126                 * property change
4127                 *
4128                 * @param e ListSelectionEvent
4129                 *
4130                 */
4131                public void valueChanged(TreeSelectionEvent e) {
4132                    // Fixes 4546503 - JTree is sending incorrect active 
4133                    // descendant events
4134                    TreePath oldLeadSelectionPath = e.getOldLeadSelectionPath();
4135                    leadSelectionPath = e.getNewLeadSelectionPath();
4136
4137                    if (oldLeadSelectionPath != leadSelectionPath) {
4138                        // Set parent to null so AccessibleJTreeNode computes 
4139                        // its parent.
4140                        Accessible oldLSA = leadSelectionAccessible;
4141                        leadSelectionAccessible = (leadSelectionPath != null) ? new AccessibleJTreeNode(
4142                                JTree.this , leadSelectionPath, null) // parent
4143                                : null;
4144                        firePropertyChange(
4145                                AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
4146                                oldLSA, leadSelectionAccessible);
4147                    }
4148                    firePropertyChange(
4149                            AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
4150                            Boolean.valueOf(false), Boolean.valueOf(true));
4151                }
4152
4153                /**
4154                 * Fire a visible data property change notification.
4155                 * A 'visible' data property is one that represents
4156                 * something about the way the component appears on the
4157                 * display, where that appearance isn't bound to any other
4158                 * property. It notifies screen readers  that the visual 
4159                 * appearance of the component has changed, so they can 
4160                 * notify the user.
4161                 */
4162                public void fireVisibleDataPropertyChange() {
4163                    firePropertyChange(
4164                            AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
4165                            Boolean.valueOf(false), Boolean.valueOf(true));
4166                }
4167
4168                // Fire the visible data changes for the model changes.
4169
4170                /**
4171                 * Tree Model Node change notification.
4172                 *
4173                 * @param e  a Tree Model event
4174                 */
4175                public void treeNodesChanged(TreeModelEvent e) {
4176                    fireVisibleDataPropertyChange();
4177                }
4178
4179                /**
4180                 * Tree Model Node change notification.
4181                 *
4182                 * @param e  a Tree node insertion event
4183                 */
4184                public void treeNodesInserted(TreeModelEvent e) {
4185                    fireVisibleDataPropertyChange();
4186                }
4187
4188                /**
4189                 * Tree Model Node change notification.
4190                 *
4191                 * @param e  a Tree node(s) removal event
4192                 */
4193                public void treeNodesRemoved(TreeModelEvent e) {
4194                    fireVisibleDataPropertyChange();
4195                }
4196
4197                /**
4198                 * Tree Model structure change change notification.
4199                 *
4200                 * @param e  a Tree Model event
4201                 */
4202                public void treeStructureChanged(TreeModelEvent e) {
4203                    fireVisibleDataPropertyChange();
4204                }
4205
4206                /**
4207                 * Tree Collapsed notification.
4208                 *
4209                 * @param e  a TreeExpansionEvent
4210                 */
4211                public void treeCollapsed(TreeExpansionEvent e) {
4212                    fireVisibleDataPropertyChange();
4213                    TreePath path = e.getPath();
4214                    if (path != null) {
4215                        // Set parent to null so AccessibleJTreeNode computes 
4216                        // its parent.
4217                        AccessibleJTreeNode node = new AccessibleJTreeNode(
4218                                JTree.this , path, null);
4219                        PropertyChangeEvent pce = new PropertyChangeEvent(node,
4220                                AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4221                                AccessibleState.EXPANDED,
4222                                AccessibleState.COLLAPSED);
4223                        firePropertyChange(
4224                                AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4225                                null, pce);
4226                    }
4227                }
4228
4229                /**
4230                 * Tree Model Expansion notification.
4231                 *
4232                 * @param e  a Tree node insertion event
4233                 */
4234                public void treeExpanded(TreeExpansionEvent e) {
4235                    fireVisibleDataPropertyChange();
4236                    TreePath path = e.getPath();
4237                    if (path != null) {
4238                        // TIGER - 4839971 
4239                        // Set parent to null so AccessibleJTreeNode computes 
4240                        // its parent.
4241                        AccessibleJTreeNode node = new AccessibleJTreeNode(
4242                                JTree.this , path, null);
4243                        PropertyChangeEvent pce = new PropertyChangeEvent(node,
4244                                AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4245                                AccessibleState.COLLAPSED,
4246                                AccessibleState.EXPANDED);
4247                        firePropertyChange(
4248                                AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4249                                null, pce);
4250                    }
4251                }
4252
4253                private AccessibleContext getCurrentAccessibleContext() {
4254                    Component c = getCurrentComponent();
4255                    if (c instanceof  Accessible) {
4256                        return (((Accessible) c).getAccessibleContext());
4257                    } else {
4258                        return null;
4259                    }
4260                }
4261
4262                private Component getCurrentComponent() {
4263                    // is the object visible?
4264                    // if so, get row, selected, focus & leaf state, 
4265                    // and then get the renderer component and return it
4266                    TreeModel model = JTree.this .getModel();
4267                    if (model == null) {
4268                        return null;
4269                    }
4270                    TreePath path = new TreePath(model.getRoot());
4271                    if (JTree.this .isVisible(path)) {
4272                        TreeCellRenderer r = JTree.this .getCellRenderer();
4273                        TreeUI ui = JTree.this .getUI();
4274                        if (ui != null) {
4275                            int row = ui.getRowForPath(JTree.this , path);
4276                            int lsr = JTree.this .getLeadSelectionRow();
4277                            boolean hasFocus = JTree.this .isFocusOwner()
4278                                    && (lsr == row);
4279                            boolean selected = JTree.this .isPathSelected(path);
4280                            boolean expanded = JTree.this .isExpanded(path);
4281
4282                            return r.getTreeCellRendererComponent(JTree.this ,
4283                                    model.getRoot(), selected, expanded, model
4284                                            .isLeaf(model.getRoot()), row,
4285                                    hasFocus);
4286                        }
4287                    }
4288                    return null;
4289                }
4290
4291                // Overridden methods from AccessibleJComponent
4292
4293                /**
4294                 * Get the role of this object.
4295                 *
4296                 * @return an instance of AccessibleRole describing the role of the 
4297                 * object
4298                 * @see AccessibleRole
4299                 */
4300                public AccessibleRole getAccessibleRole() {
4301                    return AccessibleRole.TREE;
4302                }
4303
4304                /**
4305                 * Returns the <code>Accessible</code> child, if one exists,
4306                 * contained at the local coordinate <code>Point</code>.
4307                 * Otherwise returns <code>null</code>.
4308                 *
4309                 * @param p point in local coordinates of this <code>Accessible</code>
4310                 * @return the <code>Accessible</code>, if it exists,
4311                 *    at the specified location; else <code>null</code>
4312                 */
4313                public Accessible getAccessibleAt(Point p) {
4314                    TreePath path = getClosestPathForLocation(p.x, p.y);
4315                    if (path != null) {
4316                        // JTree.this is NOT the parent; parent will get computed later
4317                        return new AccessibleJTreeNode(JTree.this , path, null);
4318                    } else {
4319                        return null;
4320                    }
4321                }
4322
4323                /**
4324                 * Returns the number of top-level children nodes of this 
4325                 * JTree.  Each of these nodes may in turn have children nodes.
4326                 *
4327                 * @return the number of accessible children nodes in the tree.
4328                 */
4329                public int getAccessibleChildrenCount() {
4330                    TreeModel model = JTree.this .getModel();
4331                    if (model == null) {
4332                        return 0;
4333                    }
4334                    if (isRootVisible()) {
4335                        return 1; // the root node
4336                    }
4337
4338                    // return the root's first set of children count
4339                    return model.getChildCount(model.getRoot());
4340                }
4341
4342                /**
4343                 * Return the nth Accessible child of the object.
4344                 *
4345                 * @param i zero-based index of child
4346                 * @return the nth Accessible child of the object
4347                 */
4348                public Accessible getAccessibleChild(int i) {
4349                    TreeModel model = JTree.this .getModel();
4350                    if (model == null) {
4351                        return null;
4352                    }
4353                    if (isRootVisible()) {
4354                        if (i == 0) { // return the root node Accessible
4355                            Object[] objPath = { model.getRoot() };
4356                            TreePath path = new TreePath(objPath);
4357                            return new AccessibleJTreeNode(JTree.this , path,
4358                                    JTree.this );
4359                        } else {
4360                            return null;
4361                        }
4362                    }
4363
4364                    // return Accessible for one of root's child nodes
4365                    int count = model.getChildCount(model.getRoot());
4366                    if (i < 0 || i >= count) {
4367                        return null;
4368                    }
4369                    Object obj = model.getChild(model.getRoot(), i);
4370                    Object[] objPath = { model.getRoot(), obj };
4371                    TreePath path = new TreePath(objPath);
4372                    return new AccessibleJTreeNode(JTree.this , path, JTree.this );
4373                }
4374
4375                /**
4376                 * Get the index of this object in its accessible parent. 
4377                 *
4378                 * @return the index of this object in its parent.  Since a JTree
4379                 * top-level object does not have an accessible parent.
4380                 * @see #getAccessibleParent
4381                 */
4382                public int getAccessibleIndexInParent() {
4383                    // didn't ever need to override this...
4384                    return super .getAccessibleIndexInParent();
4385                }
4386
4387                // AccessibleSelection methods
4388                /**
4389                 * Get the AccessibleSelection associated with this object.  In the
4390                 * implementation of the Java Accessibility API for this class, 
4391                 * return this object, which is responsible for implementing the
4392                 * AccessibleSelection interface on behalf of itself.
4393                 * 
4394                 * @return this object
4395                 */
4396                public AccessibleSelection getAccessibleSelection() {
4397                    return this ;
4398                }
4399
4400                /**
4401                 * Returns the number of items currently selected.
4402                 * If no items are selected, the return value will be 0.
4403                 *
4404                 * @return the number of items currently selected.
4405                 */
4406                public int getAccessibleSelectionCount() {
4407                    Object[] rootPath = new Object[1];
4408                    rootPath[0] = treeModel.getRoot();
4409                    TreePath childPath = new TreePath(rootPath);
4410                    if (JTree.this .isPathSelected(childPath)) {
4411                        return 1;
4412                    } else {
4413                        return 0;
4414                    }
4415                }
4416
4417                /**
4418                 * Returns an Accessible representing the specified selected item
4419                 * in the object.  If there isn't a selection, or there are 
4420                 * fewer items selected than the integer passed in, the return
4421                 * value will be null.
4422                 *
4423                 * @param i the zero-based index of selected items
4424                 * @return an Accessible containing the selected item
4425                 */
4426                public Accessible getAccessibleSelection(int i) {
4427                    // The JTree can have only one accessible child, the root.
4428                    if (i == 0) {
4429                        Object[] rootPath = new Object[1];
4430                        rootPath[0] = treeModel.getRoot();
4431                        TreePath childPath = new TreePath(rootPath);
4432                        if (JTree.this .isPathSelected(childPath)) {
4433                            return new AccessibleJTreeNode(JTree.this ,
4434                                    childPath, JTree.this );
4435                        }
4436                    }
4437                    return null;
4438                }
4439
4440                /**
4441                 * Returns true if the current child of this object is selected.
4442                 *
4443                 * @param i the zero-based index of the child in this Accessible object.
4444                 * @see AccessibleContext#getAccessibleChild
4445                 */
4446                public boolean isAccessibleChildSelected(int i) {
4447                    // The JTree can have only one accessible child, the root.
4448                    if (i == 0) {
4449                        Object[] rootPath = new Object[1];
4450                        rootPath[0] = treeModel.getRoot();
4451                        TreePath childPath = new TreePath(rootPath);
4452                        return JTree.this .isPathSelected(childPath);
4453                    } else {
4454                        return false;
4455                    }
4456                }
4457
4458                /**
4459                 * Adds the specified selected item in the object to the object's
4460                 * selection.  If the object supports multiple selections,
4461                 * the specified item is added to any existing selection, otherwise
4462                 * it replaces any existing selection in the object.  If the
4463                 * specified item is already selected, this method has no effect.
4464                 *
4465                 * @param i the zero-based index of selectable items
4466                 */
4467                public void addAccessibleSelection(int i) {
4468                    TreeModel model = JTree.this .getModel();
4469                    if (model != null) {
4470                        if (i == 0) {
4471                            Object[] objPath = { model.getRoot() };
4472                            TreePath path = new TreePath(objPath);
4473                            JTree.this .addSelectionPath(path);
4474                        }
4475                    }
4476                }
4477
4478                /**
4479                 * Removes the specified selected item in the object from the object's
4480                 * selection.  If the specified item isn't currently selected, this
4481                 * method has no effect.
4482                 *
4483                 * @param i the zero-based index of selectable items
4484                 */
4485                public void removeAccessibleSelection(int i) {
4486                    TreeModel model = JTree.this .getModel();
4487                    if (model != null) {
4488                        if (i == 0) {
4489                            Object[] objPath = { model.getRoot() };
4490                            TreePath path = new TreePath(objPath);
4491                            JTree.this .removeSelectionPath(path);
4492                        }
4493                    }
4494                }
4495
4496                /**
4497                 * Clears the selection in the object, so that nothing in the
4498                 * object is selected.
4499                 */
4500                public void clearAccessibleSelection() {
4501                    int childCount = getAccessibleChildrenCount();
4502                    for (int i = 0; i < childCount; i++) {
4503                        removeAccessibleSelection(i);
4504                    }
4505                }
4506
4507                /**
4508                 * Causes every selected item in the object to be selected
4509                 * if the object supports multiple selections.
4510                 */
4511                public void selectAllAccessibleSelection() {
4512                    TreeModel model = JTree.this .getModel();
4513                    if (model != null) {
4514                        Object[] objPath = { model.getRoot() };
4515                        TreePath path = new TreePath(objPath);
4516                        JTree.this .addSelectionPath(path);
4517                    }
4518                }
4519
4520                /**
4521                 * This class implements accessibility support for the 
4522                 * <code>JTree</code> child.  It provides an implementation of the 
4523                 * Java Accessibility API appropriate to tree nodes.
4524                 */
4525                protected class AccessibleJTreeNode extends AccessibleContext
4526                        implements  Accessible, AccessibleComponent,
4527                        AccessibleSelection, AccessibleAction {
4528
4529                    private JTree tree = null;
4530                    private TreeModel treeModel = null;
4531                    private Object obj = null;
4532                    private TreePath path = null;
4533                    private Accessible accessibleParent = null;
4534                    private int index = 0;
4535                    private boolean isLeaf = false;
4536
4537                    /**
4538                     *  Constructs an AccessibleJTreeNode
4539                     * @since 1.4
4540                     */
4541                    public AccessibleJTreeNode(JTree t, TreePath p,
4542                            Accessible ap) {
4543                        tree = t;
4544                        path = p;
4545                        accessibleParent = ap;
4546                        treeModel = t.getModel();
4547                        obj = p.getLastPathComponent();
4548                        if (treeModel != null) {
4549                            isLeaf = treeModel.isLeaf(obj);
4550                        }
4551                    }
4552
4553                    private TreePath getChildTreePath(int i) {
4554                        // Tree nodes can't be so complex that they have
4555                        // two sets of children -> we're ignoring that case
4556                        if (i < 0 || i >= getAccessibleChildrenCount()) {
4557                            return null;
4558                        } else {
4559                            Object childObj = treeModel.getChild(obj, i);
4560                            Object[] objPath = path.getPath();
4561                            Object[] objChildPath = new Object[objPath.length + 1];
4562                            java.lang.System.arraycopy(objPath, 0,
4563                                    objChildPath, 0, objPath.length);
4564                            objChildPath[objChildPath.length - 1] = childObj;
4565                            return new TreePath(objChildPath);
4566                        }
4567                    }
4568
4569                    /**
4570                     * Get the AccessibleContext associated with this tree node. 
4571                     * In the implementation of the Java Accessibility API for 
4572                     * this class, return this object, which is its own 
4573                     * AccessibleContext.
4574                     *
4575                     * @return this object
4576                     */
4577                    public AccessibleContext getAccessibleContext() {
4578                        return this ;
4579                    }
4580
4581                    private AccessibleContext getCurrentAccessibleContext() {
4582                        Component c = getCurrentComponent();
4583                        if (c instanceof  Accessible) {
4584                            return (((Accessible) c).getAccessibleContext());
4585                        } else {
4586                            return null;
4587                        }
4588                    }
4589
4590                    private Component getCurrentComponent() {
4591                        // is the object visible?
4592                        // if so, get row, selected, focus & leaf state, 
4593                        // and then get the renderer component and return it
4594                        if (tree.isVisible(path)) {
4595                            TreeCellRenderer r = tree.getCellRenderer();
4596                            if (r == null) {
4597                                return null;
4598                            }
4599                            TreeUI ui = tree.getUI();
4600                            if (ui != null) {
4601                                int row = ui.getRowForPath(JTree.this , path);
4602                                boolean selected = tree.isPathSelected(path);
4603                                boolean expanded = tree.isExpanded(path);
4604                                boolean hasFocus = false; // how to tell?? -PK
4605                                return r.getTreeCellRendererComponent(tree,
4606                                        obj, selected, expanded, isLeaf, row,
4607                                        hasFocus);
4608                            }
4609                        }
4610                        return null;
4611                    }
4612
4613                    // AccessibleContext methods
4614
4615                    /**
4616                     * Get the accessible name of this object.
4617                     *
4618                     * @return the localized name of the object; null if this 
4619                     * object does not have a name
4620                     */
4621                    public String getAccessibleName() {
4622                        AccessibleContext ac = getCurrentAccessibleContext();
4623                        if (ac != null) {
4624                            String name = ac.getAccessibleName();
4625                            if ((name != null) && (name != "")) {
4626                                return ac.getAccessibleName();
4627                            } else {
4628                                return null;
4629                            }
4630                        }
4631                        if ((accessibleName != null) && (accessibleName != "")) {
4632                            return accessibleName;
4633                        } else {
4634                            // fall back to the client property
4635                            return (String) getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY);
4636                        }
4637                    }
4638
4639                    /**
4640                     * Set the localized accessible name of this object.
4641                     *
4642                     * @param s the new localized name of the object.
4643                     */
4644                    public void setAccessibleName(String s) {
4645                        AccessibleContext ac = getCurrentAccessibleContext();
4646                        if (ac != null) {
4647                            ac.setAccessibleName(s);
4648                        } else {
4649                            super .setAccessibleName(s);
4650                        }
4651                    }
4652
4653                    //
4654                    // *** should check tooltip text for desc. (needs MouseEvent)
4655                    //
4656                    /**
4657                     * Get the accessible description of this object.
4658                     *
4659                     * @return the localized description of the object; null if 
4660                     * this object does not have a description
4661                     */
4662                    public String getAccessibleDescription() {
4663                        AccessibleContext ac = getCurrentAccessibleContext();
4664                        if (ac != null) {
4665                            return ac.getAccessibleDescription();
4666                        } else {
4667                            return super .getAccessibleDescription();
4668                        }
4669                    }
4670
4671                    /**
4672                     * Set the accessible description of this object.
4673                     *
4674                     * @param s the new localized description of the object
4675                     */
4676                    public void setAccessibleDescription(String s) {
4677                        AccessibleContext ac = getCurrentAccessibleContext();
4678                        if (ac != null) {
4679                            ac.setAccessibleDescription(s);
4680                        } else {
4681                            super .setAccessibleDescription(s);
4682                        }
4683                    }
4684
4685                    /**
4686                     * Get the role of this object.
4687                     *
4688                     * @return an instance of AccessibleRole describing the role of the object
4689                     * @see AccessibleRole
4690                     */
4691                    public AccessibleRole getAccessibleRole() {
4692                        AccessibleContext ac = getCurrentAccessibleContext();
4693                        if (ac != null) {
4694                            return ac.getAccessibleRole();
4695                        } else {
4696                            return AccessibleRole.UNKNOWN;
4697                        }
4698                    }
4699
4700                    /**
4701                     * Get the state set of this object.
4702                     *
4703                     * @return an instance of AccessibleStateSet containing the 
4704                     * current state set of the object
4705                     * @see AccessibleState
4706                     */
4707                    public AccessibleStateSet getAccessibleStateSet() {
4708                        AccessibleContext ac = getCurrentAccessibleContext();
4709                        AccessibleStateSet states;
4710                        if (ac != null) {
4711                            states = ac.getAccessibleStateSet();
4712                        } else {
4713                            states = new AccessibleStateSet();
4714                        }
4715                        // need to test here, 'cause the underlying component 
4716                        // is a cellRenderer, which is never showing...
4717                        if (isShowing()) {
4718                            states.add(AccessibleState.SHOWING);
4719                        } else if (states.contains(AccessibleState.SHOWING)) {
4720                            states.remove(AccessibleState.SHOWING);
4721                        }
4722                        if (isVisible()) {
4723                            states.add(AccessibleState.VISIBLE);
4724                        } else if (states.contains(AccessibleState.VISIBLE)) {
4725                            states.remove(AccessibleState.VISIBLE);
4726                        }
4727                        if (tree.isPathSelected(path)) {
4728                            states.add(AccessibleState.SELECTED);
4729                        }
4730                        if (path == getLeadSelectionPath()) {
4731                            states.add(AccessibleState.ACTIVE);
4732                        }
4733                        if (!isLeaf) {
4734                            states.add(AccessibleState.EXPANDABLE);
4735                        }
4736                        if (tree.isExpanded(path)) {
4737                            states.add(AccessibleState.EXPANDED);
4738                        } else {
4739                            states.add(AccessibleState.COLLAPSED);
4740                        }
4741                        if (tree.isEditable()) {
4742                            states.add(AccessibleState.EDITABLE);
4743                        }
4744                        return states;
4745                    }
4746
4747                    /**
4748                     * Get the Accessible parent of this object.
4749                     *
4750                     * @return the Accessible parent of this object; null if this
4751                     * object does not have an Accessible parent
4752                     */
4753                    public Accessible getAccessibleParent() {
4754                        // someone wants to know, so we need to create our parent
4755                        // if we don't have one (hey, we're a talented kid!)
4756                        if (accessibleParent == null) {
4757                            Object[] objPath = path.getPath();
4758                            if (objPath.length > 1) {
4759                                Object objParent = objPath[objPath.length - 2];
4760                                if (treeModel != null) {
4761                                    index = treeModel.getIndexOfChild(
4762                                            objParent, obj);
4763                                }
4764                                Object[] objParentPath = new Object[objPath.length - 1];
4765                                java.lang.System.arraycopy(objPath, 0,
4766                                        objParentPath, 0, objPath.length - 1);
4767                                TreePath parentPath = new TreePath(
4768                                        objParentPath);
4769                                accessibleParent = new AccessibleJTreeNode(
4770                                        tree, parentPath, null);
4771                                this .setAccessibleParent(accessibleParent);
4772                            } else if (treeModel != null) {
4773                                accessibleParent = tree; // we're the top!
4774                                index = 0; // we're an only child!
4775                                this .setAccessibleParent(accessibleParent);
4776                            }
4777                        }
4778                        return accessibleParent;
4779                    }
4780
4781                    /**
4782                     * Get the index of this object in its accessible parent. 
4783                     *
4784                     * @return the index of this object in its parent; -1 if this 
4785                     * object does not have an accessible parent.
4786                     * @see #getAccessibleParent
4787                     */
4788                    public int getAccessibleIndexInParent() {
4789                        // index is invalid 'till we have an accessibleParent...
4790                        if (accessibleParent == null) {
4791                            getAccessibleParent();
4792                        }
4793                        Object[] objPath = path.getPath();
4794                        if (objPath.length > 1) {
4795                            Object objParent = objPath[objPath.length - 2];
4796                            if (treeModel != null) {
4797                                index = treeModel.getIndexOfChild(objParent,
4798                                        obj);
4799                            }
4800                        }
4801                        return index;
4802                    }
4803
4804                    /**
4805                     * Returns the number of accessible children in the object.
4806                     *
4807                     * @return the number of accessible children in the object.
4808                     */
4809                    public int getAccessibleChildrenCount() {
4810                        // Tree nodes can't be so complex that they have 
4811                        // two sets of children -> we're ignoring that case
4812                        return treeModel.getChildCount(obj);
4813                    }
4814
4815                    /**
4816                     * Return the specified Accessible child of the object.
4817                     *
4818                     * @param i zero-based index of child
4819                     * @return the Accessible child of the object
4820                     */
4821                    public Accessible getAccessibleChild(int i) {
4822                        // Tree nodes can't be so complex that they have 
4823                        // two sets of children -> we're ignoring that case
4824                        if (i < 0 || i >= getAccessibleChildrenCount()) {
4825                            return null;
4826                        } else {
4827                            Object childObj = treeModel.getChild(obj, i);
4828                            Object[] objPath = path.getPath();
4829                            Object[] objChildPath = new Object[objPath.length + 1];
4830                            java.lang.System.arraycopy(objPath, 0,
4831                                    objChildPath, 0, objPath.length);
4832                            objChildPath[objChildPath.length - 1] = childObj;
4833                            TreePath childPath = new TreePath(objChildPath);
4834                            return new AccessibleJTreeNode(JTree.this ,
4835                                    childPath, this );
4836                        }
4837                    }
4838
4839                    /** 
4840                     * Gets the locale of the component. If the component does not have
4841                     * a locale, then the locale of its parent is returned.  
4842                     *
4843                     * @return This component's locale. If this component does not have 
4844                     * a locale, the locale of its parent is returned.
4845                     * @exception IllegalComponentStateException 
4846                     * If the Component does not have its own locale and has not yet 
4847                     * been added to a containment hierarchy such that the locale can be
4848                     * determined from the containing parent. 
4849                     * @see #setLocale
4850                     */
4851                    public Locale getLocale() {
4852                        AccessibleContext ac = getCurrentAccessibleContext();
4853                        if (ac != null) {
4854                            return ac.getLocale();
4855                        } else {
4856                            return tree.getLocale();
4857                        }
4858                    }
4859
4860                    /**
4861                     * Add a PropertyChangeListener to the listener list.
4862                     * The listener is registered for all properties.
4863                     *
4864                     * @param l  The PropertyChangeListener to be added
4865                     */
4866                    public void addPropertyChangeListener(
4867                            PropertyChangeListener l) {
4868                        AccessibleContext ac = getCurrentAccessibleContext();
4869                        if (ac != null) {
4870                            ac.addPropertyChangeListener(l);
4871                        } else {
4872                            super .addPropertyChangeListener(l);
4873                        }
4874                    }
4875
4876                    /**
4877                     * Remove a PropertyChangeListener from the listener list.
4878                     * This removes a PropertyChangeListener that was registered
4879                     * for all properties.
4880                     *
4881                     * @param l  The PropertyChangeListener to be removed
4882                     */
4883                    public void removePropertyChangeListener(
4884                            PropertyChangeListener l) {
4885                        AccessibleContext ac = getCurrentAccessibleContext();
4886                        if (ac != null) {
4887                            ac.removePropertyChangeListener(l);
4888                        } else {
4889                            super .removePropertyChangeListener(l);
4890                        }
4891                    }
4892
4893                    /**
4894                     * Get the AccessibleAction associated with this object.  In the
4895                     * implementation of the Java Accessibility API for this class, 
4896                     * return this object, which is responsible for implementing the
4897                     * AccessibleAction interface on behalf of itself.
4898                     * 
4899                     * @return this object
4900                     */
4901                    public AccessibleAction getAccessibleAction() {
4902                        return this ;
4903                    }
4904
4905                    /**
4906                     * Get the AccessibleComponent associated with this object.  In the
4907                     * implementation of the Java Accessibility API for this class, 
4908                     * return this object, which is responsible for implementing the
4909                     * AccessibleComponent interface on behalf of itself.
4910                     * 
4911                     * @return this object
4912                     */
4913                    public AccessibleComponent getAccessibleComponent() {
4914                        return this ; // to override getBounds()
4915                    }
4916
4917                    /**
4918                     * Get the AccessibleSelection associated with this object if one
4919                     * exists.  Otherwise return null.
4920                     *
4921                     * @return the AccessibleSelection, or null
4922                     */
4923                    public AccessibleSelection getAccessibleSelection() {
4924                        AccessibleContext ac = getCurrentAccessibleContext();
4925                        if (ac != null && isLeaf) {
4926                            return getCurrentAccessibleContext()
4927                                    .getAccessibleSelection();
4928                        } else {
4929                            return this ;
4930                        }
4931                    }
4932
4933                    /**
4934                     * Get the AccessibleText associated with this object if one
4935                     * exists.  Otherwise return null.
4936                     *
4937                     * @return the AccessibleText, or null
4938                     */
4939                    public AccessibleText getAccessibleText() {
4940                        AccessibleContext ac = getCurrentAccessibleContext();
4941                        if (ac != null) {
4942                            return getCurrentAccessibleContext()
4943                                    .getAccessibleText();
4944                        } else {
4945                            return null;
4946                        }
4947                    }
4948
4949                    /**
4950                     * Get the AccessibleValue associated with this object if one
4951                     * exists.  Otherwise return null.
4952                     *
4953                     * @return the AccessibleValue, or null
4954                     */
4955                    public AccessibleValue getAccessibleValue() {
4956                        AccessibleContext ac = getCurrentAccessibleContext();
4957                        if (ac != null) {
4958                            return getCurrentAccessibleContext()
4959                                    .getAccessibleValue();
4960                        } else {
4961                            return null;
4962                        }
4963                    }
4964
4965                    // AccessibleComponent methods
4966
4967                    /**
4968                     * Get the background color of this object.
4969                     *
4970                     * @return the background color, if supported, of the object; 
4971                     * otherwise, null
4972                     */
4973                    public Color getBackground() {
4974                        AccessibleContext ac = getCurrentAccessibleContext();
4975                        if (ac instanceof  AccessibleComponent) {
4976                            return ((AccessibleComponent) ac).getBackground();
4977                        } else {
4978                            Component c = getCurrentComponent();
4979                            if (c != null) {
4980                                return c.getBackground();
4981                            } else {
4982                                return null;
4983                            }
4984                        }
4985                    }
4986
4987                    /**
4988                     * Set the background color of this object.
4989                     *
4990                     * @param c the new Color for the background
4991                     */
4992                    public void setBackground(Color c) {
4993                        AccessibleContext ac = getCurrentAccessibleContext();
4994                        if (ac instanceof  AccessibleComponent) {
4995                            ((AccessibleComponent) ac).setBackground(c);
4996                        } else {
4997                            Component cp = getCurrentComponent();
4998                            if (cp != null) {
4999                                cp.setBackground(c);
5000                            }
5001                        }
5002                    }
5003
5004                    /**
5005                     * Get the foreground color of this object.
5006                     *
5007                     * @return the foreground color, if supported, of the object; 
5008                     * otherwise, null
5009                     */
5010                    public Color getForeground() {
5011                        AccessibleContext ac = getCurrentAccessibleContext();
5012                        if (ac instanceof  AccessibleComponent) {
5013                            return ((AccessibleComponent) ac).getForeground();
5014                        } else {
5015                            Component c = getCurrentComponent();
5016                            if (c != null) {
5017                                return c.getForeground();
5018                            } else {
5019                                return null;
5020                            }
5021                        }
5022                    }
5023
5024                    public void setForeground(Color c) {
5025                        AccessibleContext ac = getCurrentAccessibleContext();
5026                        if (ac instanceof  AccessibleComponent) {
5027                            ((AccessibleComponent) ac).setForeground(c);
5028                        } else {
5029                            Component cp = getCurrentComponent();
5030                            if (cp != null) {
5031                                cp.setForeground(c);
5032                            }
5033                        }
5034                    }
5035
5036                    public Cursor getCursor() {
5037                        AccessibleContext ac = getCurrentAccessibleContext();
5038                        if (ac instanceof  AccessibleComponent) {
5039                            return ((AccessibleComponent) ac).getCursor();
5040                        } else {
5041                            Component c = getCurrentComponent();
5042                            if (c != null) {
5043                                return c.getCursor();
5044                            } else {
5045                                Accessible ap = getAccessibleParent();
5046                                if (ap instanceof  AccessibleComponent) {
5047                                    return ((AccessibleComponent) ap)
5048                                            .getCursor();
5049                                } else {
5050                                    return null;
5051                                }
5052                            }
5053                        }
5054                    }
5055
5056                    public void setCursor(Cursor c) {
5057                        AccessibleContext ac = getCurrentAccessibleContext();
5058                        if (ac instanceof  AccessibleComponent) {
5059                            ((AccessibleComponent) ac).setCursor(c);
5060                        } else {
5061                            Component cp = getCurrentComponent();
5062                            if (cp != null) {
5063                                cp.setCursor(c);
5064                            }
5065                        }
5066                    }
5067
5068                    public Font getFont() {
5069                        AccessibleContext ac = getCurrentAccessibleContext();
5070                        if (ac instanceof  AccessibleComponent) {
5071                            return ((AccessibleComponent) ac).getFont();
5072                        } else {
5073                            Component c = getCurrentComponent();
5074                            if (c != null) {
5075                                return c.getFont();
5076                            } else {
5077                                return null;
5078                            }
5079                        }
5080                    }
5081
5082                    public void setFont(Font f) {
5083                        AccessibleContext ac = getCurrentAccessibleContext();
5084                        if (ac instanceof  AccessibleComponent) {
5085                            ((AccessibleComponent) ac).setFont(f);
5086                        } else {
5087                            Component c = getCurrentComponent();
5088                            if (c != null) {
5089                                c.setFont(f);
5090                            }
5091                        }
5092                    }
5093
5094                    public FontMetrics getFontMetrics(Font f) {
5095                        AccessibleContext ac = getCurrentAccessibleContext();
5096                        if (ac instanceof  AccessibleComponent) {
5097                            return ((AccessibleComponent) ac).getFontMetrics(f);
5098                        } else {
5099                            Component c = getCurrentComponent();
5100                            if (c != null) {
5101                                return c.getFontMetrics(f);
5102                            } else {
5103                                return null;
5104                            }
5105                        }
5106                    }
5107
5108                    public boolean isEnabled() {
5109                        AccessibleContext ac = getCurrentAccessibleContext();
5110                        if (ac instanceof  AccessibleComponent) {
5111                            return ((AccessibleComponent) ac).isEnabled();
5112                        } else {
5113                            Component c = getCurrentComponent();
5114                            if (c != null) {
5115                                return c.isEnabled();
5116                            } else {
5117                                return false;
5118                            }
5119                        }
5120                    }
5121
5122                    public void setEnabled(boolean b) {
5123                        AccessibleContext ac = getCurrentAccessibleContext();
5124                        if (ac instanceof  AccessibleComponent) {
5125                            ((AccessibleComponent) ac).setEnabled(b);
5126                        } else {
5127                            Component c = getCurrentComponent();
5128                            if (c != null) {
5129                                c.setEnabled(b);
5130                            }
5131                        }
5132                    }
5133
5134                    public boolean isVisible() {
5135                        Rectangle pathBounds = tree.getPathBounds(path);
5136                        Rectangle parentBounds = tree.getVisibleRect();
5137                        if (pathBounds != null && parentBounds != null
5138                                && parentBounds.intersects(pathBounds)) {
5139                            return true;
5140                        } else {
5141                            return false;
5142                        }
5143                    }
5144
5145                    public void setVisible(boolean b) {
5146                    }
5147
5148                    public boolean isShowing() {
5149                        return (tree.isShowing() && isVisible());
5150                    }
5151
5152                    public boolean contains(Point p) {
5153                        AccessibleContext ac = getCurrentAccessibleContext();
5154                        if (ac instanceof  AccessibleComponent) {
5155                            Rectangle r = ((AccessibleComponent) ac)
5156                                    .getBounds();
5157                            return r.contains(p);
5158                        } else {
5159                            Component c = getCurrentComponent();
5160                            if (c != null) {
5161                                Rectangle r = c.getBounds();
5162                                return r.contains(p);
5163                            } else {
5164                                return getBounds().contains(p);
5165                            }
5166                        }
5167                    }
5168
5169                    public Point getLocationOnScreen() {
5170                        if (tree != null) {
5171                            Point treeLocation = tree.getLocationOnScreen();
5172                            Rectangle pathBounds = tree.getPathBounds(path);
5173                            if (treeLocation != null && pathBounds != null) {
5174                                Point nodeLocation = new Point(pathBounds.x,
5175                                        pathBounds.y);
5176                                nodeLocation.translate(treeLocation.x,
5177                                        treeLocation.y);
5178                                return nodeLocation;
5179                            } else {
5180                                return null;
5181                            }
5182                        } else {
5183                            return null;
5184                        }
5185                    }
5186
5187                    protected Point getLocationInJTree() {
5188                        Rectangle r = tree.getPathBounds(path);
5189                        if (r != null) {
5190                            return r.getLocation();
5191                        } else {
5192                            return null;
5193                        }
5194                    }
5195
5196                    public Point getLocation() {
5197                        Rectangle r = getBounds();
5198                        if (r != null) {
5199                            return r.getLocation();
5200                        } else {
5201                            return null;
5202                        }
5203                    }
5204
5205                    public void setLocation(Point p) {
5206                    }
5207
5208                    public Rectangle getBounds() {
5209                        Rectangle r = tree.getPathBounds(path);
5210                        Accessible parent = getAccessibleParent();
5211                        if (parent != null) {
5212                            if (parent instanceof  AccessibleJTreeNode) {
5213                                Point parentLoc = ((AccessibleJTreeNode) parent)
5214                                        .getLocationInJTree();
5215                                if (parentLoc != null && r != null) {
5216                                    r.translate(-parentLoc.x, -parentLoc.y);
5217                                } else {
5218                                    return null; // not visible!
5219                                }
5220                            }
5221                        }
5222                        return r;
5223                    }
5224
5225                    public void setBounds(Rectangle r) {
5226                        AccessibleContext ac = getCurrentAccessibleContext();
5227                        if (ac instanceof  AccessibleComponent) {
5228                            ((AccessibleComponent) ac).setBounds(r);
5229                        } else {
5230                            Component c = getCurrentComponent();
5231                            if (c != null) {
5232                                c.setBounds(r);
5233                            }
5234                        }
5235                    }
5236
5237                    public Dimension getSize() {
5238                        return getBounds().getSize();
5239                    }
5240
5241                    public void setSize(Dimension d) {
5242                        AccessibleContext ac = getCurrentAccessibleContext();
5243                        if (ac instanceof  AccessibleComponent) {
5244                            ((AccessibleComponent) ac).setSize(d);
5245                        } else {
5246                            Component c = getCurrentComponent();
5247                            if (c != null) {
5248                                c.setSize(d);
5249                            }
5250                        }
5251                    }
5252
5253                    /**
5254                     * Returns the <code>Accessible</code> child, if one exists,
5255                     * contained at the local coordinate <code>Point</code>.
5256                     * Otherwise returns <code>null</code>.
5257                     *
5258                     * @param p point in local coordinates of this
5259                     *    <code>Accessible</code>
5260                     * @return the <code>Accessible</code>, if it exists,
5261                     *    at the specified location; else <code>null</code>
5262                     */
5263                    public Accessible getAccessibleAt(Point p) {
5264                        AccessibleContext ac = getCurrentAccessibleContext();
5265                        if (ac instanceof  AccessibleComponent) {
5266                            return ((AccessibleComponent) ac)
5267                                    .getAccessibleAt(p);
5268                        } else {
5269                            return null;
5270                        }
5271                    }
5272
5273                    public boolean isFocusTraversable() {
5274                        AccessibleContext ac = getCurrentAccessibleContext();
5275                        if (ac instanceof  AccessibleComponent) {
5276                            return ((AccessibleComponent) ac)
5277                                    .isFocusTraversable();
5278                        } else {
5279                            Component c = getCurrentComponent();
5280                            if (c != null) {
5281                                return c.isFocusTraversable();
5282                            } else {
5283                                return false;
5284                            }
5285                        }
5286                    }
5287
5288                    public void requestFocus() {
5289                        AccessibleContext ac = getCurrentAccessibleContext();
5290                        if (ac instanceof  AccessibleComponent) {
5291                            ((AccessibleComponent) ac).requestFocus();
5292                        } else {
5293                            Component c = getCurrentComponent();
5294                            if (c != null) {
5295                                c.requestFocus();
5296                            }
5297                        }
5298                    }
5299
5300                    public void addFocusListener(FocusListener l) {
5301                        AccessibleContext ac = getCurrentAccessibleContext();
5302                        if (ac instanceof  AccessibleComponent) {
5303                            ((AccessibleComponent) ac).addFocusListener(l);
5304                        } else {
5305                            Component c = getCurrentComponent();
5306                            if (c != null) {
5307                                c.addFocusListener(l);
5308                            }
5309                        }
5310                    }
5311
5312                    public void removeFocusListener(FocusListener l) {
5313                        AccessibleContext ac = getCurrentAccessibleContext();
5314                        if (ac instanceof  AccessibleComponent) {
5315                            ((AccessibleComponent) ac).removeFocusListener(l);
5316                        } else {
5317                            Component c = getCurrentComponent();
5318                            if (c != null) {
5319                                c.removeFocusListener(l);
5320                            }
5321                        }
5322                    }
5323
5324                    // AccessibleSelection methods
5325
5326                    /**
5327                     * Returns the number of items currently selected.
5328                     * If no items are selected, the return value will be 0.
5329                     *
5330                     * @return the number of items currently selected.
5331                     */
5332                    public int getAccessibleSelectionCount() {
5333                        int count = 0;
5334                        int childCount = getAccessibleChildrenCount();
5335                        for (int i = 0; i < childCount; i++) {
5336                            TreePath childPath = getChildTreePath(i);
5337                            if (tree.isPathSelected(childPath)) {
5338                                count++;
5339                            }
5340                        }
5341                        return count;
5342                    }
5343
5344                    /**
5345                     * Returns an Accessible representing the specified selected item
5346                     * in the object.  If there isn't a selection, or there are 
5347                     * fewer items selected than the integer passed in, the return
5348                     * value will be null.
5349                     *
5350                     * @param i the zero-based index of selected items
5351                     * @return an Accessible containing the selected item
5352                     */
5353                    public Accessible getAccessibleSelection(int i) {
5354                        int childCount = getAccessibleChildrenCount();
5355                        if (i < 0 || i >= childCount) {
5356                            return null; // out of range
5357                        }
5358                        int count = 0;
5359                        for (int j = 0; j < childCount && i >= count; j++) {
5360                            TreePath childPath = getChildTreePath(j);
5361                            if (tree.isPathSelected(childPath)) {
5362                                if (count == i) {
5363                                    return new AccessibleJTreeNode(tree,
5364                                            childPath, this );
5365                                } else {
5366                                    count++;
5367                                }
5368                            }
5369                        }
5370                        return null;
5371                    }
5372
5373                    /**
5374                     * Returns true if the current child of this object is selected.
5375                     *
5376                     * @param i the zero-based index of the child in this Accessible 
5377                     * object.
5378                     * @see AccessibleContext#getAccessibleChild
5379                     */
5380                    public boolean isAccessibleChildSelected(int i) {
5381                        int childCount = getAccessibleChildrenCount();
5382                        if (i < 0 || i >= childCount) {
5383                            return false; // out of range
5384                        } else {
5385                            TreePath childPath = getChildTreePath(i);
5386                            return tree.isPathSelected(childPath);
5387                        }
5388                    }
5389
5390                    /**
5391                     * Adds the specified selected item in the object to the object's
5392                     * selection.  If the object supports multiple selections,
5393                     * the specified item is added to any existing selection, otherwise
5394                     * it replaces any existing selection in the object.  If the
5395                     * specified item is already selected, this method has no effect.
5396                     *
5397                     * @param i the zero-based index of selectable items
5398                     */
5399                    public void addAccessibleSelection(int i) {
5400                        TreeModel model = JTree.this .getModel();
5401                        if (model != null) {
5402                            if (i >= 0 && i < getAccessibleChildrenCount()) {
5403                                TreePath path = getChildTreePath(i);
5404                                JTree.this .addSelectionPath(path);
5405                            }
5406                        }
5407                    }
5408
5409                    /**
5410                     * Removes the specified selected item in the object from the 
5411                     * object's
5412                     * selection.  If the specified item isn't currently selected, this
5413                     * method has no effect.
5414                     *
5415                     * @param i the zero-based index of selectable items
5416                     */
5417                    public void removeAccessibleSelection(int i) {
5418                        TreeModel model = JTree.this .getModel();
5419                        if (model != null) {
5420                            if (i >= 0 && i < getAccessibleChildrenCount()) {
5421                                TreePath path = getChildTreePath(i);
5422                                JTree.this .removeSelectionPath(path);
5423                            }
5424                        }
5425                    }
5426
5427                    /**
5428                     * Clears the selection in the object, so that nothing in the
5429                     * object is selected.
5430                     */
5431                    public void clearAccessibleSelection() {
5432                        int childCount = getAccessibleChildrenCount();
5433                        for (int i = 0; i < childCount; i++) {
5434                            removeAccessibleSelection(i);
5435                        }
5436                    }
5437
5438                    /**
5439                     * Causes every selected item in the object to be selected
5440                     * if the object supports multiple selections.
5441                     */
5442                    public void selectAllAccessibleSelection() {
5443                        TreeModel model = JTree.this .getModel();
5444                        if (model != null) {
5445                            int childCount = getAccessibleChildrenCount();
5446                            TreePath path;
5447                            for (int i = 0; i < childCount; i++) {
5448                                path = getChildTreePath(i);
5449                                JTree.this .addSelectionPath(path);
5450                            }
5451                        }
5452                    }
5453
5454                    // AccessibleAction methods
5455
5456                    /**
5457                     * Returns the number of accessible actions available in this 
5458                     * tree node.  If this node is not a leaf, there is at least 
5459                     * one action (toggle expand), in addition to any available
5460                     * on the object behind the TreeCellRenderer.
5461                     *
5462                     * @return the number of Actions in this object
5463                     */
5464                    public int getAccessibleActionCount() {
5465                        AccessibleContext ac = getCurrentAccessibleContext();
5466                        if (ac != null) {
5467                            AccessibleAction aa = ac.getAccessibleAction();
5468                            if (aa != null) {
5469                                return (aa.getAccessibleActionCount() + (isLeaf ? 0
5470                                        : 1));
5471                            }
5472                        }
5473                        return isLeaf ? 0 : 1;
5474                    }
5475
5476                    /**
5477                     * Return a description of the specified action of the tree node.
5478                     * If this node is not a leaf, there is at least one action
5479                     * description (toggle expand), in addition to any available
5480                     * on the object behind the TreeCellRenderer.
5481                     *
5482                     * @param i zero-based index of the actions
5483                     * @return a description of the action
5484                     */
5485                    public String getAccessibleActionDescription(int i) {
5486                        if (i < 0 || i >= getAccessibleActionCount()) {
5487                            return null;
5488                        }
5489                        AccessibleContext ac = getCurrentAccessibleContext();
5490                        if (i == 0) {
5491                            // TIGER - 4766636
5492                            return AccessibleAction.TOGGLE_EXPAND;
5493                        } else if (ac != null) {
5494                            AccessibleAction aa = ac.getAccessibleAction();
5495                            if (aa != null) {
5496                                return aa.getAccessibleActionDescription(i - 1);
5497                            }
5498                        }
5499                        return null;
5500                    }
5501
5502                    /**
5503                     * Perform the specified Action on the tree node.  If this node
5504                     * is not a leaf, there is at least one action which can be
5505                     * done (toggle expand), in addition to any available on the 
5506                     * object behind the TreeCellRenderer.
5507                     *
5508                     * @param i zero-based index of actions
5509                     * @return true if the the action was performed; else false.
5510                     */
5511                    public boolean doAccessibleAction(int i) {
5512                        if (i < 0 || i >= getAccessibleActionCount()) {
5513                            return false;
5514                        }
5515                        AccessibleContext ac = getCurrentAccessibleContext();
5516                        if (i == 0) {
5517                            if (JTree.this .isExpanded(path)) {
5518                                JTree.this .collapsePath(path);
5519                            } else {
5520                                JTree.this .expandPath(path);
5521                            }
5522                            return true;
5523                        } else if (ac != null) {
5524                            AccessibleAction aa = ac.getAccessibleAction();
5525                            if (aa != null) {
5526                                return aa.doAccessibleAction(i - 1);
5527                            }
5528                        }
5529                        return false;
5530                    }
5531
5532                } // inner class AccessibleJTreeNode
5533
5534            } // inner class AccessibleJTree
5535
5536        } // End of class JTree
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.