Home » openjdk-7 » javax » swing » [javadoc | source]

    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

Home » openjdk-7 » javax » swing » [javadoc | source]