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