1 /*
2 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package javax.swing;
27
28 import java.awt;
29 import java.awt.event;
30 import java.beans;
31 import java.io;
32 import java.util;
33 import javax.swing.event;
34 import javax.swing.plaf;
35 import javax.swing.tree;
36 import javax.swing.text.Position;
37 import javax.accessibility;
38 import sun.swing.SwingUtilities2;
39 import sun.swing.SwingUtilities2.Section;
40 import static sun.swing.SwingUtilities2.Section.*;
41
42
43 /**
44 * <a name="jtree_description">
45 * A control that displays a set of hierarchical data as an outline.
46 * You can find task-oriented documentation and examples of using trees in
47 * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html">How to Use Trees</a>,
48 * a section in <em>The Java Tutorial.</em>
49 * <p>
50 * A specific node in a tree can be identified either by a
51 * <code>TreePath</code> (an object
52 * that encapsulates a node and all of its ancestors), or by its
53 * display row, where each row in the display area displays one node.
54 * An <i>expanded</i> node is a non-leaf node (as identified by
55 * <code>TreeModel.isLeaf(node)</code> returning false) that will displays
56 * its children when all its ancestors are <i>expanded</i>.
57 * A <i>collapsed</i>
58 * node is one which hides them. A <i>hidden</i> node is one which is
59 * under a collapsed ancestor. All of a <i>viewable</i> nodes parents
60 * are expanded, but may or may not be displayed. A <i>displayed</i> node
61 * is both viewable and in the display area, where it can be seen.
62 * <p>
63 * The following <code>JTree</code> methods use "visible" to mean "displayed":
64 * <ul>
65 * <li><code>isRootVisible()</code>
66 * <li><code>setRootVisible()</code>
67 * <li><code>scrollPathToVisible()</code>
68 * <li><code>scrollRowToVisible()</code>
69 * <li><code>getVisibleRowCount()</code>
70 * <li><code>setVisibleRowCount()</code>
71 * </ul>
72 * <p>
73 * The next group of <code>JTree</code> methods use "visible" to mean
74 * "viewable" (under an expanded parent):
75 * <ul>
76 * <li><code>isVisible()</code>
77 * <li><code>makeVisible()</code>
78 * </ul>
79 * <p>
80 * If you are interested in knowing when the selection changes implement
81 * the <code>TreeSelectionListener</code> interface and add the instance
82 * using the method <code>addTreeSelectionListener</code>.
83 * <code>valueChanged</code> will be invoked when the
84 * selection changes, that is if the user clicks twice on the same
85 * node <code>valueChanged</code> will only be invoked once.
86 * <p>
87 * If you are interested in detecting either double-click events or when
88 * a user clicks on a node, regardless of whether or not it was selected,
89 * we recommend you do the following:
90 * <pre>
91 * final JTree tree = ...;
92 *
93 * MouseListener ml = new MouseAdapter() {
94 * public void <b>mousePressed</b>(MouseEvent e) {
95 * int selRow = tree.getRowForLocation(e.getX(), e.getY());
96 * TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
97 * if(selRow != -1) {
98 * if(e.getClickCount() == 1) {
99 * mySingleClick(selRow, selPath);
100 * }
101 * else if(e.getClickCount() == 2) {
102 * myDoubleClick(selRow, selPath);
103 * }
104 * }
105 * }
106 * };
107 * tree.addMouseListener(ml);
108 * </pre>
109 * NOTE: This example obtains both the path and row, but you only need to
110 * get the one you're interested in.
111 * <p>
112 * To use <code>JTree</code> to display compound nodes
113 * (for example, nodes containing both
114 * a graphic icon and text), subclass {@link TreeCellRenderer} and use
115 * {@link #setCellRenderer} to tell the tree to use it. To edit such nodes,
116 * subclass {@link TreeCellEditor} and use {@link #setCellEditor}.
117 * <p>
118 * Like all <code>JComponent</code> classes, you can use {@link InputMap} and
119 * {@link ActionMap}
120 * to associate an {@link Action} object with a {@link KeyStroke}
121 * and execute the action under specified conditions.
122 * <p>
123 * <strong>Warning:</strong> Swing is not thread safe. For more
124 * information see <a
125 * href="package-summary.html#threading">Swing's Threading
126 * Policy</a>.
127 * <p>
128 * <strong>Warning:</strong>
129 * Serialized objects of this class will not be compatible with
130 * future Swing releases. The current serialization support is
131 * appropriate for short term storage or RMI between applications running
132 * the same version of Swing. As of 1.4, support for long term storage
133 * of all JavaBeans<sup><font size="-2">TM</font></sup>
134 * has been added to the <code>java.beans</code> package.
135 * Please see {@link java.beans.XMLEncoder}.
136 *
137 * @beaninfo
138 * attribute: isContainer false
139 * description: A component that displays a set of hierarchical data as an outline.
140 *
141 * @author Rob Davis
142 * @author Ray Ryan
143 * @author Scott Violet
144 */
145 public class JTree extends JComponent implements Scrollable, Accessible
146 {
147 /**
148 * @see #getUIClassID
149 * @see #readObject
150 */
151 private static final String uiClassID = "TreeUI";
152
153 /**
154 * The model that defines the tree displayed by this object.
155 */
156 transient protected TreeModel treeModel;
157
158 /**
159 * Models the set of selected nodes in this tree.
160 */
161 transient protected TreeSelectionModel selectionModel;
162
163 /**
164 * True if the root node is displayed, false if its children are
165 * the highest visible nodes.
166 */
167 protected boolean rootVisible;
168
169 /**
170 * The cell used to draw nodes. If <code>null</code>, the UI uses a default
171 * <code>cellRenderer</code>.
172 */
173 transient protected TreeCellRenderer cellRenderer;
174
175 /**
176 * Height to use for each display row. If this is <= 0 the renderer
177 * determines the height for each row.
178 */
179 protected int rowHeight;
180 private boolean rowHeightSet = false;
181
182 /**
183 * Maps from <code>TreePath</code> to <code>Boolean</code>
184 * indicating whether or not the
185 * particular path is expanded. This ONLY indicates whether a
186 * given path is expanded, and NOT if it is visible or not. That
187 * information must be determined by visiting all the parent
188 * paths and seeing if they are visible.
189 */
190 transient private Hashtable expandedState;
191
192
193 /**
194 * True if handles are displayed at the topmost level of the tree.
195 * <p>
196 * A handle is a small icon that displays adjacent to the node which
197 * allows the user to click once to expand or collapse the node. A
198 * common interface shows a plus sign (+) for a node which can be
199 * expanded and a minus sign (-) for a node which can be collapsed.
200 * Handles are always shown for nodes below the topmost level.
201 * <p>
202 * If the <code>rootVisible</code> setting specifies that the root
203 * node is to be displayed, then that is the only node at the topmost
204 * level. If the root node is not displayed, then all of its
205 * children are at the topmost level of the tree. Handles are
206 * always displayed for nodes other than the topmost.
207 * <p>
208 * If the root node isn't visible, it is generally a good to make
209 * this value true. Otherwise, the tree looks exactly like a list,
210 * and users may not know that the "list entries" are actually
211 * tree nodes.
212 *
213 * @see #rootVisible
214 */
215 protected boolean showsRootHandles;
216 private boolean showsRootHandlesSet = false;
217
218 /**
219 * Creates a new event and passed it off the
220 * <code>selectionListeners</code>.
221 */
222 protected transient TreeSelectionRedirector selectionRedirector;
223
224 /**
225 * Editor for the entries. Default is <code>null</code>
226 * (tree is not editable).
227 */
228 transient protected TreeCellEditor cellEditor;
229
230 /**
231 * Is the tree editable? Default is false.
232 */
233 protected boolean editable;
234
235 /**
236 * Is this tree a large model? This is a code-optimization setting.
237 * A large model can be used when the cell height is the same for all
238 * nodes. The UI will then cache very little information and instead
239 * continually message the model. Without a large model the UI caches
240 * most of the information, resulting in fewer method calls to the model.
241 * <p>
242 * This value is only a suggestion to the UI. Not all UIs will
243 * take advantage of it. Default value is false.
244 */
245 protected boolean largeModel;
246
247 /**
248 * Number of rows to make visible at one time. This value is used for
249 * the <code>Scrollable</code> interface. It determines the preferred
250 * size of the display area.
251 */
252 protected int visibleRowCount;
253
254 /**
255 * If true, when editing is to be stopped by way of selection changing,
256 * data in tree changing or other means <code>stopCellEditing</code>
257 * is invoked, and changes are saved. If false,
258 * <code>cancelCellEditing</code> is invoked, and changes
259 * are discarded. Default is false.
260 */
261 protected boolean invokesStopCellEditing;
262
263 /**
264 * If true, when a node is expanded, as many of the descendants are
265 * scrolled to be visible.
266 */
267 protected boolean scrollsOnExpand;
268 private boolean scrollsOnExpandSet = false;
269
270 /**
271 * Number of mouse clicks before a node is expanded.
272 */
273 protected int toggleClickCount;
274
275 /**
276 * Updates the <code>expandedState</code>.
277 */
278 transient protected TreeModelListener treeModelListener;
279
280 /**
281 * Used when <code>setExpandedState</code> is invoked,
282 * will be a <code>Stack</code> of <code>Stack</code>s.
283 */
284 transient private Stack expandedStack;
285
286 /**
287 * Lead selection path, may not be <code>null</code>.
288 */
289 private TreePath leadPath;
290
291 /**
292 * Anchor path.
293 */
294 private TreePath anchorPath;
295
296 /**
297 * True if paths in the selection should be expanded.
298 */
299 private boolean expandsSelectedPaths;
300
301 /**
302 * This is set to true for the life of the <code>setUI</code> call.
303 */
304 private boolean settingUI;
305
306 /** If true, mouse presses on selections initiate a drag operation. */
307 private boolean dragEnabled;
308
309 /**
310 * The drop mode for this component.
311 */
312 private DropMode dropMode = DropMode.USE_SELECTION;
313
314 /**
315 * The drop location.
316 */
317 private transient DropLocation dropLocation;
318
319 /**
320 * A subclass of <code>TransferHandler.DropLocation</code> representing
321 * a drop location for a <code>JTree</code>.
322 *
323 * @see #getDropLocation
324 * @since 1.6
325 */
326 public static final class DropLocation extends TransferHandler.DropLocation {
327 private final TreePath path;
328 private final int index;
329
330 private DropLocation(Point p, TreePath path, int index) {
331 super(p);
332 this.path = path;
333 this.index = index;
334 }
335
336 /**
337 * Returns the index where the dropped data should be inserted
338 * with respect to the path returned by <code>getPath()</code>.
339 * <p>
340 * For drop modes <code>DropMode.USE_SELECTION</code> and
341 * <code>DropMode.ON</code>, this index is unimportant (and it will
342 * always be <code>-1</code>) as the only interesting data is the
343 * path over which the drop operation occurred.
344 * <p>
345 * For drop mode <code>DropMode.INSERT</code>, this index
346 * indicates the index at which the data should be inserted into
347 * the parent path represented by <code>getPath()</code>.
348 * <code>-1</code> indicates that the drop occurred over the
349 * parent itself, and in most cases should be treated as inserting
350 * into either the beginning or the end of the parent's list of
351 * children.
352 * <p>
353 * For <code>DropMode.ON_OR_INSERT</code>, this value will be
354 * an insert index, as described above, or <code>-1</code> if
355 * the drop occurred over the path itself.
356 *
357 * @return the child index
358 * @see #getPath
359 */
360 public int getChildIndex() {
361 return index;
362 }
363
364 /**
365 * Returns the path where dropped data should be placed in the
366 * tree.
367 * <p>
368 * Interpretation of this value depends on the drop mode set on the
369 * component. If the drop mode is <code>DropMode.USE_SELECTION</code>
370 * or <code>DropMode.ON</code>, the return value is the path in the
371 * tree over which the data has been (or will be) dropped.
372 * <code>null</code> indicates that the drop is over empty space,
373 * not associated with a particular path.
374 * <p>
375 * If the drop mode is <code>DropMode.INSERT</code>, the return value
376 * refers to the path that should become the parent of the new data,
377 * in which case <code>getChildIndex()</code> indicates where the
378 * new item should be inserted into this parent path. A
379 * <code>null</code> path indicates that no parent path has been
380 * determined, which can happen for multiple reasons:
381 * <ul>
382 * <li>The tree has no model
383 * <li>There is no root in the tree
384 * <li>The root is collapsed
385 * <li>The root is a leaf node
386 * </ul>
387 * It is up to the developer to decide if and how they wish to handle
388 * the <code>null</code> case.
389 * <p>
390 * If the drop mode is <code>DropMode.ON_OR_INSERT</code>,
391 * <code>getChildIndex</code> can be used to determine whether the
392 * drop is on top of the path itself (<code>-1</code>) or the index
393 * at which it should be inserted into the path (values other than
394 * <code>-1</code>).
395 *
396 * @return the drop path
397 * @see #getChildIndex
398 */
399 public TreePath getPath() {
400 return path;
401 }
402
403 /**
404 * Returns a string representation of this drop location.
405 * This method is intended to be used for debugging purposes,
406 * and the content and format of the returned string may vary
407 * between implementations.
408 *
409 * @return a string representation of this drop location
410 */
411 public String toString() {
412 return getClass().getName()
413 + "[dropPoint=" + getDropPoint() + ","
414 + "path=" + path + ","
415 + "childIndex=" + index + "]";
416 }
417 }
418
419 /**
420 * The row to expand during DnD.
421 */
422 private int expandRow = -1;
423
424 private class TreeTimer extends Timer {
425 public TreeTimer() {
426 super(2000, null);
427 setRepeats(false);
428 }
429
430 public void fireActionPerformed(ActionEvent ae) {
431 JTree.this.expandRow(expandRow);
432 }
433 }
434
435 /**
436 * A timer to expand nodes during drop.
437 */
438 private TreeTimer dropTimer;
439
440 /**
441 * When <code>addTreeExpansionListener</code> is invoked,
442 * and <code>settingUI</code> is true, this ivar gets set to the passed in
443 * <code>Listener</code>. This listener is then notified first in
444 * <code>fireTreeCollapsed</code> and <code>fireTreeExpanded</code>.
445 * <p>This is an ugly workaround for a way to have the UI listener
446 * get notified before other listeners.
447 */
448 private transient TreeExpansionListener uiTreeExpansionListener;
449
450 /**
451 * Max number of stacks to keep around.
452 */
453 private static int TEMP_STACK_SIZE = 11;
454
455 //
456 // Bound property names
457 //
458 /** Bound property name for <code>cellRenderer</code>. */
459 public final static String CELL_RENDERER_PROPERTY = "cellRenderer";
460 /** Bound property name for <code>treeModel</code>. */
461 public final static String TREE_MODEL_PROPERTY = "model";
462 /** Bound property name for <code>rootVisible</code>. */
463 public final static String ROOT_VISIBLE_PROPERTY = "rootVisible";
464 /** Bound property name for <code>showsRootHandles</code>. */
465 public final static String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
466 /** Bound property name for <code>rowHeight</code>. */
467 public final static String ROW_HEIGHT_PROPERTY = "rowHeight";
468 /** Bound property name for <code>cellEditor</code>. */
469 public final static String CELL_EDITOR_PROPERTY = "cellEditor";
470 /** Bound property name for <code>editable</code>. */
471 public final static String EDITABLE_PROPERTY = "editable";
472 /** Bound property name for <code>largeModel</code>. */
473 public final static String LARGE_MODEL_PROPERTY = "largeModel";
474 /** Bound property name for selectionModel. */
475 public final static String SELECTION_MODEL_PROPERTY = "selectionModel";
476 /** Bound property name for <code>visibleRowCount</code>. */
477 public final static String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
478 /** Bound property name for <code>messagesStopCellEditing</code>. */
479 public final static String INVOKES_STOP_CELL_EDITING_PROPERTY = "invokesStopCellEditing";
480 /** Bound property name for <code>scrollsOnExpand</code>. */
481 public final static String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
482 /** Bound property name for <code>toggleClickCount</code>. */
483 public final static String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
484 /** Bound property name for <code>leadSelectionPath</code>.
485 * @since 1.3 */
486 public final static String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
487 /** Bound property name for anchor selection path.
488 * @since 1.3 */
489 public final static String ANCHOR_SELECTION_PATH_PROPERTY = "anchorSelectionPath";
490 /** Bound property name for expands selected paths property
491 * @since 1.3 */
492 public final static String EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths";
493
494
495 /**
496 * Creates and returns a sample <code>TreeModel</code>.
497 * Used primarily for beanbuilders to show something interesting.
498 *
499 * @return the default <code>TreeModel</code>
500 */
501 protected static TreeModel getDefaultTreeModel() {
502 DefaultMutableTreeNode root = new DefaultMutableTreeNode("JTree");
503 DefaultMutableTreeNode parent;
504
505 parent = new DefaultMutableTreeNode("colors");
506 root.add(parent);
507 parent.add(new DefaultMutableTreeNode("blue"));
508 parent.add(new DefaultMutableTreeNode("violet"));
509 parent.add(new DefaultMutableTreeNode("red"));
510 parent.add(new DefaultMutableTreeNode("yellow"));
511
512 parent = new DefaultMutableTreeNode("sports");
513 root.add(parent);
514 parent.add(new DefaultMutableTreeNode("basketball"));
515 parent.add(new DefaultMutableTreeNode("soccer"));
516 parent.add(new DefaultMutableTreeNode("football"));
517 parent.add(new DefaultMutableTreeNode("hockey"));
518
519 parent = new DefaultMutableTreeNode("food");
520 root.add(parent);
521 parent.add(new DefaultMutableTreeNode("hot dogs"));
522 parent.add(new DefaultMutableTreeNode("pizza"));
523 parent.add(new DefaultMutableTreeNode("ravioli"));
524 parent.add(new DefaultMutableTreeNode("bananas"));
525 return new DefaultTreeModel(root);
526 }
527
528 /**
529 * Returns a <code>TreeModel</code> wrapping the specified object.
530 * If the object is:<ul>
531 * <li>an array of <code>Object</code>s,
532 * <li>a <code>Hashtable</code>, or
533 * <li>a <code>Vector</code>
534 * </ul>then a new root node is created with each of the incoming
535 * objects as children. Otherwise, a new root is created with
536 * a value of {@code "root"}.
537 *
538 * @param value the <code>Object</code> used as the foundation for
539 * the <code>TreeModel</code>
540 * @return a <code>TreeModel</code> wrapping the specified object
541 */
542 protected static TreeModel createTreeModel(Object value) {
543 DefaultMutableTreeNode root;
544
545 if((value instanceof Object[]) || (value instanceof Hashtable) ||
546 (value instanceof Vector)) {
547 root = new DefaultMutableTreeNode("root");
548 DynamicUtilTreeNode.createChildren(root, value);
549 }
550 else {
551 root = new DynamicUtilTreeNode("root", value);
552 }
553 return new DefaultTreeModel(root, false);
554 }
555
556 /**
557 * Returns a <code>JTree</code> with a sample model.
558 * The default model used by the tree defines a leaf node as any node
559 * without children.
560 *
561 * @see DefaultTreeModel#asksAllowsChildren
562 */
563 public JTree() {
564 this(getDefaultTreeModel());
565 }
566
567 /**
568 * Returns a <code>JTree</code> with each element of the
569 * specified array as the
570 * child of a new root node which is not displayed.
571 * By default, the tree defines a leaf node as any node without
572 * children.
573 *
574 * @param value an array of <code>Object</code>s
575 * @see DefaultTreeModel#asksAllowsChildren
576 */
577 public JTree(Object[] value) {
578 this(createTreeModel(value));
579 this.setRootVisible(false);
580 this.setShowsRootHandles(true);
581 expandRoot();
582 }
583
584 /**
585 * Returns a <code>JTree</code> with each element of the specified
586 * <code>Vector</code> as the
587 * child of a new root node which is not displayed. By default, the
588 * tree defines a leaf node as any node without children.
589 *
590 * @param value a <code>Vector</code>
591 * @see DefaultTreeModel#asksAllowsChildren
592 */
593 public JTree(Vector<?> value) {
594 this(createTreeModel(value));
595 this.setRootVisible(false);
596 this.setShowsRootHandles(true);
597 expandRoot();
598 }
599
600 /**
601 * Returns a <code>JTree</code> created from a <code>Hashtable</code>
602 * which does not display with root.
603 * Each value-half of the key/value pairs in the <code>HashTable</code>
604 * becomes a child of the new root node. By default, the tree defines
605 * a leaf node as any node without children.
606 *
607 * @param value a <code>Hashtable</code>
608 * @see DefaultTreeModel#asksAllowsChildren
609 */
610 public JTree(Hashtable<?,?> value) {
611 this(createTreeModel(value));
612 this.setRootVisible(false);
613 this.setShowsRootHandles(true);
614 expandRoot();
615 }
616
617 /**
618 * Returns a <code>JTree</code> with the specified
619 * <code>TreeNode</code> as its root,
620 * which displays the root node.
621 * By default, the tree defines a leaf node as any node without children.
622 *
623 * @param root a <code>TreeNode</code> object
624 * @see DefaultTreeModel#asksAllowsChildren
625 */
626 public JTree(TreeNode root) {
627 this(root, false);
628 }
629
630 /**
631 * Returns a <code>JTree</code> with the specified <code>TreeNode</code>
632 * as its root, which
633 * displays the root node and which decides whether a node is a
634 * leaf node in the specified manner.
635 *
636 * @param root a <code>TreeNode</code> object
637 * @param asksAllowsChildren if false, any node without children is a
638 * leaf node; if true, only nodes that do not allow
639 * children are leaf nodes
640 * @see DefaultTreeModel#asksAllowsChildren
641 */
642 public JTree(TreeNode root, boolean asksAllowsChildren) {
643 this(new DefaultTreeModel(root, asksAllowsChildren));
644 }
645
646 /**
647 * Returns an instance of <code>JTree</code> which displays the root node
648 * -- the tree is created using the specified data model.
649 *
650 * @param newModel the <code>TreeModel</code> to use as the data model
651 */
652 @ConstructorProperties({"model"})
653 public JTree(TreeModel newModel) {
654 super();
655 expandedStack = new Stack();
656 toggleClickCount = 2;
657 expandedState = new Hashtable();
658 setLayout(null);
659 rowHeight = 16;
660 visibleRowCount = 20;
661 rootVisible = true;
662 selectionModel = new DefaultTreeSelectionModel();
663 cellRenderer = null;
664 scrollsOnExpand = true;
665 setOpaque(true);
666 expandsSelectedPaths = true;
667 updateUI();
668 setModel(newModel);
669 }
670
671 /**
672 * Returns the L&F object that renders this component.
673 *
674 * @return the <code>TreeUI</code> object that renders this component
675 */
676 public TreeUI getUI() {
677 return (TreeUI)ui;
678 }
679
680 /**
681 * Sets the L&F object that renders this component.
682 * <p>
683 * This is a bound property.
684 *
685 * @param ui the <code>TreeUI</code> L&F object
686 * @see UIDefaults#getUI
687 * @beaninfo
688 * bound: true
689 * hidden: true
690 * attribute: visualUpdate true
691 * description: The UI object that implements the Component's LookAndFeel.
692 */
693 public void setUI(TreeUI ui) {
694 if ((TreeUI)this.ui != ui) {
695 settingUI = true;
696 uiTreeExpansionListener = null;
697 try {
698 super.setUI(ui);
699 }
700 finally {
701 settingUI = false;
702 }
703 }
704 }
705
706 /**
707 * Notification from the <code>UIManager</code> that the L&F has changed.
708 * Replaces the current UI object with the latest version from the
709 * <code>UIManager</code>.
710 *
711 * @see JComponent#updateUI
712 */
713 public void updateUI() {
714 setUI((TreeUI)UIManager.getUI(this));
715
716 SwingUtilities.updateRendererOrEditorUI(getCellRenderer());
717 SwingUtilities.updateRendererOrEditorUI(getCellEditor());
718 }
719
720
721 /**
722 * Returns the name of the L&F class that renders this component.
723 *
724 * @return the string "TreeUI"
725 * @see JComponent#getUIClassID
726 * @see UIDefaults#getUI
727 */
728 public String getUIClassID() {
729 return uiClassID;
730 }
731
732
733 /**
734 * Returns the current <code>TreeCellRenderer</code>
735 * that is rendering each cell.
736 *
737 * @return the <code>TreeCellRenderer</code> that is rendering each cell
738 */
739 public TreeCellRenderer getCellRenderer() {
740 return cellRenderer;
741 }
742
743 /**
744 * Sets the <code>TreeCellRenderer</code> that will be used to
745 * draw each cell.
746 * <p>
747 * This is a bound property.
748 *
749 * @param x the <code>TreeCellRenderer</code> that is to render each cell
750 * @beaninfo
751 * bound: true
752 * description: The TreeCellRenderer that will be used to draw
753 * each cell.
754 */
755 public void setCellRenderer(TreeCellRenderer x) {
756 TreeCellRenderer oldValue = cellRenderer;
757
758 cellRenderer = x;
759 firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, cellRenderer);
760 invalidate();
761 }
762
763 /**
764 * Determines whether the tree is editable. Fires a property
765 * change event if the new setting is different from the existing
766 * setting.
767 * <p>
768 * This is a bound property.
769 *
770 * @param flag a boolean value, true if the tree is editable
771 * @beaninfo
772 * bound: true
773 * description: Whether the tree is editable.
774 */
775 public void setEditable(boolean flag) {
776 boolean oldValue = this.editable;
777
778 this.editable = flag;
779 firePropertyChange(EDITABLE_PROPERTY, oldValue, flag);
780 if (accessibleContext != null) {
781 accessibleContext.firePropertyChange(
782 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
783 (oldValue ? AccessibleState.EDITABLE : null),
784 (flag ? AccessibleState.EDITABLE : null));
785 }
786 }
787
788 /**
789 * Returns true if the tree is editable.
790 *
791 * @return true if the tree is editable
792 */
793 public boolean isEditable() {
794 return editable;
795 }
796
797 /**
798 * Sets the cell editor. A <code>null</code> value implies that the
799 * tree cannot be edited. If this represents a change in the
800 * <code>cellEditor</code>, the <code>propertyChange</code>
801 * method is invoked on all listeners.
802 * <p>
803 * This is a bound property.
804 *
805 * @param cellEditor the <code>TreeCellEditor</code> to use
806 * @beaninfo
807 * bound: true
808 * description: The cell editor. A null value implies the tree
809 * cannot be edited.
810 */
811 public void setCellEditor(TreeCellEditor cellEditor) {
812 TreeCellEditor oldEditor = this.cellEditor;
813
814 this.cellEditor = cellEditor;
815 firePropertyChange(CELL_EDITOR_PROPERTY, oldEditor, cellEditor);
816 invalidate();
817 }
818
819 /**
820 * Returns the editor used to edit entries in the tree.
821 *
822 * @return the <code>TreeCellEditor</code> in use,
823 * or <code>null</code> if the tree cannot be edited
824 */
825 public TreeCellEditor getCellEditor() {
826 return cellEditor;
827 }
828
829 /**
830 * Returns the <code>TreeModel</code> that is providing the data.
831 *
832 * @return the <code>TreeModel</code> that is providing the data
833 */
834 public TreeModel getModel() {
835 return treeModel;
836 }
837
838 /**
839 * Sets the <code>TreeModel</code> that will provide the data.
840 * <p>
841 * This is a bound property.
842 *
843 * @param newModel the <code>TreeModel</code> that is to provide the data
844 * @beaninfo
845 * bound: true
846 * description: The TreeModel that will provide the data.
847 */
848 public void setModel(TreeModel newModel) {
849 clearSelection();
850
851 TreeModel oldModel = treeModel;
852
853 if(treeModel != null && treeModelListener != null)
854 treeModel.removeTreeModelListener(treeModelListener);
855
856 if (accessibleContext != null) {
857 if (treeModel != null) {
858 treeModel.removeTreeModelListener((TreeModelListener)accessibleContext);
859 }
860 if (newModel != null) {
861 newModel.addTreeModelListener((TreeModelListener)accessibleContext);
862 }
863 }
864
865 treeModel = newModel;
866 clearToggledPaths();
867 if(treeModel != null) {
868 if(treeModelListener == null)
869 treeModelListener = createTreeModelListener();
870 if(treeModelListener != null)
871 treeModel.addTreeModelListener(treeModelListener);
872 // Mark the root as expanded, if it isn't a leaf.
873 if(treeModel.getRoot() != null &&
874 !treeModel.isLeaf(treeModel.getRoot())) {
875 expandedState.put(new TreePath(treeModel.getRoot()),
876 Boolean.TRUE);
877 }
878 }
879 firePropertyChange(TREE_MODEL_PROPERTY, oldModel, treeModel);
880 invalidate();
881 }
882
883 /**
884 * Returns true if the root node of the tree is displayed.
885 *
886 * @return true if the root node of the tree is displayed
887 * @see #rootVisible
888 */
889 public boolean isRootVisible() {
890 return rootVisible;
891 }
892
893 /**
894 * Determines whether or not the root node from
895 * the <code>TreeModel</code> is visible.
896 * <p>
897 * This is a bound property.
898 *
899 * @param rootVisible true if the root node of the tree is to be displayed
900 * @see #rootVisible
901 * @beaninfo
902 * bound: true
903 * description: Whether or not the root node
904 * from the TreeModel is visible.
905 */
906 public void setRootVisible(boolean rootVisible) {
907 boolean oldValue = this.rootVisible;
908
909 this.rootVisible = rootVisible;
910 firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, this.rootVisible);
911 if (accessibleContext != null) {
912 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
913 }
914 }
915
916 /**
917 * Sets the value of the <code>showsRootHandles</code> property,
918 * which specifies whether the node handles should be displayed.
919 * The default value of this property depends on the constructor
920 * used to create the <code>JTree</code>.
921 * Some look and feels might not support handles;
922 * they will ignore this property.
923 * <p>
924 * This is a bound property.
925 *
926 * @param newValue <code>true</code> if root handles should be displayed;
927 * otherwise, <code>false</code>
928 * @see #showsRootHandles
929 * @see #getShowsRootHandles
930 * @beaninfo
931 * bound: true
932 * description: Whether the node handles are to be
933 * displayed.
934 */
935 public void setShowsRootHandles(boolean newValue) {
936 boolean oldValue = showsRootHandles;
937 TreeModel model = getModel();
938
939 showsRootHandles = newValue;
940 showsRootHandlesSet = true;
941 firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue,
942 showsRootHandles);
943 if (accessibleContext != null) {
944 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
945 }
946 invalidate();
947 }
948
949 /**
950 * Returns the value of the <code>showsRootHandles</code> property.
951 *
952 * @return the value of the <code>showsRootHandles</code> property
953 * @see #showsRootHandles
954 */
955 public boolean getShowsRootHandles()
956 {
957 return showsRootHandles;
958 }
959
960 /**
961 * Sets the height of each cell, in pixels. If the specified value
962 * is less than or equal to zero the current cell renderer is
963 * queried for each row's height.
964 * <p>
965 * This is a bound property.
966 *
967 * @param rowHeight the height of each cell, in pixels
968 * @beaninfo
969 * bound: true
970 * description: The height of each cell.
971 */
972 public void setRowHeight(int rowHeight)
973 {
974 int oldValue = this.rowHeight;
975
976 this.rowHeight = rowHeight;
977 rowHeightSet = true;
978 firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, this.rowHeight);
979 invalidate();
980 }
981
982 /**
983 * Returns the height of each row. If the returned value is less than
984 * or equal to 0 the height for each row is determined by the
985 * renderer.
986 *
987 */
988 public int getRowHeight()
989 {
990 return rowHeight;
991 }
992
993 /**
994 * Returns true if the height of each display row is a fixed size.
995 *
996 * @return true if the height of each row is a fixed size
997 */
998 public boolean isFixedRowHeight()
999 {
1000 return (rowHeight > 0);
1001 }
1002
1003 /**
1004 * Specifies whether the UI should use a large model.
1005 * (Not all UIs will implement this.) Fires a property change
1006 * for the LARGE_MODEL_PROPERTY.
1007 * <p>
1008 * This is a bound property.
1009 *
1010 * @param newValue true to suggest a large model to the UI
1011 * @see #largeModel
1012 * @beaninfo
1013 * bound: true
1014 * description: Whether the UI should use a
1015 * large model.
1016 */
1017 public void setLargeModel(boolean newValue) {
1018 boolean oldValue = largeModel;
1019
1020 largeModel = newValue;
1021 firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, newValue);
1022 }
1023
1024 /**
1025 * Returns true if the tree is configured for a large model.
1026 *
1027 * @return true if a large model is suggested
1028 * @see #largeModel
1029 */
1030 public boolean isLargeModel() {
1031 return largeModel;
1032 }
1033
1034 /**
1035 * Determines what happens when editing is interrupted by selecting
1036 * another node in the tree, a change in the tree's data, or by some
1037 * other means. Setting this property to <code>true</code> causes the
1038 * changes to be automatically saved when editing is interrupted.
1039 * <p>
1040 * Fires a property change for the INVOKES_STOP_CELL_EDITING_PROPERTY.
1041 *
1042 * @param newValue true means that <code>stopCellEditing</code> is invoked
1043 * when editing is interrupted, and data is saved; false means that
1044 * <code>cancelCellEditing</code> is invoked, and changes are lost
1045 * @beaninfo
1046 * bound: true
1047 * description: Determines what happens when editing is interrupted,
1048 * selecting another node in the tree, a change in the
1049 * tree's data, or some other means.
1050 */
1051 public void setInvokesStopCellEditing(boolean newValue) {
1052 boolean oldValue = invokesStopCellEditing;
1053
1054 invokesStopCellEditing = newValue;
1055 firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, oldValue,
1056 newValue);
1057 }
1058
1059 /**
1060 * Returns the indicator that tells what happens when editing is
1061 * interrupted.
1062 *
1063 * @return the indicator that tells what happens when editing is
1064 * interrupted
1065 * @see #setInvokesStopCellEditing
1066 */
1067 public boolean getInvokesStopCellEditing() {
1068 return invokesStopCellEditing;
1069 }
1070
1071 /**
1072 * Sets the <code>scrollsOnExpand</code> property,
1073 * which determines whether the
1074 * tree might scroll to show previously hidden children.
1075 * If this property is <code>true</code> (the default),
1076 * when a node expands
1077 * the tree can use scrolling to make
1078 * the maximum possible number of the node's descendants visible.
1079 * In some look and feels, trees might not need to scroll when expanded;
1080 * those look and feels will ignore this property.
1081 * <p>
1082 * This is a bound property.
1083 *
1084 * @param newValue <code>false</code> to disable scrolling on expansion;
1085 * <code>true</code> to enable it
1086 * @see #getScrollsOnExpand
1087 *
1088 * @beaninfo
1089 * bound: true
1090 * description: Indicates if a node descendant should be scrolled when expanded.
1091 */
1092 public void setScrollsOnExpand(boolean newValue) {
1093 boolean oldValue = scrollsOnExpand;
1094
1095 scrollsOnExpand = newValue;
1096 scrollsOnExpandSet = true;
1097 firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue,
1098 newValue);
1099 }
1100
1101 /**
1102 * Returns the value of the <code>scrollsOnExpand</code> property.
1103 *
1104 * @return the value of the <code>scrollsOnExpand</code> property
1105 */
1106 public boolean getScrollsOnExpand() {
1107 return scrollsOnExpand;
1108 }
1109
1110 /**
1111 * Sets the number of mouse clicks before a node will expand or close.
1112 * The default is two.
1113 * <p>
1114 * This is a bound property.
1115 *
1116 * @since 1.3
1117 * @beaninfo
1118 * bound: true
1119 * description: Number of clicks before a node will expand/collapse.
1120 */
1121 public void setToggleClickCount(int clickCount) {
1122 int oldCount = toggleClickCount;
1123
1124 toggleClickCount = clickCount;
1125 firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldCount,
1126 clickCount);
1127 }
1128
1129 /**
1130 * Returns the number of mouse clicks needed to expand or close a node.
1131 *
1132 * @return number of mouse clicks before node is expanded
1133 * @since 1.3
1134 */
1135 public int getToggleClickCount() {
1136 return toggleClickCount;
1137 }
1138
1139 /**
1140 * Configures the <code>expandsSelectedPaths</code> property. If
1141 * true, any time the selection is changed, either via the
1142 * <code>TreeSelectionModel</code>, or the cover methods provided by
1143 * <code>JTree</code>, the <code>TreePath</code>s parents will be
1144 * expanded to make them visible (visible meaning the parent path is
1145 * expanded, not necessarily in the visible rectangle of the
1146 * <code>JTree</code>). If false, when the selection
1147 * changes the nodes parent is not made visible (all its parents expanded).
1148 * This is useful if you wish to have your selection model maintain paths
1149 * that are not always visible (all parents expanded).
1150 * <p>
1151 * This is a bound property.
1152 *
1153 * @param newValue the new value for <code>expandsSelectedPaths</code>
1154 *
1155 * @since 1.3
1156 * @beaninfo
1157 * bound: true
1158 * description: Indicates whether changes to the selection should make
1159 * the parent of the path visible.
1160 */
1161 public void setExpandsSelectedPaths(boolean newValue) {
1162 boolean oldValue = expandsSelectedPaths;
1163
1164 expandsSelectedPaths = newValue;
1165 firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue,
1166 newValue);
1167 }
1168
1169 /**
1170 * Returns the <code>expandsSelectedPaths</code> property.
1171 * @return true if selection changes result in the parent path being
1172 * expanded
1173 * @since 1.3
1174 * @see #setExpandsSelectedPaths
1175 */
1176 public boolean getExpandsSelectedPaths() {
1177 return expandsSelectedPaths;
1178 }
1179
1180 /**
1181 * Turns on or off automatic drag handling. In order to enable automatic
1182 * drag handling, this property should be set to {@code true}, and the
1183 * tree's {@code TransferHandler} needs to be {@code non-null}.
1184 * The default value of the {@code dragEnabled} property is {@code false}.
1185 * <p>
1186 * The job of honoring this property, and recognizing a user drag gesture,
1187 * lies with the look and feel implementation, and in particular, the tree's
1188 * {@code TreeUI}. When automatic drag handling is enabled, most look and
1189 * feels (including those that subclass {@code BasicLookAndFeel}) begin a
1190 * drag and drop operation whenever the user presses the mouse button over
1191 * an item and then moves the mouse a few pixels. Setting this property to
1192 * {@code true} can therefore have a subtle effect on how selections behave.
1193 * <p>
1194 * If a look and feel is used that ignores this property, you can still
1195 * begin a drag and drop operation by calling {@code exportAsDrag} on the
1196 * tree's {@code TransferHandler}.
1197 *
1198 * @param b whether or not to enable automatic drag handling
1199 * @exception HeadlessException if
1200 * <code>b</code> is <code>true</code> and
1201 * <code>GraphicsEnvironment.isHeadless()</code>
1202 * returns <code>true</code>
1203 * @see java.awt.GraphicsEnvironment#isHeadless
1204 * @see #getDragEnabled
1205 * @see #setTransferHandler
1206 * @see TransferHandler
1207 * @since 1.4
1208 *
1209 * @beaninfo
1210 * description: determines whether automatic drag handling is enabled
1211 * bound: false
1212 */
1213 public void setDragEnabled(boolean b) {
1214 if (b && GraphicsEnvironment.isHeadless()) {
1215 throw new HeadlessException();
1216 }
1217 dragEnabled = b;
1218 }
1219
1220 /**
1221 * Returns whether or not automatic drag handling is enabled.
1222 *
1223 * @return the value of the {@code dragEnabled} property
1224 * @see #setDragEnabled
1225 * @since 1.4
1226 */
1227 public boolean getDragEnabled() {
1228 return dragEnabled;
1229 }
1230
1231 /**
1232 * Sets the drop mode for this component. For backward compatibility,
1233 * the default for this property is <code>DropMode.USE_SELECTION</code>.
1234 * Usage of one of the other modes is recommended, however, for an
1235 * improved user experience. <code>DropMode.ON</code>, for instance,
1236 * offers similar behavior of showing items as selected, but does so without
1237 * affecting the actual selection in the tree.
1238 * <p>
1239 * <code>JTree</code> supports the following drop modes:
1240 * <ul>
1241 * <li><code>DropMode.USE_SELECTION</code></li>
1242 * <li><code>DropMode.ON</code></li>
1243 * <li><code>DropMode.INSERT</code></li>
1244 * <li><code>DropMode.ON_OR_INSERT</code></li>
1245 * </ul>
1246 * <p>
1247 * The drop mode is only meaningful if this component has a
1248 * <code>TransferHandler</code> that accepts drops.
1249 *
1250 * @param dropMode the drop mode to use
1251 * @throws IllegalArgumentException if the drop mode is unsupported
1252 * or <code>null</code>
1253 * @see #getDropMode
1254 * @see #getDropLocation
1255 * @see #setTransferHandler
1256 * @see TransferHandler
1257 * @since 1.6
1258 */
1259 public final void setDropMode(DropMode dropMode) {
1260 if (dropMode != null) {
1261 switch (dropMode) {
1262 case USE_SELECTION:
1263 case ON:
1264 case INSERT:
1265 case ON_OR_INSERT:
1266 this.dropMode = dropMode;
1267 return;
1268 }
1269 }
1270
1271 throw new IllegalArgumentException(dropMode + ": Unsupported drop mode for tree");
1272 }
1273
1274 /**
1275 * Returns the drop mode for this component.
1276 *
1277 * @return the drop mode for this component
1278 * @see #setDropMode
1279 * @since 1.6
1280 */
1281 public final DropMode getDropMode() {
1282 return dropMode;
1283 }
1284
1285 /**
1286 * Calculates a drop location in this component, representing where a
1287 * drop at the given point should insert data.
1288 *
1289 * @param p the point to calculate a drop location for
1290 * @return the drop location, or <code>null</code>
1291 */
1292 DropLocation dropLocationForPoint(Point p) {
1293 DropLocation location = null;
1294
1295 int row = getClosestRowForLocation(p.x, p.y);
1296 Rectangle bounds = getRowBounds(row);
1297 TreeModel model = getModel();
1298 Object root = (model == null) ? null : model.getRoot();
1299 TreePath rootPath = (root == null) ? null : new TreePath(root);
1300
1301 TreePath child = null;
1302 TreePath parent = null;
1303 boolean outside = row == -1
1304 || p.y < bounds.y
1305 || p.y >= bounds.y + bounds.height;
1306
1307 switch(dropMode) {
1308 case USE_SELECTION:
1309 case ON:
1310 if (outside) {
1311 location = new DropLocation(p, null, -1);
1312 } else {
1313 location = new DropLocation(p, getPathForRow(row), -1);
1314 }
1315
1316 break;
1317 case INSERT:
1318 case ON_OR_INSERT:
1319 if (row == -1) {
1320 if (root != null && !model.isLeaf(root) && isExpanded(rootPath)) {
1321 location = new DropLocation(p, rootPath, 0);
1322 } else {
1323 location = new DropLocation(p, null, -1);
1324 }
1325
1326 break;
1327 }
1328
1329 boolean checkOn = dropMode == DropMode.ON_OR_INSERT
1330 || !model.isLeaf(getPathForRow(row).getLastPathComponent());
1331
1332 Section section = SwingUtilities2.liesInVertical(bounds, p, checkOn);
1333 if(section == LEADING) {
1334 child = getPathForRow(row);
1335 parent = child.getParentPath();
1336 } else if (section == TRAILING) {
1337 int index = row + 1;
1338 if (index >= getRowCount()) {
1339 if (model.isLeaf(root) || !isExpanded(rootPath)) {
1340 location = new DropLocation(p, null, -1);
1341 } else {
1342 parent = rootPath;
1343 index = model.getChildCount(root);
1344 location = new DropLocation(p, parent, index);
1345 }
1346
1347 break;
1348 }
1349
1350 child = getPathForRow(index);
1351 parent = child.getParentPath();
1352 } else {
1353 assert checkOn;
1354 location = new DropLocation(p, getPathForRow(row), -1);
1355 break;
1356 }
1357
1358 if (parent != null) {
1359 location = new DropLocation(p, parent,
1360 model.getIndexOfChild(parent.getLastPathComponent(),
1361 child.getLastPathComponent()));
1362 } else if (checkOn || !model.isLeaf(root)) {
1363 location = new DropLocation(p, rootPath, -1);
1364 } else {
1365 location = new DropLocation(p, null, -1);
1366 }
1367
1368 break;
1369 default:
1370 assert false : "Unexpected drop mode";
1371 }
1372
1373 if (outside || row != expandRow) {
1374 cancelDropTimer();
1375 }
1376
1377 if (!outside && row != expandRow) {
1378 if (isCollapsed(row)) {
1379 expandRow = row;
1380 startDropTimer();
1381 }
1382 }
1383
1384 return location;
1385 }
1386
1387 /**
1388 * Called to set or clear the drop location during a DnD operation.
1389 * In some cases, the component may need to use it's internal selection
1390 * temporarily to indicate the drop location. To help facilitate this,
1391 * this method returns and accepts as a parameter a state object.
1392 * This state object can be used to store, and later restore, the selection
1393 * state. Whatever this method returns will be passed back to it in
1394 * future calls, as the state parameter. If it wants the DnD system to
1395 * continue storing the same state, it must pass it back every time.
1396 * Here's how this is used:
1397 * <p>
1398 * Let's say that on the first call to this method the component decides
1399 * to save some state (because it is about to use the selection to show
1400 * a drop index). It can return a state object to the caller encapsulating
1401 * any saved selection state. On a second call, let's say the drop location
1402 * is being changed to something else. The component doesn't need to
1403 * restore anything yet, so it simply passes back the same state object
1404 * to have the DnD system continue storing it. Finally, let's say this
1405 * method is messaged with <code>null</code>. This means DnD
1406 * is finished with this component for now, meaning it should restore
1407 * state. At this point, it can use the state parameter to restore
1408 * said state, and of course return <code>null</code> since there's
1409 * no longer anything to store.
1410 *
1411 * @param location the drop location (as calculated by
1412 * <code>dropLocationForPoint</code>) or <code>null</code>
1413 * if there's no longer a valid drop location
1414 * @param state the state object saved earlier for this component,
1415 * or <code>null</code>
1416 * @param forDrop whether or not the method is being called because an
1417 * actual drop occurred
1418 * @return any saved state for this component, or <code>null</code> if none
1419 */
1420 Object setDropLocation(TransferHandler.DropLocation location,
1421 Object state,
1422 boolean forDrop) {
1423
1424 Object retVal = null;
1425 DropLocation treeLocation = (DropLocation)location;
1426
1427 if (dropMode == DropMode.USE_SELECTION) {
1428 if (treeLocation == null) {
1429 if (!forDrop && state != null) {
1430 setSelectionPaths(((TreePath[][])state)[0]);
1431 setAnchorSelectionPath(((TreePath[][])state)[1][0]);
1432 setLeadSelectionPath(((TreePath[][])state)[1][1]);
1433 }
1434 } else {
1435 if (dropLocation == null) {
1436 TreePath[] paths = getSelectionPaths();
1437 if (paths == null) {
1438 paths = new TreePath[0];
1439 }
1440
1441 retVal = new TreePath[][] {paths,
1442 {getAnchorSelectionPath(), getLeadSelectionPath()}};
1443 } else {
1444 retVal = state;
1445 }
1446
1447 setSelectionPath(treeLocation.getPath());
1448 }
1449 }
1450
1451 DropLocation old = dropLocation;
1452 dropLocation = treeLocation;
1453 firePropertyChange("dropLocation", old, dropLocation);
1454
1455 return retVal;
1456 }
1457
1458 /**
1459 * Called to indicate to this component that DnD is done.
1460 * Allows for us to cancel the expand timer.
1461 */
1462 void dndDone() {
1463 cancelDropTimer();
1464 dropTimer = null;
1465 }
1466
1467 /**
1468 * Returns the location that this component should visually indicate
1469 * as the drop location during a DnD operation over the component,
1470 * or {@code null} if no location is to currently be shown.
1471 * <p>
1472 * This method is not meant for querying the drop location
1473 * from a {@code TransferHandler}, as the drop location is only
1474 * set after the {@code TransferHandler}'s <code>canImport</code>
1475 * has returned and has allowed for the location to be shown.
1476 * <p>
1477 * When this property changes, a property change event with
1478 * name "dropLocation" is fired by the component.
1479 *
1480 * @return the drop location
1481 * @see #setDropMode
1482 * @see TransferHandler#canImport(TransferHandler.TransferSupport)
1483 * @since 1.6
1484 */
1485 public final DropLocation getDropLocation() {
1486 return dropLocation;
1487 }
1488
1489 private void startDropTimer() {
1490 if (dropTimer == null) {
1491 dropTimer = new TreeTimer();
1492 }
1493 dropTimer.start();
1494 }
1495
1496 private void cancelDropTimer() {
1497 if (dropTimer != null && dropTimer.isRunning()) {
1498 expandRow = -1;
1499 dropTimer.stop();
1500 }
1501 }
1502
1503 /**
1504 * Returns <code>isEditable</code>. This is invoked from the UI before
1505 * editing begins to insure that the given path can be edited. This
1506 * is provided as an entry point for subclassers to add filtered
1507 * editing without having to resort to creating a new editor.
1508 *
1509 * @return true if every parent node and the node itself is editable
1510 * @see #isEditable
1511 */
1512 public boolean isPathEditable(TreePath path) {
1513 return isEditable();
1514 }
1515
1516 /**
1517 * Overrides <code>JComponent</code>'s <code>getToolTipText</code>
1518 * method in order to allow
1519 * renderer's tips to be used if it has text set.
1520 * <p>
1521 * NOTE: For <code>JTree</code> to properly display tooltips of its
1522 * renderers, <code>JTree</code> must be a registered component with the
1523 * <code>ToolTipManager</code>. This can be done by invoking
1524 * <code>ToolTipManager.sharedInstance().registerComponent(tree)</code>.
1525 * This is not done automatically!
1526 *
1527 * @param event the <code>MouseEvent</code> that initiated the
1528 * <code>ToolTip</code> display
1529 * @return a string containing the tooltip or <code>null</code>
1530 * if <code>event</code> is null
1531 */
1532 public String getToolTipText(MouseEvent event) {
1533 String tip = null;
1534
1535 if(event != null) {
1536 Point p = event.getPoint();
1537 int selRow = getRowForLocation(p.x, p.y);
1538 TreeCellRenderer r = getCellRenderer();
1539
1540 if(selRow != -1 && r != null) {
1541 TreePath path = getPathForRow(selRow);
1542 Object lastPath = path.getLastPathComponent();
1543 Component rComponent = r.getTreeCellRendererComponent
1544 (this, lastPath, isRowSelected(selRow),
1545 isExpanded(selRow), getModel().isLeaf(lastPath), selRow,
1546 true);
1547
1548 if(rComponent instanceof JComponent) {
1549 MouseEvent newEvent;
1550 Rectangle pathBounds = getPathBounds(path);
1551
1552 p.translate(-pathBounds.x, -pathBounds.y);
1553 newEvent = new MouseEvent(rComponent, event.getID(),
1554 event.getWhen(),
1555 event.getModifiers(),
1556 p.x, p.y,
1557 event.getXOnScreen(),
1558 event.getYOnScreen(),
1559 event.getClickCount(),
1560 event.isPopupTrigger(),
1561 MouseEvent.NOBUTTON);
1562
1563 tip = ((JComponent)rComponent).getToolTipText(newEvent);
1564 }
1565 }
1566 }
1567 // No tip from the renderer get our own tip
1568 if (tip == null) {
1569 tip = getToolTipText();
1570 }
1571 return tip;
1572 }
1573
1574 /**
1575 * Called by the renderers to convert the specified value to
1576 * text. This implementation returns <code>value.toString</code>, ignoring
1577 * all other arguments. To control the conversion, subclass this
1578 * method and use any of the arguments you need.
1579 *
1580 * @param value the <code>Object</code> to convert to text
1581 * @param selected true if the node is selected
1582 * @param expanded true if the node is expanded
1583 * @param leaf true if the node is a leaf node
1584 * @param row an integer specifying the node's display row, where 0 is
1585 * the first row in the display
1586 * @param hasFocus true if the node has the focus
1587 * @return the <code>String</code> representation of the node's value
1588 */
1589 public String convertValueToText(Object value, boolean selected,
1590 boolean expanded, boolean leaf, int row,
1591 boolean hasFocus) {
1592 if(value != null) {
1593 String sValue = value.toString();
1594 if (sValue != null) {
1595 return sValue;
1596 }
1597 }
1598 return "";
1599 }
1600
1601 //
1602 // The following are convenience methods that get forwarded to the
1603 // current TreeUI.
1604 //
1605
1606 /**
1607 * Returns the number of viewable nodes. A node is viewable if all of its
1608 * parents are expanded. The root is only included in this count if
1609 * {@code isRootVisible()} is {@code true}. This returns {@code 0} if
1610 * the UI has not been set.
1611 *
1612 * @return the number of viewable nodes
1613 */
1614 public int getRowCount() {
1615 TreeUI tree = getUI();
1616
1617 if(tree != null)
1618 return tree.getRowCount(this);
1619 return 0;
1620 }
1621
1622 /**
1623 * Selects the node identified by the specified path. If any
1624 * component of the path is hidden (under a collapsed node), and
1625 * <code>getExpandsSelectedPaths</code> is true it is
1626 * exposed (made viewable).
1627 *
1628 * @param path the <code>TreePath</code> specifying the node to select
1629 */
1630 public void setSelectionPath(TreePath path) {
1631 getSelectionModel().setSelectionPath(path);
1632 }
1633
1634 /**
1635 * Selects the nodes identified by the specified array of paths.
1636 * If any component in any of the paths is hidden (under a collapsed
1637 * node), and <code>getExpandsSelectedPaths</code> is true
1638 * it is exposed (made viewable).
1639 *
1640 * @param paths an array of <code>TreePath</code> objects that specifies
1641 * the nodes to select
1642 */
1643 public void setSelectionPaths(TreePath[] paths) {
1644 getSelectionModel().setSelectionPaths(paths);
1645 }
1646
1647 /**
1648 * Sets the path identifies as the lead. The lead may not be selected.
1649 * The lead is not maintained by <code>JTree</code>,
1650 * rather the UI will update it.
1651 * <p>
1652 * This is a bound property.
1653 *
1654 * @param newPath the new lead path
1655 * @since 1.3
1656 * @beaninfo
1657 * bound: true
1658 * description: Lead selection path
1659 */
1660 public void setLeadSelectionPath(TreePath newPath) {
1661 TreePath oldValue = leadPath;
1662
1663 leadPath = newPath;
1664 firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, newPath);
1665 }
1666
1667 /**
1668 * Sets the path identified as the anchor.
1669 * The anchor is not maintained by <code>JTree</code>, rather the UI will
1670 * update it.
1671 * <p>
1672 * This is a bound property.
1673 *
1674 * @param newPath the new anchor path
1675 * @since 1.3
1676 * @beaninfo
1677 * bound: true
1678 * description: Anchor selection path
1679 */
1680 public void setAnchorSelectionPath(TreePath newPath) {
1681 TreePath oldValue = anchorPath;
1682
1683 anchorPath = newPath;
1684 firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, newPath);
1685 }
1686
1687 /**
1688 * Selects the node at the specified row in the display.
1689 *
1690 * @param row the row to select, where 0 is the first row in
1691 * the display
1692 */
1693 public void setSelectionRow(int row) {
1694 int[] rows = { row };
1695
1696 setSelectionRows(rows);
1697 }
1698
1699 /**
1700 * Selects the nodes corresponding to each of the specified rows
1701 * in the display. If a particular element of <code>rows</code> is
1702 * < 0 or >= <code>getRowCount</code>, it will be ignored.
1703 * If none of the elements
1704 * in <code>rows</code> are valid rows, the selection will
1705 * be cleared. That is it will be as if <code>clearSelection</code>
1706 * was invoked.
1707 *
1708 * @param rows an array of ints specifying the rows to select,
1709 * where 0 indicates the first row in the display
1710 */
1711 public void setSelectionRows(int[] rows) {
1712 TreeUI ui = getUI();
1713
1714 if(ui != null && rows != null) {
1715 int numRows = rows.length;
1716 TreePath[] paths = new TreePath[numRows];
1717
1718 for(int counter = 0; counter < numRows; counter++) {
1719 paths[counter] = ui.getPathForRow(this, rows[counter]);
1720 }
1721 setSelectionPaths(paths);
1722 }
1723 }
1724
1725 /**
1726 * Adds the node identified by the specified <code>TreePath</code>
1727 * to the current selection. If any component of the path isn't
1728 * viewable, and <code>getExpandsSelectedPaths</code> is true it is
1729 * made viewable.
1730 * <p>
1731 * Note that <code>JTree</code> does not allow duplicate nodes to
1732 * exist as children under the same parent -- each sibling must be
1733 * a unique object.
1734 *
1735 * @param path the <code>TreePath</code> to add
1736 */
1737 public void addSelectionPath(TreePath path) {
1738 getSelectionModel().addSelectionPath(path);
1739 }
1740
1741 /**
1742 * Adds each path in the array of paths to the current selection. If
1743 * any component of any of the paths isn't viewable and
1744 * <code>getExpandsSelectedPaths</code> is true, it is
1745 * made viewable.
1746 * <p>
1747 * Note that <code>JTree</code> does not allow duplicate nodes to
1748 * exist as children under the same parent -- each sibling must be
1749 * a unique object.
1750 *
1751 * @param paths an array of <code>TreePath</code> objects that specifies
1752 * the nodes to add
1753 */
1754 public void addSelectionPaths(TreePath[] paths) {
1755 getSelectionModel().addSelectionPaths(paths);
1756 }
1757
1758 /**
1759 * Adds the path at the specified row to the current selection.
1760 *
1761 * @param row an integer specifying the row of the node to add,
1762 * where 0 is the first row in the display
1763 */
1764 public void addSelectionRow(int row) {
1765 int[] rows = { row };
1766
1767 addSelectionRows(rows);
1768 }
1769
1770 /**
1771 * Adds the paths at each of the specified rows to the current selection.
1772 *
1773 * @param rows an array of ints specifying the rows to add,
1774 * where 0 indicates the first row in the display
1775 */
1776 public void addSelectionRows(int[] rows) {
1777 TreeUI ui = getUI();
1778
1779 if(ui != null && rows != null) {
1780 int numRows = rows.length;
1781 TreePath[] paths = new TreePath[numRows];
1782
1783 for(int counter = 0; counter < numRows; counter++)
1784 paths[counter] = ui.getPathForRow(this, rows[counter]);
1785 addSelectionPaths(paths);
1786 }
1787 }
1788
1789 /**
1790 * Returns the last path component of the selected path. This is
1791 * a convenience method for
1792 * {@code getSelectionModel().getSelectionPath().getLastPathComponent()}.
1793 * This is typically only useful if the selection has one path.
1794 *
1795 * @return the last path component of the selected path, or
1796 * <code>null</code> if nothing is selected
1797 * @see TreePath#getLastPathComponent
1798 */
1799 public Object getLastSelectedPathComponent() {
1800 TreePath selPath = getSelectionModel().getSelectionPath();
1801
1802 if(selPath != null)
1803 return selPath.getLastPathComponent();
1804 return null;
1805 }
1806
1807 /**
1808 * Returns the path identified as the lead.
1809 * @return path identified as the lead
1810 */
1811 public TreePath getLeadSelectionPath() {
1812 return leadPath;
1813 }
1814
1815 /**
1816 * Returns the path identified as the anchor.
1817 * @return path identified as the anchor
1818 * @since 1.3
1819 */
1820 public TreePath getAnchorSelectionPath() {
1821 return anchorPath;
1822 }
1823
1824 /**
1825 * Returns the path to the first selected node.
1826 *
1827 * @return the <code>TreePath</code> for the first selected node,
1828 * or <code>null</code> if nothing is currently selected
1829 */
1830 public TreePath getSelectionPath() {
1831 return getSelectionModel().getSelectionPath();
1832 }
1833
1834 /**
1835 * Returns the paths of all selected values.
1836 *
1837 * @return an array of <code>TreePath</code> objects indicating the selected
1838 * nodes, or <code>null</code> if nothing is currently selected
1839 */
1840 public TreePath[] getSelectionPaths() {
1841 return getSelectionModel().getSelectionPaths();
1842 }
1843
1844 /**
1845 * Returns all of the currently selected rows. This method is simply
1846 * forwarded to the <code>TreeSelectionModel</code>.
1847 * If nothing is selected <code>null</code> or an empty array will
1848 * be returned, based on the <code>TreeSelectionModel</code>
1849 * implementation.
1850 *
1851 * @return an array of integers that identifies all currently selected rows
1852 * where 0 is the first row in the display
1853 */
1854 public int[] getSelectionRows() {
1855 return getSelectionModel().getSelectionRows();
1856 }
1857
1858 /**
1859 * Returns the number of nodes selected.
1860 *
1861 * @return the number of nodes selected
1862 */
1863 public int getSelectionCount() {
1864 return selectionModel.getSelectionCount();
1865 }
1866
1867 /**
1868 * Returns the smallest selected row. If the selection is empty, or
1869 * none of the selected paths are viewable, {@code -1} is returned.
1870 *
1871 * @return the smallest selected row
1872 */
1873 public int getMinSelectionRow() {
1874 return getSelectionModel().getMinSelectionRow();
1875 }
1876
1877 /**
1878 * Returns the largest selected row. If the selection is empty, or
1879 * none of the selected paths are viewable, {@code -1} is returned.
1880 *
1881 * @return the largest selected row
1882 */
1883 public int getMaxSelectionRow() {
1884 return getSelectionModel().getMaxSelectionRow();
1885 }
1886
1887 /**
1888 * Returns the row index corresponding to the lead path.
1889 *
1890 * @return an integer giving the row index of the lead path,
1891 * where 0 is the first row in the display; or -1
1892 * if <code>leadPath</code> is <code>null</code>
1893 */
1894 public int getLeadSelectionRow() {
1895 TreePath leadPath = getLeadSelectionPath();
1896
1897 if (leadPath != null) {
1898 return getRowForPath(leadPath);
1899 }
1900 return -1;
1901 }
1902
1903 /**
1904 * Returns true if the item identified by the path is currently selected.
1905 *
1906 * @param path a <code>TreePath</code> identifying a node
1907 * @return true if the node is selected
1908 */
1909 public boolean isPathSelected(TreePath path) {
1910 return getSelectionModel().isPathSelected(path);
1911 }
1912
1913 /**
1914 * Returns true if the node identified by row is selected.
1915 *
1916 * @param row an integer specifying a display row, where 0 is the first
1917 * row in the display
1918 * @return true if the node is selected
1919 */
1920 public boolean isRowSelected(int row) {
1921 return getSelectionModel().isRowSelected(row);
1922 }
1923
1924 /**
1925 * Returns an <code>Enumeration</code> of the descendants of the
1926 * path <code>parent</code> that
1927 * are currently expanded. If <code>parent</code> is not currently
1928 * expanded, this will return <code>null</code>.
1929 * If you expand/collapse nodes while
1930 * iterating over the returned <code>Enumeration</code>
1931 * this may not return all
1932 * the expanded paths, or may return paths that are no longer expanded.
1933 *
1934 * @param parent the path which is to be examined
1935 * @return an <code>Enumeration</code> of the descendents of
1936 * <code>parent</code>, or <code>null</code> if
1937 * <code>parent</code> is not currently expanded
1938 */
1939 public Enumeration<TreePath> getExpandedDescendants(TreePath parent) {
1940 if(!isExpanded(parent))
1941 return null;
1942
1943 Enumeration toggledPaths = expandedState.keys();
1944 Vector elements = null;
1945 TreePath path;
1946 Object value;
1947
1948 if(toggledPaths != null) {
1949 while(toggledPaths.hasMoreElements()) {
1950 path = (TreePath)toggledPaths.nextElement();
1951 value = expandedState.get(path);
1952 // Add the path if it is expanded, a descendant of parent,
1953 // and it is visible (all parents expanded). This is rather
1954 // expensive!
1955 if(path != parent && value != null &&
1956 ((Boolean)value).booleanValue() &&
1957 parent.isDescendant(path) && isVisible(path)) {
1958 if (elements == null) {
1959 elements = new Vector();
1960 }
1961 elements.addElement(path);
1962 }
1963 }
1964 }
1965 if (elements == null) {
1966 Set<TreePath> empty = Collections.emptySet();
1967 return Collections.enumeration(empty);
1968 }
1969 return elements.elements();
1970 }
1971
1972 /**
1973 * Returns true if the node identified by the path has ever been
1974 * expanded.
1975 * @return true if the <code>path</code> has ever been expanded
1976 */
1977 public boolean hasBeenExpanded(TreePath path) {
1978 return (path != null && expandedState.get(path) != null);
1979 }
1980
1981 /**
1982 * Returns true if the node identified by the path is currently expanded,
1983 *
1984 * @param path the <code>TreePath</code> specifying the node to check
1985 * @return false if any of the nodes in the node's path are collapsed,
1986 * true if all nodes in the path are expanded
1987 */
1988 public boolean isExpanded(TreePath path) {
1989 if(path == null)
1990 return false;
1991
1992 // Is this node expanded?
1993 Object value = expandedState.get(path);
1994
1995 if(value == null || !((Boolean)value).booleanValue())
1996 return false;
1997
1998 // It is, make sure its parent is also expanded.
1999 TreePath parentPath = path.getParentPath();
2000
2001 if(parentPath != null)
2002 return isExpanded(parentPath);
2003 return true;
2004 }
2005
2006 /**
2007 * Returns true if the node at the specified display row is currently
2008 * expanded.
2009 *
2010 * @param row the row to check, where 0 is the first row in the
2011 * display
2012 * @return true if the node is currently expanded, otherwise false
2013 */
2014 public boolean isExpanded(int row) {
2015 TreeUI tree = getUI();
2016
2017 if(tree != null) {
2018 TreePath path = tree.getPathForRow(this, row);
2019
2020 if(path != null) {
2021 Boolean value = (Boolean)expandedState.get(path);
2022
2023 return (value != null && value.booleanValue());
2024 }
2025 }
2026 return false;
2027 }
2028
2029 /**
2030 * Returns true if the value identified by path is currently collapsed,
2031 * this will return false if any of the values in path are currently
2032 * not being displayed.
2033 *
2034 * @param path the <code>TreePath</code> to check
2035 * @return true if any of the nodes in the node's path are collapsed,
2036 * false if all nodes in the path are expanded
2037 */
2038 public boolean isCollapsed(TreePath path) {
2039 return !isExpanded(path);
2040 }
2041
2042 /**
2043 * Returns true if the node at the specified display row is collapsed.
2044 *
2045 * @param row the row to check, where 0 is the first row in the
2046 * display
2047 * @return true if the node is currently collapsed, otherwise false
2048 */
2049 public boolean isCollapsed(int row) {
2050 return !isExpanded(row);
2051 }
2052
2053 /**
2054 * Ensures that the node identified by path is currently viewable.
2055 *
2056 * @param path the <code>TreePath</code> to make visible
2057 */
2058 public void makeVisible(TreePath path) {
2059 if(path != null) {
2060 TreePath parentPath = path.getParentPath();
2061
2062 if(parentPath != null) {
2063 expandPath(parentPath);
2064 }
2065 }
2066 }
2067
2068 /**
2069 * Returns true if the value identified by path is currently viewable,
2070 * which means it is either the root or all of its parents are expanded.
2071 * Otherwise, this method returns false.
2072 *
2073 * @return true if the node is viewable, otherwise false
2074 */
2075 public boolean isVisible(TreePath path) {
2076 if(path != null) {
2077 TreePath parentPath = path.getParentPath();
2078
2079 if(parentPath != null)
2080 return isExpanded(parentPath);
2081 // Root.
2082 return true;
2083 }
2084 return false;
2085 }
2086
2087 /**
2088 * Returns the <code>Rectangle</code> that the specified node will be drawn
2089 * into. Returns <code>null</code> if any component in the path is hidden
2090 * (under a collapsed parent).
2091 * <p>
2092 * Note:<br>
2093 * This method returns a valid rectangle, even if the specified
2094 * node is not currently displayed.
2095 *
2096 * @param path the <code>TreePath</code> identifying the node
2097 * @return the <code>Rectangle</code> the node is drawn in,
2098 * or <code>null</code>
2099 */
2100 public Rectangle getPathBounds(TreePath path) {
2101 TreeUI tree = getUI();
2102
2103 if(tree != null)
2104 return tree.getPathBounds(this, path);
2105 return null;
2106 }
2107
2108 /**
2109 * Returns the <code>Rectangle</code> that the node at the specified row is
2110 * drawn in.
2111 *
2112 * @param row the row to be drawn, where 0 is the first row in the
2113 * display
2114 * @return the <code>Rectangle</code> the node is drawn in
2115 */
2116 public Rectangle getRowBounds(int row) {
2117 return getPathBounds(getPathForRow(row));
2118 }
2119
2120 /**
2121 * Makes sure all the path components in path are expanded (except
2122 * for the last path component) and scrolls so that the
2123 * node identified by the path is displayed. Only works when this
2124 * <code>JTree</code> is contained in a <code>JScrollPane</code>.
2125 *
2126 * @param path the <code>TreePath</code> identifying the node to
2127 * bring into view
2128 */
2129 public void scrollPathToVisible(TreePath path) {
2130 if(path != null) {
2131 makeVisible(path);
2132
2133 Rectangle bounds = getPathBounds(path);
2134
2135 if(bounds != null) {
2136 scrollRectToVisible(bounds);
2137 if (accessibleContext != null) {
2138 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
2139 }
2140 }
2141 }
2142 }
2143
2144 /**
2145 * Scrolls the item identified by row until it is displayed. The minimum
2146 * of amount of scrolling necessary to bring the row into view
2147 * is performed. Only works when this <code>JTree</code> is contained in a
2148 * <code>JScrollPane</code>.
2149 *
2150 * @param row an integer specifying the row to scroll, where 0 is the
2151 * first row in the display
2152 */
2153 public void scrollRowToVisible(int row) {
2154 scrollPathToVisible(getPathForRow(row));
2155 }
2156
2157 /**
2158 * Returns the path for the specified row. If <code>row</code> is
2159 * not visible, or a {@code TreeUI} has not been set, <code>null</code>
2160 * is returned.
2161 *
2162 * @param row an integer specifying a row
2163 * @return the <code>TreePath</code> to the specified node,
2164 * <code>null</code> if <code>row < 0</code>
2165 * or <code>row >= getRowCount()</code>
2166 */
2167 public TreePath getPathForRow(int row) {
2168 TreeUI tree = getUI();
2169
2170 if(tree != null)
2171 return tree.getPathForRow(this, row);
2172 return null;
2173 }
2174
2175 /**
2176 * Returns the row that displays the node identified by the specified
2177 * path.
2178 *
2179 * @param path the <code>TreePath</code> identifying a node
2180 * @return an integer specifying the display row, where 0 is the first
2181 * row in the display, or -1 if any of the elements in path
2182 * are hidden under a collapsed parent.
2183 */
2184 public int getRowForPath(TreePath path) {
2185 TreeUI tree = getUI();
2186
2187 if(tree != null)
2188 return tree.getRowForPath(this, path);
2189 return -1;
2190 }
2191
2192 /**
2193 * Ensures that the node identified by the specified path is
2194 * expanded and viewable. If the last item in the path is a
2195 * leaf, this will have no effect.
2196 *
2197 * @param path the <code>TreePath</code> identifying a node
2198 */
2199 public void expandPath(TreePath path) {
2200 // Only expand if not leaf!
2201 TreeModel model = getModel();
2202
2203 if(path != null && model != null &&
2204 !model.isLeaf(path.getLastPathComponent())) {
2205 setExpandedState(path, true);
2206 }
2207 }
2208
2209 /**
2210 * Ensures that the node in the specified row is expanded and
2211 * viewable.
2212 * <p>
2213 * If <code>row</code> is < 0 or >= <code>getRowCount</code> this
2214 * will have no effect.
2215 *
2216 * @param row an integer specifying a display row, where 0 is the
2217 * first row in the display
2218 */
2219 public void expandRow(int row) {
2220 expandPath(getPathForRow(row));
2221 }
2222
2223 /**
2224 * Ensures that the node identified by the specified path is
2225 * collapsed and viewable.
2226 *
2227 * @param path the <code>TreePath</code> identifying a node
2228 */
2229 public void collapsePath(TreePath path) {
2230 setExpandedState(path, false);
2231 }
2232
2233 /**
2234 * Ensures that the node in the specified row is collapsed.
2235 * <p>
2236 * If <code>row</code> is < 0 or >= <code>getRowCount</code> this
2237 * will have no effect.
2238 *
2239 * @param row an integer specifying a display row, where 0 is the
2240 * first row in the display
2241 */
2242 public void collapseRow(int row) {
2243 collapsePath(getPathForRow(row));
2244 }
2245
2246 /**
2247 * Returns the path for the node at the specified location.
2248 *
2249 * @param x an integer giving the number of pixels horizontally from
2250 * the left edge of the display area, minus any left margin
2251 * @param y an integer giving the number of pixels vertically from
2252 * the top of the display area, minus any top margin
2253 * @return the <code>TreePath</code> for the node at that location
2254 */
2255 public TreePath getPathForLocation(int x, int y) {
2256 TreePath closestPath = getClosestPathForLocation(x, y);
2257
2258 if(closestPath != null) {
2259 Rectangle pathBounds = getPathBounds(closestPath);
2260
2261 if(pathBounds != null &&
2262 x >= pathBounds.x && x < (pathBounds.x + pathBounds.width) &&
2263 y >= pathBounds.y && y < (pathBounds.y + pathBounds.height))
2264 return closestPath;
2265 }
2266 return null;
2267 }
2268
2269 /**
2270 * Returns the row for the specified location.
2271 *
2272 * @param x an integer giving the number of pixels horizontally from
2273 * the left edge of the display area, minus any left margin
2274 * @param y an integer giving the number of pixels vertically from
2275 * the top of the display area, minus any top margin
2276 * @return the row corresponding to the location, or -1 if the
2277 * location is not within the bounds of a displayed cell
2278 * @see #getClosestRowForLocation
2279 */
2280 public int getRowForLocation(int x, int y) {
2281 return getRowForPath(getPathForLocation(x, y));
2282 }
2283
2284 /**
2285 * Returns the path to the node that is closest to x,y. If
2286 * no nodes are currently viewable, or there is no model, returns
2287 * <code>null</code>, otherwise it always returns a valid path. To test if
2288 * the node is exactly at x, y, get the node's bounds and
2289 * test x, y against that.
2290 *
2291 * @param x an integer giving the number of pixels horizontally from
2292 * the left edge of the display area, minus any left margin
2293 * @param y an integer giving the number of pixels vertically from
2294 * the top of the display area, minus any top margin
2295 * @return the <code>TreePath</code> for the node closest to that location,
2296 * <code>null</code> if nothing is viewable or there is no model
2297 *
2298 * @see #getPathForLocation
2299 * @see #getPathBounds
2300 */
2301 public TreePath getClosestPathForLocation(int x, int y) {
2302 TreeUI tree = getUI();
2303
2304 if(tree != null)
2305 return tree.getClosestPathForLocation(this, x, y);
2306 return null;
2307 }
2308
2309 /**
2310 * Returns the row to the node that is closest to x,y. If no nodes
2311 * are viewable or there is no model, returns -1. Otherwise,
2312 * it always returns a valid row. To test if the returned object is
2313 * exactly at x, y, get the bounds for the node at the returned
2314 * row and test x, y against that.
2315 *
2316 * @param x an integer giving the number of pixels horizontally from
2317 * the left edge of the display area, minus any left margin
2318 * @param y an integer giving the number of pixels vertically from
2319 * the top of the display area, minus any top margin
2320 * @return the row closest to the location, -1 if nothing is
2321 * viewable or there is no model
2322 *
2323 * @see #getRowForLocation
2324 * @see #getRowBounds
2325 */
2326 public int getClosestRowForLocation(int x, int y) {
2327 return getRowForPath(getClosestPathForLocation(x, y));
2328 }
2329
2330 /**
2331 * Returns true if the tree is being edited. The item that is being
2332 * edited can be obtained using <code>getSelectionPath</code>.
2333 *
2334 * @return true if the user is currently editing a node
2335 * @see #getSelectionPath
2336 */
2337 public boolean isEditing() {
2338 TreeUI tree = getUI();
2339
2340 if(tree != null)
2341 return tree.isEditing(this);
2342 return false;
2343 }
2344
2345 /**
2346 * Ends the current editing session.
2347 * (The <code>DefaultTreeCellEditor</code>
2348 * object saves any edits that are currently in progress on a cell.
2349 * Other implementations may operate differently.)
2350 * Has no effect if the tree isn't being edited.
2351 * <blockquote>
2352 * <b>Note:</b><br>
2353 * To make edit-saves automatic whenever the user changes
2354 * their position in the tree, use {@link #setInvokesStopCellEditing}.
2355 * </blockquote>
2356 *
2357 * @return true if editing was in progress and is now stopped,
2358 * false if editing was not in progress
2359 */
2360 public boolean stopEditing() {
2361 TreeUI tree = getUI();
2362
2363 if(tree != null)
2364 return tree.stopEditing(this);
2365 return false;
2366 }
2367
2368 /**
2369 * Cancels the current editing session. Has no effect if the
2370 * tree isn't being edited.
2371 */
2372 public void cancelEditing() {
2373 TreeUI tree = getUI();
2374
2375 if(tree != null)
2376 tree.cancelEditing(this);
2377 }
2378
2379 /**
2380 * Selects the node identified by the specified path and initiates
2381 * editing. The edit-attempt fails if the <code>CellEditor</code>
2382 * does not allow
2383 * editing for the specified item.
2384 *
2385 * @param path the <code>TreePath</code> identifying a node
2386 */
2387 public void startEditingAtPath(TreePath path) {
2388 TreeUI tree = getUI();
2389
2390 if(tree != null)
2391 tree.startEditingAtPath(this, path);
2392 }
2393
2394 /**
2395 * Returns the path to the element that is currently being edited.
2396 *
2397 * @return the <code>TreePath</code> for the node being edited
2398 */
2399 public TreePath getEditingPath() {
2400 TreeUI tree = getUI();
2401
2402 if(tree != null)
2403 return tree.getEditingPath(this);
2404 return null;
2405 }
2406
2407 //
2408 // Following are primarily convenience methods for mapping from
2409 // row based selections to path selections. Sometimes it is
2410 // easier to deal with these than paths (mouse downs, key downs
2411 // usually just deal with index based selections).
2412 // Since row based selections require a UI many of these won't work
2413 // without one.
2414 //
2415
2416 /**
2417 * Sets the tree's selection model. When a <code>null</code> value is
2418 * specified an empty
2419 * <code>selectionModel</code> is used, which does not allow selections.
2420 * <p>
2421 * This is a bound property.
2422 *
2423 * @param selectionModel the <code>TreeSelectionModel</code> to use,
2424 * or <code>null</code> to disable selections
2425 * @see TreeSelectionModel
2426 * @beaninfo
2427 * bound: true
2428 * description: The tree's selection model.
2429 */
2430 public void setSelectionModel(TreeSelectionModel selectionModel) {
2431 if(selectionModel == null)
2432 selectionModel = EmptySelectionModel.sharedInstance();
2433
2434 TreeSelectionModel oldValue = this.selectionModel;
2435
2436 if (this.selectionModel != null && selectionRedirector != null) {
2437 &n