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