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"></a>
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://docs.oracle.com/javase/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 * The next group of <code>JTree</code> methods use "visible" to mean
73 * "viewable" (under an expanded parent):
74 * <ul>
75 * <li><code>isVisible()</code>
76 * <li><code>makeVisible()</code>
77 * </ul>
78 * If you are interested in knowing when the selection changes implement
79 * the <code>TreeSelectionListener</code> interface and add the instance
80 * using the method <code>addTreeSelectionListener</code>.
81 * <code>valueChanged</code> will be invoked when the
82 * selection changes, that is if the user clicks twice on the same
83 * node <code>valueChanged</code> will only be invoked once.
84 * <p>
85 * If you are interested in detecting either double-click events or when
86 * a user clicks on a node, regardless of whether or not it was selected,
87 * we recommend you do the following:
88 * </p>
89 * <pre>
90 * final JTree tree = ...;
91 *
92 * MouseListener ml = new MouseAdapter() {
93 * public void <b>mousePressed</b>(MouseEvent e) {
94 * int selRow = tree.getRowForLocation(e.getX(), e.getY());
95 * TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
96 * if(selRow != -1) {
97 * if(e.getClickCount() == 1) {
98 * mySingleClick(selRow, selPath);
99 * }
100 * else if(e.getClickCount() == 2) {
101 * myDoubleClick(selRow, selPath);
102 * }
103 * }
104 * }
105 * };
106 * tree.addMouseListener(ml);
107 * </pre>
108 * NOTE: This example obtains both the path and row, but you only need to
109 * get the one you're interested in.
110 * <p>
111 * To use <code>JTree</code> to display compound nodes
112 * (for example, nodes containing both
113 * a graphic icon and text), subclass {@link TreeCellRenderer} and use
114 * {@link #setCellRenderer} to tell the tree to use it. To edit such nodes,
115 * subclass {@link TreeCellEditor} and use {@link #setCellEditor}.
116 * </p>
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™
134 * has been added to the <code>java.beans</code> package.
135 * Please see {@link java.beans.XMLEncoder}.
136 *</p>
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 * @since 1.2
145 */
146 @SuppressWarnings("serial")
147 public class JTree extends JComponent implements Scrollable, Accessible
148 {
149 /**
150 * @see #getUIClassID
151 * @see #readObject
152 */
153 private static final String uiClassID = "TreeUI";
154
155 /**
156 * The model that defines the tree displayed by this object.
157 */
158 protected transient TreeModel treeModel;
159
160 /**
161 * Models the set of selected nodes in this tree.
162 */
163 protected transient TreeSelectionModel selectionModel;
164
165 /**
166 * True if the root node is displayed, false if its children are
167 * the highest visible nodes.
168 */
169 protected boolean rootVisible;
170
171 /**
172 * The cell used to draw nodes. If <code>null</code>, the UI uses a default
173 * <code>cellRenderer</code>.
174 */
175 protected transient TreeCellRenderer cellRenderer;
176
177 /**
178 * Height to use for each display row. If this is <= 0 the renderer
179 * determines the height for each row.
180 */
181 protected int rowHeight;
182 private boolean rowHeightSet = false;
183
184 /**
185 * Maps from <code>TreePath</code> to <code>Boolean</code>
186 * indicating whether or not the
187 * particular path is expanded. This ONLY indicates whether a
188 * given path is expanded, and NOT if it is visible or not. That
189 * information must be determined by visiting all the parent
190 * paths and seeing if they are visible.
191 */
192 private transient Hashtable<TreePath, Boolean> expandedState;
193
194
195 /**
196 * True if handles are displayed at the topmost level of the tree.
197 * <p>
198 * A handle is a small icon that displays adjacent to the node which
199 * allows the user to click once to expand or collapse the node. A
200 * common interface shows a plus sign (+) for a node which can be
201 * expanded and a minus sign (-) for a node which can be collapsed.
202 * Handles are always shown for nodes below the topmost level.
203 * <p>
204 * If the <code>rootVisible</code> setting specifies that the root
205 * node is to be displayed, then that is the only node at the topmost
206 * level. If the root node is not displayed, then all of its
207 * children are at the topmost level of the tree. Handles are
208 * always displayed for nodes other than the topmost.
209 * <p>
210 * If the root node isn't visible, it is generally a good to make
211 * this value true. Otherwise, the tree looks exactly like a list,
212 * and users may not know that the "list entries" are actually
213 * tree nodes.
214 *
215 * @see #rootVisible
216 */
217 protected boolean showsRootHandles;
218 private boolean showsRootHandlesSet = false;
219
220 /**
221 * Creates a new event and passed it off the
222 * <code>selectionListeners</code>.
223 */
224 protected transient TreeSelectionRedirector selectionRedirector;
225
226 /**
227 * Editor for the entries. Default is <code>null</code>
228 * (tree is not editable).
229 */
230 protected transient TreeCellEditor cellEditor;
231
232 /**
233 * Is the tree editable? Default is false.
234 */
235 protected boolean editable;
236
237 /**
238 * Is this tree a large model? This is a code-optimization setting.
239 * A large model can be used when the cell height is the same for all
240 * nodes. The UI will then cache very little information and instead
241 * continually message the model. Without a large model the UI caches
242 * most of the information, resulting in fewer method calls to the model.
243 * <p>
244 * This value is only a suggestion to the UI. Not all UIs will
245 * take advantage of it. Default value is false.
246 */
247 protected boolean largeModel;
248
249 /**
250 * Number of rows to make visible at one time. This value is used for
251 * the <code>Scrollable</code> interface. It determines the preferred
252 * size of the display area.
253 */
254 protected int visibleRowCount;
255
256 /**
257 * If true, when editing is to be stopped by way of selection changing,
258 * data in tree changing or other means <code>stopCellEditing</code>
259 * is invoked, and changes are saved. If false,
260 * <code>cancelCellEditing</code> is invoked, and changes
261 * are discarded. Default is false.
262 */
263 protected boolean invokesStopCellEditing;
264
265 /**
266 * If true, when a node is expanded, as many of the descendants are
267 * scrolled to be visible.
268 */
269 protected boolean scrollsOnExpand;
270 private boolean scrollsOnExpandSet = false;
271
272 /**
273 * Number of mouse clicks before a node is expanded.
274 */
275 protected int toggleClickCount;
276
277 /**
278 * Updates the <code>expandedState</code>.
279 */
280 protected transient TreeModelListener treeModelListener;
281
282 /**
283 * Used when <code>setExpandedState</code> is invoked,
284 * will be a <code>Stack</code> of <code>Stack</code>s.
285 */
286 private transient Stack<Stack<TreePath>> expandedStack;
287
288 /**
289 * Lead selection path, may not be <code>null</code>.
290 */
291 private TreePath leadPath;
292
293 /**
294 * Anchor path.
295 */
296 private TreePath anchorPath;
297
298 /**
299 * True if paths in the selection should be expanded.
300 */
301 private boolean expandsSelectedPaths;
302
303 /**
304 * This is set to true for the life of the <code>setUI</code> call.
305 */
306 private boolean settingUI;
307
308 /** If true, mouse presses on selections initiate a drag operation. */
309 private boolean dragEnabled;
310
311 /**
312 * The drop mode for this component.
313 */
314 private DropMode dropMode = DropMode.USE_SELECTION;
315
316 /**
317 * The drop location.
318 */
319 private transient DropLocation dropLocation;
320
321 /**
322 * A subclass of <code>TransferHandler.DropLocation</code> representing
323 * a drop location for a <code>JTree</code>.
324 *
325 * @see #getDropLocation
326 * @since 1.6
327 */
328 public static final class DropLocation extends TransferHandler.DropLocation {
329 private final TreePath path;
330 private final int index;
331
332 private DropLocation(Point p, TreePath path, int index) {
333 super(p);
334 this.path = path;
335 this.index = index;
336 }
337
338 /**
339 * Returns the index where the dropped data should be inserted
340 * with respect to the path returned by <code>getPath()</code>.
341 * <p>
342 * For drop modes <code>DropMode.USE_SELECTION</code> and
343 * <code>DropMode.ON</code>, this index is unimportant (and it will
344 * always be <code>-1</code>) as the only interesting data is the
345 * path over which the drop operation occurred.
346 * <p>
347 * For drop mode <code>DropMode.INSERT</code>, this index
348 * indicates the index at which the data should be inserted into
349 * the parent path represented by <code>getPath()</code>.
350 * <code>-1</code> indicates that the drop occurred over the
351 * parent itself, and in most cases should be treated as inserting
352 * into either the beginning or the end of the parent's list of
353 * children.
354 * <p>
355 * For <code>DropMode.ON_OR_INSERT</code>, this value will be
356 * an insert index, as described above, or <code>-1</code> if
357 * the drop occurred over the path itself.
358 *
359 * @return the child index
360 * @see #getPath
361 */
362 public int getChildIndex() {
363 return index;
364 }
365
366 /**
367 * Returns the path where dropped data should be placed in the
368 * tree.
369 * <p>
370 * Interpretation of this value depends on the drop mode set on the
371 * component. If the drop mode is <code>DropMode.USE_SELECTION</code>
372 * or <code>DropMode.ON</code>, the return value is the path in the
373 * tree over which the data has been (or will be) dropped.
374 * <code>null</code> indicates that the drop is over empty space,
375 * not associated with a particular path.
376 * <p>
377 * If the drop mode is <code>DropMode.INSERT</code>, the return value
378 * refers to the path that should become the parent of the new data,
379 * in which case <code>getChildIndex()</code> indicates where the
380 * new item should be inserted into this parent path. A
381 * <code>null</code> path indicates that no parent path has been
382 * determined, which can happen for multiple reasons:
383 * <ul>
384 * <li>The tree has no model
385 * <li>There is no root in the tree
386 * <li>The root is collapsed
387 * <li>The root is a leaf node
388 * </ul>
389 * It is up to the developer to decide if and how they wish to handle
390 * the <code>null</code> case.
391 * <p>
392 * If the drop mode is <code>DropMode.ON_OR_INSERT</code>,
393 * <code>getChildIndex</code> can be used to determine whether the
394 * drop is on top of the path itself (<code>-1</code>) or the index
395 * at which it should be inserted into the path (values other than
396 * <code>-1</code>).
397 *
398 * @return the drop path
399 * @see #getChildIndex
400 */
401 public TreePath getPath() {
402 return path;
403 }
404
405 /**
406 * Returns a string representation of this drop location.
407 * This method is intended to be used for debugging purposes,
408 * and the content and format of the returned string may vary
409 * between implementations.
410 *
411 * @return a string representation of this drop location
412 */
413 public String toString() {
414 return getClass().getName()
415 + "[dropPoint=" + getDropPoint() + ","
416 + "path=" + path + ","
424 private int expandRow = -1;
425
426 @SuppressWarnings("serial")
427 private class TreeTimer extends Timer {
428 public TreeTimer() {
429 super(2000, null);
430 setRepeats(false);
431 }
432
433 public void fireActionPerformed(ActionEvent ae) {
434 JTree.this.expandRow(expandRow);
435 }
436 }
437
438 /**
439 * A timer to expand nodes during drop.
440 */
441 private TreeTimer dropTimer;
442
443 /**
444 * When <code>addTreeExpansionListener</code> is invoked,
445 * and <code>settingUI</code> is true, this ivar gets set to the passed in
446 * <code>Listener</code>. This listener is then notified first in
447 * <code>fireTreeCollapsed</code> and <code>fireTreeExpanded</code>.
448 * <p>This is an ugly workaround for a way to have the UI listener
449 * get notified before other listeners.
450 */
451 private transient TreeExpansionListener uiTreeExpansionListener;
452
453 /**
454 * Max number of stacks to keep around.
455 */
456 private static int TEMP_STACK_SIZE = 11;
457
458 //
459 // Bound property names
460 //
461 /** Bound property name for <code>cellRenderer</code>. */
462 public static final String CELL_RENDERER_PROPERTY = "cellRenderer";
463 /** Bound property name for <code>treeModel</code>. */
464 public static final String TREE_MODEL_PROPERTY = "model";
465 /** Bound property name for <code>rootVisible</code>. */
466 public static final String ROOT_VISIBLE_PROPERTY = "rootVisible";
467 /** Bound property name for <code>showsRootHandles</code>. */
468 public static final String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
469 /** Bound property name for <code>rowHeight</code>. */
470 public static final String ROW_HEIGHT_PROPERTY = "rowHeight";
471 /** Bound property name for <code>cellEditor</code>. */
472 public static final String CELL_EDITOR_PROPERTY = "cellEditor";
473 /** Bound property name for <code>editable</code>. */
474 public static final String EDITABLE_PROPERTY = "editable";
475 /** Bound property name for <code>largeModel</code>. */
476 public static final String LARGE_MODEL_PROPERTY = "largeModel";
477 /** Bound property name for selectionModel. */
478 public static final String SELECTION_MODEL_PROPERTY = "selectionModel";
479 /** Bound property name for <code>visibleRowCount</code>. */
480 public static final String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
481 /** Bound property name for <code>messagesStopCellEditing</code>. */
482 public static final String INVOKES_STOP_CELL_EDITING_PROPERTY = "invokesStopCellEditing";
483 /** Bound property name for <code>scrollsOnExpand</code>. */
484 public static final String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
485 /** Bound property name for <code>toggleClickCount</code>. */
486 public static final String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
487 /** Bound property name for <code>leadSelectionPath</code>.
488 * @since 1.3 */
489 public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
490 /** Bound property name for anchor selection path.
491 * @since 1.3 */
492 public static final String ANCHOR_SELECTION_PATH_PROPERTY = "anchorSelectionPath";
493 /** Bound property name for expands selected paths property
494 * @since 1.3 */
495 public static final String EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths";
496
497
498 /**
499 * Creates and returns a sample <code>TreeModel</code>.
500 * Used primarily for beanbuilders to show something interesting.
501 *
502 * @return the default <code>TreeModel</code>
503 */
504 protected static TreeModel getDefaultTreeModel() {
505 DefaultMutableTreeNode root = new DefaultMutableTreeNode("JTree");
506 DefaultMutableTreeNode parent;
507
508 parent = new DefaultMutableTreeNode("colors");
509 root.add(parent);
510 parent.add(new DefaultMutableTreeNode("blue"));
511 parent.add(new DefaultMutableTreeNode("violet"));
512 parent.add(new DefaultMutableTreeNode("red"));
513 parent.add(new DefaultMutableTreeNode("yellow"));
514
515 parent = new DefaultMutableTreeNode("sports");
516 root.add(parent);
517 parent.add(new DefaultMutableTreeNode("basketball"));
518 parent.add(new DefaultMutableTreeNode("soccer"));
519 parent.add(new DefaultMutableTreeNode("football"));
520 parent.add(new DefaultMutableTreeNode("hockey"));
521
522 parent = new DefaultMutableTreeNode("food");
523 root.add(parent);
524 parent.add(new DefaultMutableTreeNode("hot dogs"));
525 parent.add(new DefaultMutableTreeNode("pizza"));
526 parent.add(new DefaultMutableTreeNode("ravioli"));
527 parent.add(new DefaultMutableTreeNode("bananas"));
528 return new DefaultTreeModel(root);
529 }
530
531 /**
532 * Returns a <code>TreeModel</code> wrapping the specified object.
533 * If the object is:<ul>
534 * <li>an array of <code>Object</code>s,
535 * <li>a <code>Hashtable</code>, or
536 * <li>a <code>Vector</code>
537 * </ul>then a new root node is created with each of the incoming
538 * objects as children. Otherwise, a new root is created with
539 * a value of {@code "root"}.
540 *
541 * @param value the <code>Object</code> used as the foundation for
542 * the <code>TreeModel</code>
543 * @return a <code>TreeModel</code> wrapping the specified object
544 */
545 protected static TreeModel createTreeModel(Object value) {
546 DefaultMutableTreeNode root;
547
548 if((value instanceof Object[]) || (value instanceof Hashtable) ||
549 (value instanceof Vector)) {
550 root = new DefaultMutableTreeNode("root");
551 DynamicUtilTreeNode.createChildren(root, value);
552 }
553 else {
554 root = new DynamicUtilTreeNode("root", value);
555 }
556 return new DefaultTreeModel(root, false);
557 }
558
559 /**
560 * Returns a <code>JTree</code> with a sample model.
561 * The default model used by the tree defines a leaf node as any node
562 * without children.
563 *
564 * @see DefaultTreeModel#asksAllowsChildren
565 */
566 public JTree() {
567 this(getDefaultTreeModel());
568 }
569
570 /**
571 * Returns a <code>JTree</code> with each element of the
572 * specified array as the
573 * child of a new root node which is not displayed.
574 * By default, the tree defines a leaf node as any node without
575 * children.
576 *
577 * @param value an array of <code>Object</code>s
578 * @see DefaultTreeModel#asksAllowsChildren
579 */
580 public JTree(Object[] value) {
581 this(createTreeModel(value));
582 this.setRootVisible(false);
583 this.setShowsRootHandles(true);
584 expandRoot();
585 }
586
587 /**
588 * Returns a <code>JTree</code> with each element of the specified
589 * <code>Vector</code> as the
590 * child of a new root node which is not displayed. By default, the
591 * tree defines a leaf node as any node without children.
592 *
593 * @param value a <code>Vector</code>
594 * @see DefaultTreeModel#asksAllowsChildren
595 */
596 public JTree(Vector<?> value) {
597 this(createTreeModel(value));
598 this.setRootVisible(false);
599 this.setShowsRootHandles(true);
600 expandRoot();
601 }
602
603 /**
604 * Returns a <code>JTree</code> created from a <code>Hashtable</code>
605 * which does not display with root.
606 * Each value-half of the key/value pairs in the <code>HashTable</code>
607 * becomes a child of the new root node. By default, the tree defines
608 * a leaf node as any node without children.
609 *
610 * @param value a <code>Hashtable</code>
611 * @see DefaultTreeModel#asksAllowsChildren
612 */
613 public JTree(Hashtable<?,?> value) {
614 this(createTreeModel(value));
615 this.setRootVisible(false);
616 this.setShowsRootHandles(true);
617 expandRoot();
618 }
619
620 /**
621 * Returns a <code>JTree</code> with the specified
622 * <code>TreeNode</code> as its root,
623 * which displays the root node.
624 * By default, the tree defines a leaf node as any node without children.
625 *
626 * @param root a <code>TreeNode</code> object
627 * @see DefaultTreeModel#asksAllowsChildren
628 */
629 public JTree(TreeNode root) {
630 this(root, false);
631 }
632
633 /**
634 * Returns a <code>JTree</code> with the specified <code>TreeNode</code>
635 * as its root, which
636 * displays the root node and which decides whether a node is a
637 * leaf node in the specified manner.
638 *
639 * @param root a <code>TreeNode</code> object
640 * @param asksAllowsChildren if false, any node without children is a
641 * leaf node; if true, only nodes that do not allow
642 * children are leaf nodes
643 * @see DefaultTreeModel#asksAllowsChildren
644 */
645 public JTree(TreeNode root, boolean asksAllowsChildren) {
646 this(new DefaultTreeModel(root, asksAllowsChildren));
647 }
648
649 /**
650 * Returns an instance of <code>JTree</code> which displays the root node
651 * -- the tree is created using the specified data model.
652 *
653 * @param newModel the <code>TreeModel</code> to use as the data model
654 */
655 @ConstructorProperties({"model"})
656 public JTree(TreeModel newModel) {
657 super();
658 expandedStack = new Stack<Stack<TreePath>>();
659 toggleClickCount = 2;
660 expandedState = new Hashtable<TreePath, Boolean>();
661 setLayout(null);
662 rowHeight = 16;
663 visibleRowCount = 20;
664 rootVisible = true;
665 selectionModel = new DefaultTreeSelectionModel();
666 cellRenderer = null;
667 scrollsOnExpand = true;
668 setOpaque(true);
669 expandsSelectedPaths = true;
670 updateUI();
671 setModel(newModel);
672 }
673
674 /**
675 * Returns the L&F object that renders this component.
676 *
677 * @return the <code>TreeUI</code> object that renders this component
678 */
679 public TreeUI getUI() {
680 return (TreeUI)ui;
681 }
682
683 /**
684 * Sets the L&F object that renders this component.
685 * <p>
686 * This is a bound property.
687 *
688 * @param ui the <code>TreeUI</code> L&F object
689 * @see UIDefaults#getUI
690 * @beaninfo
691 * bound: true
692 * hidden: true
693 * attribute: visualUpdate true
694 * description: The UI object that implements the Component's LookAndFeel.
695 */
696 public void setUI(TreeUI ui) {
697 if (this.ui != ui) {
698 settingUI = true;
699 uiTreeExpansionListener = null;
700 try {
701 super.setUI(ui);
702 }
703 finally {
704 settingUI = false;
705 }
706 }
707 }
708
709 /**
710 * Notification from the <code>UIManager</code> that the L&F has changed.
711 * Replaces the current UI object with the latest version from the
712 * <code>UIManager</code>.
713 *
714 * @see JComponent#updateUI
715 */
716 public void updateUI() {
717 setUI((TreeUI)UIManager.getUI(this));
718
719 SwingUtilities.updateRendererOrEditorUI(getCellRenderer());
720 SwingUtilities.updateRendererOrEditorUI(getCellEditor());
721 }
722
723
724 /**
725 * Returns the name of the L&F class that renders this component.
726 *
727 * @return the string "TreeUI"
728 * @see JComponent#getUIClassID
729 * @see UIDefaults#getUI
730 */
731 public String getUIClassID() {
732 return uiClassID;
733 }
734
735
736 /**
737 * Returns the current <code>TreeCellRenderer</code>
738 * that is rendering each cell.
739 *
740 * @return the <code>TreeCellRenderer</code> that is rendering each cell
741 */
742 public TreeCellRenderer getCellRenderer() {
743 return cellRenderer;
744 }
745
746 /**
747 * Sets the <code>TreeCellRenderer</code> that will be used to
748 * draw each cell.
749 * <p>
750 * This is a bound property.
751 *
752 * @param x the <code>TreeCellRenderer</code> that is to render each cell
753 * @beaninfo
754 * bound: true
755 * description: The TreeCellRenderer that will be used to draw
756 * each cell.
757 */
758 public void setCellRenderer(TreeCellRenderer x) {
759 TreeCellRenderer oldValue = cellRenderer;
760
761 cellRenderer = x;
762 firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, cellRenderer);
763 invalidate();
764 }
765
766 /**
767 * Determines whether the tree is editable. Fires a property
768 * change event if the new setting is different from the existing
769 * setting.
770 * <p>
771 * This is a bound property.
772 *
781 this.editable = flag;
782 firePropertyChange(EDITABLE_PROPERTY, oldValue, flag);
783 if (accessibleContext != null) {
784 accessibleContext.firePropertyChange(
785 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
786 (oldValue ? AccessibleState.EDITABLE : null),
787 (flag ? AccessibleState.EDITABLE : null));
788 }
789 }
790
791 /**
792 * Returns true if the tree is editable.
793 *
794 * @return true if the tree is editable
795 */
796 public boolean isEditable() {
797 return editable;
798 }
799
800 /**
801 * Sets the cell editor. A <code>null</code> value implies that the
802 * tree cannot be edited. If this represents a change in the
803 * <code>cellEditor</code>, the <code>propertyChange</code>
804 * method is invoked on all listeners.
805 * <p>
806 * This is a bound property.
807 *
808 * @param cellEditor the <code>TreeCellEditor</code> to use
809 * @beaninfo
810 * bound: true
811 * description: The cell editor. A null value implies the tree
812 * cannot be edited.
813 */
814 public void setCellEditor(TreeCellEditor cellEditor) {
815 TreeCellEditor oldEditor = this.cellEditor;
816
817 this.cellEditor = cellEditor;
818 firePropertyChange(CELL_EDITOR_PROPERTY, oldEditor, cellEditor);
819 invalidate();
820 }
821
822 /**
823 * Returns the editor used to edit entries in the tree.
824 *
825 * @return the <code>TreeCellEditor</code> in use,
826 * or <code>null</code> if the tree cannot be edited
827 */
828 public TreeCellEditor getCellEditor() {
829 return cellEditor;
830 }
831
832 /**
833 * Returns the <code>TreeModel</code> that is providing the data.
834 *
835 * @return the <code>TreeModel</code> that is providing the data
836 */
837 public TreeModel getModel() {
838 return treeModel;
839 }
840
841 /**
842 * Sets the <code>TreeModel</code> that will provide the data.
843 * <p>
844 * This is a bound property.
845 *
846 * @param newModel the <code>TreeModel</code> that is to provide the data
847 * @beaninfo
848 * bound: true
849 * description: The TreeModel that will provide the data.
850 */
851 public void setModel(TreeModel newModel) {
852 clearSelection();
853
854 TreeModel oldModel = treeModel;
855
856 if(treeModel != null && treeModelListener != null)
857 treeModel.removeTreeModelListener(treeModelListener);
858
859 if (accessibleContext != null) {
860 if (treeModel != null) {
861 treeModel.removeTreeModelListener((TreeModelListener)accessibleContext);
862 }
863 if (newModel != null) {
864 newModel.addTreeModelListener((TreeModelListener)accessibleContext);
865 }
866 }
880 expandedState.put(new TreePath(treeRoot),
881 Boolean.TRUE);
882 }
883 }
884 firePropertyChange(TREE_MODEL_PROPERTY, oldModel, treeModel);
885 invalidate();
886 }
887
888 /**
889 * Returns true if the root node of the tree is displayed.
890 *
891 * @return true if the root node of the tree is displayed
892 * @see #rootVisible
893 */
894 public boolean isRootVisible() {
895 return rootVisible;
896 }
897
898 /**
899 * Determines whether or not the root node from
900 * the <code>TreeModel</code> is visible.
901 * <p>
902 * This is a bound property.
903 *
904 * @param rootVisible true if the root node of the tree is to be displayed
905 * @see #rootVisible
906 * @beaninfo
907 * bound: true
908 * description: Whether or not the root node
909 * from the TreeModel is visible.
910 */
911 public void setRootVisible(boolean rootVisible) {
912 boolean oldValue = this.rootVisible;
913
914 this.rootVisible = rootVisible;
915 firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, this.rootVisible);
916 if (accessibleContext != null) {
917 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
918 }
919 }
920
921 /**
922 * Sets the value of the <code>showsRootHandles</code> property,
923 * which specifies whether the node handles should be displayed.
924 * The default value of this property depends on the constructor
925 * used to create the <code>JTree</code>.
926 * Some look and feels might not support handles;
927 * they will ignore this property.
928 * <p>
929 * This is a bound property.
930 *
931 * @param newValue <code>true</code> if root handles should be displayed;
932 * otherwise, <code>false</code>
933 * @see #showsRootHandles
934 * @see #getShowsRootHandles
935 * @beaninfo
936 * bound: true
937 * description: Whether the node handles are to be
938 * displayed.
939 */
940 public void setShowsRootHandles(boolean newValue) {
941 boolean oldValue = showsRootHandles;
942 TreeModel model = getModel();
943
944 showsRootHandles = newValue;
945 showsRootHandlesSet = true;
946 firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue,
947 showsRootHandles);
948 if (accessibleContext != null) {
949 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
950 }
951 invalidate();
952 }
953
954 /**
955 * Returns the value of the <code>showsRootHandles</code> property.
956 *
957 * @return the value of the <code>showsRootHandles</code> property
958 * @see #showsRootHandles
959 */
960 public boolean getShowsRootHandles()
961 {
962 return showsRootHandles;
963 }
964
965 /**
966 * Sets the height of each cell, in pixels. If the specified value
967 * is less than or equal to zero the current cell renderer is
968 * queried for each row's height.
969 * <p>
970 * This is a bound property.
971 *
972 * @param rowHeight the height of each cell, in pixels
973 * @beaninfo
974 * bound: true
975 * description: The height of each cell.
976 */
977 public void setRowHeight(int rowHeight)
1023 public void setLargeModel(boolean newValue) {
1024 boolean oldValue = largeModel;
1025
1026 largeModel = newValue;
1027 firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, newValue);
1028 }
1029
1030 /**
1031 * Returns true if the tree is configured for a large model.
1032 *
1033 * @return true if a large model is suggested
1034 * @see #largeModel
1035 */
1036 public boolean isLargeModel() {
1037 return largeModel;
1038 }
1039
1040 /**
1041 * Determines what happens when editing is interrupted by selecting
1042 * another node in the tree, a change in the tree's data, or by some
1043 * other means. Setting this property to <code>true</code> causes the
1044 * changes to be automatically saved when editing is interrupted.
1045 * <p>
1046 * Fires a property change for the INVOKES_STOP_CELL_EDITING_PROPERTY.
1047 *
1048 * @param newValue true means that <code>stopCellEditing</code> is invoked
1049 * when editing is interrupted, and data is saved; false means that
1050 * <code>cancelCellEditing</code> is invoked, and changes are lost
1051 * @beaninfo
1052 * bound: true
1053 * description: Determines what happens when editing is interrupted,
1054 * selecting another node in the tree, a change in the
1055 * tree's data, or some other means.
1056 */
1057 public void setInvokesStopCellEditing(boolean newValue) {
1058 boolean oldValue = invokesStopCellEditing;
1059
1060 invokesStopCellEditing = newValue;
1061 firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, oldValue,
1062 newValue);
1063 }
1064
1065 /**
1066 * Returns the indicator that tells what happens when editing is
1067 * interrupted.
1068 *
1069 * @return the indicator that tells what happens when editing is
1070 * interrupted
1071 * @see #setInvokesStopCellEditing
1072 */
1073 public boolean getInvokesStopCellEditing() {
1074 return invokesStopCellEditing;
1075 }
1076
1077 /**
1078 * Sets the <code>scrollsOnExpand</code> property,
1079 * which determines whether the
1080 * tree might scroll to show previously hidden children.
1081 * If this property is <code>true</code> (the default),
1082 * when a node expands
1083 * the tree can use scrolling to make
1084 * the maximum possible number of the node's descendants visible.
1085 * In some look and feels, trees might not need to scroll when expanded;
1086 * those look and feels will ignore this property.
1087 * <p>
1088 * This is a bound property.
1089 *
1090 * @param newValue <code>false</code> to disable scrolling on expansion;
1091 * <code>true</code> to enable it
1092 * @see #getScrollsOnExpand
1093 *
1094 * @beaninfo
1095 * bound: true
1096 * description: Indicates if a node descendant should be scrolled when expanded.
1097 */
1098 public void setScrollsOnExpand(boolean newValue) {
1099 boolean oldValue = scrollsOnExpand;
1100
1101 scrollsOnExpand = newValue;
1102 scrollsOnExpandSet = true;
1103 firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue,
1104 newValue);
1105 }
1106
1107 /**
1108 * Returns the value of the <code>scrollsOnExpand</code> property.
1109 *
1110 * @return the value of the <code>scrollsOnExpand</code> property
1111 */
1112 public boolean getScrollsOnExpand() {
1113 return scrollsOnExpand;
1114 }
1115
1116 /**
1117 * Sets the number of mouse clicks before a node will expand or close.
1118 * The default is two.
1119 * <p>
1120 * This is a bound property.
1121 *
1122 * @param clickCount the number of mouse clicks to get a node expanded or closed
1123 * @since 1.3
1124 * @beaninfo
1125 * bound: true
1126 * description: Number of clicks before a node will expand/collapse.
1127 */
1128 public void setToggleClickCount(int clickCount) {
1129 int oldCount = toggleClickCount;
1130
1131 toggleClickCount = clickCount;
1132 firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldCount,
1133 clickCount);
1134 }
1135
1136 /**
1137 * Returns the number of mouse clicks needed to expand or close a node.
1138 *
1139 * @return number of mouse clicks before node is expanded
1140 * @since 1.3
1141 */
1142 public int getToggleClickCount() {
1143 return toggleClickCount;
1144 }
1145
1146 /**
1147 * Configures the <code>expandsSelectedPaths</code> property. If
1148 * true, any time the selection is changed, either via the
1149 * <code>TreeSelectionModel</code>, or the cover methods provided by
1150 * <code>JTree</code>, the <code>TreePath</code>s parents will be
1151 * expanded to make them visible (visible meaning the parent path is
1152 * expanded, not necessarily in the visible rectangle of the
1153 * <code>JTree</code>). If false, when the selection
1154 * changes the nodes parent is not made visible (all its parents expanded).
1155 * This is useful if you wish to have your selection model maintain paths
1156 * that are not always visible (all parents expanded).
1157 * <p>
1158 * This is a bound property.
1159 *
1160 * @param newValue the new value for <code>expandsSelectedPaths</code>
1161 *
1162 * @since 1.3
1163 * @beaninfo
1164 * bound: true
1165 * description: Indicates whether changes to the selection should make
1166 * the parent of the path visible.
1167 */
1168 public void setExpandsSelectedPaths(boolean newValue) {
1169 boolean oldValue = expandsSelectedPaths;
1170
1171 expandsSelectedPaths = newValue;
1172 firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue,
1173 newValue);
1174 }
1175
1176 /**
1177 * Returns the <code>expandsSelectedPaths</code> property.
1178 * @return true if selection changes result in the parent path being
1179 * expanded
1180 * @since 1.3
1181 * @see #setExpandsSelectedPaths
1182 */
1183 public boolean getExpandsSelectedPaths() {
1184 return expandsSelectedPaths;
1185 }
1186
1187 /**
1188 * Turns on or off automatic drag handling. In order to enable automatic
1189 * drag handling, this property should be set to {@code true}, and the
1190 * tree's {@code TransferHandler} needs to be {@code non-null}.
1191 * The default value of the {@code dragEnabled} property is {@code false}.
1192 * <p>
1193 * The job of honoring this property, and recognizing a user drag gesture,
1194 * lies with the look and feel implementation, and in particular, the tree's
1195 * {@code TreeUI}. When automatic drag handling is enabled, most look and
1196 * feels (including those that subclass {@code BasicLookAndFeel}) begin a
1197 * drag and drop operation whenever the user presses the mouse button over
1198 * an item and then moves the mouse a few pixels. Setting this property to
1199 * {@code true} can therefore have a subtle effect on how selections behave.
1200 * <p>
1201 * If a look and feel is used that ignores this property, you can still
1202 * begin a drag and drop operation by calling {@code exportAsDrag} on the
1203 * tree's {@code TransferHandler}.
1204 *
1205 * @param b whether or not to enable automatic drag handling
1206 * @exception HeadlessException if
1207 * <code>b</code> is <code>true</code> and
1208 * <code>GraphicsEnvironment.isHeadless()</code>
1209 * returns <code>true</code>
1210 * @see java.awt.GraphicsEnvironment#isHeadless
1211 * @see #getDragEnabled
1212 * @see #setTransferHandler
1213 * @see TransferHandler
1214 * @since 1.4
1215 *
1216 * @beaninfo
1217 * description: determines whether automatic drag handling is enabled
1218 * bound: false
1219 */
1220 public void setDragEnabled(boolean b) {
1221 checkDragEnabled(b);
1222 dragEnabled = b;
1223 }
1224
1225 private static void checkDragEnabled(boolean b) {
1226 if (b && GraphicsEnvironment.isHeadless()) {
1227 throw new HeadlessException();
1228 }
1229 }
1230
1231 /**
1232 * Returns whether or not automatic drag handling is enabled.
1233 *
1234 * @return the value of the {@code dragEnabled} property
1235 * @see #setDragEnabled
1236 * @since 1.4
1237 */
1238 public boolean getDragEnabled() {
1239 return dragEnabled;
1240 }
1241
1242 /**
1243 * Sets the drop mode for this component. For backward compatibility,
1244 * the default for this property is <code>DropMode.USE_SELECTION</code>.
1245 * Usage of one of the other modes is recommended, however, for an
1246 * improved user experience. <code>DropMode.ON</code>, for instance,
1247 * offers similar behavior of showing items as selected, but does so without
1248 * affecting the actual selection in the tree.
1249 * <p>
1250 * <code>JTree</code> supports the following drop modes:
1251 * <ul>
1252 * <li><code>DropMode.USE_SELECTION</code></li>
1253 * <li><code>DropMode.ON</code></li>
1254 * <li><code>DropMode.INSERT</code></li>
1255 * <li><code>DropMode.ON_OR_INSERT</code></li>
1256 * </ul>
1257 * <p>
1258 * The drop mode is only meaningful if this component has a
1259 * <code>TransferHandler</code> that accepts drops.
1260 *
1261 * @param dropMode the drop mode to use
1262 * @throws IllegalArgumentException if the drop mode is unsupported
1263 * or <code>null</code>
1264 * @see #getDropMode
1265 * @see #getDropLocation
1266 * @see #setTransferHandler
1267 * @see TransferHandler
1268 * @since 1.6
1269 */
1270 public final void setDropMode(DropMode dropMode) {
1271 checkDropMode(dropMode);
1272 this.dropMode = dropMode;
1273 }
1274
1275 private static void checkDropMode(DropMode dropMode) {
1276 if (dropMode != null) {
1277 switch (dropMode) {
1278 case USE_SELECTION:
1279 case ON:
1280 case INSERT:
1281 case ON_OR_INSERT:
1282 return;
1283 }
1286 throw new IllegalArgumentException(dropMode +
1287 ": Unsupported drop mode for tree");
1288 }
1289
1290 /**
1291 * Returns the drop mode for this component.
1292 *
1293 * @return the drop mode for this component
1294 * @see #setDropMode
1295 * @since 1.6
1296 */
1297 public final DropMode getDropMode() {
1298 return dropMode;
1299 }
1300
1301 /**
1302 * Calculates a drop location in this component, representing where a
1303 * drop at the given point should insert data.
1304 *
1305 * @param p the point to calculate a drop location for
1306 * @return the drop location, or <code>null</code>
1307 */
1308 DropLocation dropLocationForPoint(Point p) {
1309 DropLocation location = null;
1310
1311 int row = getClosestRowForLocation(p.x, p.y);
1312 Rectangle bounds = getRowBounds(row);
1313 TreeModel model = getModel();
1314 Object root = (model == null) ? null : model.getRoot();
1315 TreePath rootPath = (root == null) ? null : new TreePath(root);
1316
1317 TreePath child;
1318 TreePath parent;
1319 boolean outside = row == -1
1320 || p.y < bounds.y
1321 || p.y >= bounds.y + bounds.height;
1322
1323 switch(dropMode) {
1324 case USE_SELECTION:
1325 case ON:
1326 if (outside) {
1408 }
1409
1410 /**
1411 * Called to set or clear the drop location during a DnD operation.
1412 * In some cases, the component may need to use it's internal selection
1413 * temporarily to indicate the drop location. To help facilitate this,
1414 * this method returns and accepts as a parameter a state object.
1415 * This state object can be used to store, and later restore, the selection
1416 * state. Whatever this method returns will be passed back to it in
1417 * future calls, as the state parameter. If it wants the DnD system to
1418 * continue storing the same state, it must pass it back every time.
1419 * Here's how this is used:
1420 * <p>
1421 * Let's say that on the first call to this method the component decides
1422 * to save some state (because it is about to use the selection to show
1423 * a drop index). It can return a state object to the caller encapsulating
1424 * any saved selection state. On a second call, let's say the drop location
1425 * is being changed to something else. The component doesn't need to
1426 * restore anything yet, so it simply passes back the same state object
1427 * to have the DnD system continue storing it. Finally, let's say this
1428 * method is messaged with <code>null</code>. This means DnD
1429 * is finished with this component for now, meaning it should restore
1430 * state. At this point, it can use the state parameter to restore
1431 * said state, and of course return <code>null</code> since there's
1432 * no longer anything to store.
1433 *
1434 * @param location the drop location (as calculated by
1435 * <code>dropLocationForPoint</code>) or <code>null</code>
1436 * if there's no longer a valid drop location
1437 * @param state the state object saved earlier for this component,
1438 * or <code>null</code>
1439 * @param forDrop whether or not the method is being called because an
1440 * actual drop occurred
1441 * @return any saved state for this component, or <code>null</code> if none
1442 */
1443 Object setDropLocation(TransferHandler.DropLocation location,
1444 Object state,
1445 boolean forDrop) {
1446
1447 Object retVal = null;
1448 DropLocation treeLocation = (DropLocation)location;
1449
1450 if (dropMode == DropMode.USE_SELECTION) {
1451 if (treeLocation == null) {
1452 if (!forDrop && state != null) {
1453 setSelectionPaths(((TreePath[][])state)[0]);
1454 setAnchorSelectionPath(((TreePath[][])state)[1][0]);
1455 setLeadSelectionPath(((TreePath[][])state)[1][1]);
1456 }
1457 } else {
1458 if (dropLocation == null) {
1459 TreePath[] paths = getSelectionPaths();
1460 if (paths == null) {
1461 paths = new TreePath[0];
1477
1478 return retVal;
1479 }
1480
1481 /**
1482 * Called to indicate to this component that DnD is done.
1483 * Allows for us to cancel the expand timer.
1484 */
1485 void dndDone() {
1486 cancelDropTimer();
1487 dropTimer = null;
1488 }
1489
1490 /**
1491 * Returns the location that this component should visually indicate
1492 * as the drop location during a DnD operation over the component,
1493 * or {@code null} if no location is to currently be shown.
1494 * <p>
1495 * This method is not meant for querying the drop location
1496 * from a {@code TransferHandler}, as the drop location is only
1497 * set after the {@code TransferHandler}'s <code>canImport</code>
1498 * has returned and has allowed for the location to be shown.
1499 * <p>
1500 * When this property changes, a property change event with
1501 * name "dropLocation" is fired by the component.
1502 *
1503 * @return the drop location
1504 * @see #setDropMode
1505 * @see TransferHandler#canImport(TransferHandler.TransferSupport)
1506 * @since 1.6
1507 */
1508 public final DropLocation getDropLocation() {
1509 return dropLocation;
1510 }
1511
1512 private void startDropTimer() {
1513 if (dropTimer == null) {
1514 dropTimer = new TreeTimer();
1515 }
1516 dropTimer.start();
1517 }
1518
1519 private void cancelDropTimer() {
1520 if (dropTimer != null && dropTimer.isRunning()) {
1521 expandRow = -1;
1522 dropTimer.stop();
1523 }
1524 }
1525
1526 /**
1527 * Returns <code>isEditable</code>. This is invoked from the UI before
1528 * editing begins to insure that the given path can be edited. This
1529 * is provided as an entry point for subclassers to add filtered
1530 * editing without having to resort to creating a new editor.
1531 *
1532 * @param path a {@code TreePath} identifying a node
1533 * @return true if every parent node and the node itself is editable
1534 * @see #isEditable
1535 */
1536 public boolean isPathEditable(TreePath path) {
1537 return isEditable();
1538 }
1539
1540 /**
1541 * Overrides <code>JComponent</code>'s <code>getToolTipText</code>
1542 * method in order to allow
1543 * renderer's tips to be used if it has text set.
1544 * <p>
1545 * NOTE: For <code>JTree</code> to properly display tooltips of its
1546 * renderers, <code>JTree</code> must be a registered component with the
1547 * <code>ToolTipManager</code>. This can be done by invoking
1548 * <code>ToolTipManager.sharedInstance().registerComponent(tree)</code>.
1549 * This is not done automatically!
1550 *
1551 * @param event the <code>MouseEvent</code> that initiated the
1552 * <code>ToolTip</code> display
1553 * @return a string containing the tooltip or <code>null</code>
1554 * if <code>event</code> is null
1555 */
1556 public String getToolTipText(MouseEvent event) {
1557 String tip = null;
1558
1559 if(event != null) {
1560 Point p = event.getPoint();
1561 int selRow = getRowForLocation(p.x, p.y);
1562 TreeCellRenderer r = getCellRenderer();
1563
1564 if(selRow != -1 && r != null) {
1565 TreePath path = getPathForRow(selRow);
1566 Object lastPath = path.getLastPathComponent();
1567 Component rComponent = r.getTreeCellRendererComponent
1568 (this, lastPath, isRowSelected(selRow),
1569 isExpanded(selRow), getModel().isLeaf(lastPath), selRow,
1570 true);
1571
1572 if(rComponent instanceof JComponent) {
1573 MouseEvent newEvent;
1574 Rectangle pathBounds = getPathBounds(path);
1580 p.x, p.y,
1581 event.getXOnScreen(),
1582 event.getYOnScreen(),
1583 event.getClickCount(),
1584 event.isPopupTrigger(),
1585 MouseEvent.NOBUTTON);
1586
1587 tip = ((JComponent)rComponent).getToolTipText(newEvent);
1588 }
1589 }
1590 }
1591 // No tip from the renderer get our own tip
1592 if (tip == null) {
1593 tip = getToolTipText();
1594 }
1595 return tip;
1596 }
1597
1598 /**
1599 * Called by the renderers to convert the specified value to
1600 * text. This implementation returns <code>value.toString</code>, ignoring
1601 * all other arguments. To control the conversion, subclass this
1602 * method and use any of the arguments you need.
1603 *
1604 * @param value the <code>Object</code> to convert to text
1605 * @param selected true if the node is selected
1606 * @param expanded true if the node is expanded
1607 * @param leaf true if the node is a leaf node
1608 * @param row an integer specifying the node's display row, where 0 is
1609 * the first row in the display
1610 * @param hasFocus true if the node has the focus
1611 * @return the <code>String</code> representation of the node's value
1612 */
1613 public String convertValueToText(Object value, boolean selected,
1614 boolean expanded, boolean leaf, int row,
1615 boolean hasFocus) {
1616 if(value != null) {
1617 String sValue = value.toString();
1618 if (sValue != null) {
1619 return sValue;
1620 }
1621 }
1622 return "";
1623 }
1624
1625 //
1626 // The following are convenience methods that get forwarded to the
1627 // current TreeUI.
1628 //
1629
1630 /**
1631 * Returns the number of viewable nodes. A node is viewable if all of its
1632 * parents are expanded. The root is only included in this count if
1633 * {@code isRootVisible()} is {@code true}. This returns {@code 0} if
1634 * the UI has not been set.
1635 *
1636 * @return the number of viewable nodes
1637 */
1638 public int getRowCount() {
1639 TreeUI tree = getUI();
1640
1641 if(tree != null)
1642 return tree.getRowCount(this);
1643 return 0;
1644 }
1645
1646 /**
1647 * Selects the node identified by the specified path. If any
1648 * component of the path is hidden (under a collapsed node), and
1649 * <code>getExpandsSelectedPaths</code> is true it is
1650 * exposed (made viewable).
1651 *
1652 * @param path the <code>TreePath</code> specifying the node to select
1653 */
1654 public void setSelectionPath(TreePath path) {
1655 getSelectionModel().setSelectionPath(path);
1656 }
1657
1658 /**
1659 * Selects the nodes identified by the specified array of paths.
1660 * If any component in any of the paths is hidden (under a collapsed
1661 * node), and <code>getExpandsSelectedPaths</code> is true
1662 * it is exposed (made viewable).
1663 *
1664 * @param paths an array of <code>TreePath</code> objects that specifies
1665 * the nodes to select
1666 */
1667 public void setSelectionPaths(TreePath[] paths) {
1668 getSelectionModel().setSelectionPaths(paths);
1669 }
1670
1671 /**
1672 * Sets the path identifies as the lead. The lead may not be selected.
1673 * The lead is not maintained by <code>JTree</code>,
1674 * rather the UI will update it.
1675 * <p>
1676 * This is a bound property.
1677 *
1678 * @param newPath the new lead path
1679 * @since 1.3
1680 * @beaninfo
1681 * bound: true
1682 * description: Lead selection path
1683 */
1684 public void setLeadSelectionPath(TreePath newPath) {
1685 TreePath oldValue = leadPath;
1686
1687 leadPath = newPath;
1688 firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, newPath);
1689
1690 // Fire the active descendant property change here since the
1691 // leadPath got set, this is triggered both in case node
1692 // selection changed and node focus changed
1693 if (accessibleContext != null){
1694 ((AccessibleJTree)accessibleContext).
1695 fireActiveDescendantPropertyChange(oldValue, newPath);
1696 }
1697 }
1698
1699 /**
1700 * Sets the path identified as the anchor.
1701 * The anchor is not maintained by <code>JTree</code>, rather the UI will
1702 * update it.
1703 * <p>
1704 * This is a bound property.
1705 *
1706 * @param newPath the new anchor path
1707 * @since 1.3
1708 * @beaninfo
1709 * bound: true
1710 * description: Anchor selection path
1711 */
1712 public void setAnchorSelectionPath(TreePath newPath) {
1713 TreePath oldValue = anchorPath;
1714
1715 anchorPath = newPath;
1716 firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, newPath);
1717 }
1718
1719 /**
1720 * Selects the node at the specified row in the display.
1721 *
1722 * @param row the row to select, where 0 is the first row in
1723 * the display
1724 */
1725 public void setSelectionRow(int row) {
1726 int[] rows = { row };
1727
1728 setSelectionRows(rows);
1729 }
1730
1731 /**
1732 * Selects the nodes corresponding to each of the specified rows
1733 * in the display. If a particular element of <code>rows</code> is
1734 * < 0 or >= <code>getRowCount</code>, it will be ignored.
1735 * If none of the elements
1736 * in <code>rows</code> are valid rows, the selection will
1737 * be cleared. That is it will be as if <code>clearSelection</code>
1738 * was invoked.
1739 *
1740 * @param rows an array of ints specifying the rows to select,
1741 * where 0 indicates the first row in the display
1742 */
1743 public void setSelectionRows(int[] rows) {
1744 TreeUI ui = getUI();
1745
1746 if(ui != null && rows != null) {
1747 int numRows = rows.length;
1748 TreePath[] paths = new TreePath[numRows];
1749
1750 for(int counter = 0; counter < numRows; counter++) {
1751 paths[counter] = ui.getPathForRow(this, rows[counter]);
1752 }
1753 setSelectionPaths(paths);
1754 }
1755 }
1756
1757 /**
1758 * Adds the node identified by the specified <code>TreePath</code>
1759 * to the current selection. If any component of the path isn't
1760 * viewable, and <code>getExpandsSelectedPaths</code> is true it is
1761 * made viewable.
1762 * <p>
1763 * Note that <code>JTree</code> does not allow duplicate nodes to
1764 * exist as children under the same parent -- each sibling must be
1765 * a unique object.
1766 *
1767 * @param path the <code>TreePath</code> to add
1768 */
1769 public void addSelectionPath(TreePath path) {
1770 getSelectionModel().addSelectionPath(path);
1771 }
1772
1773 /**
1774 * Adds each path in the array of paths to the current selection. If
1775 * any component of any of the paths isn't viewable and
1776 * <code>getExpandsSelectedPaths</code> is true, it is
1777 * made viewable.
1778 * <p>
1779 * Note that <code>JTree</code> does not allow duplicate nodes to
1780 * exist as children under the same parent -- each sibling must be
1781 * a unique object.
1782 *
1783 * @param paths an array of <code>TreePath</code> objects that specifies
1784 * the nodes to add
1785 */
1786 public void addSelectionPaths(TreePath[] paths) {
1787 getSelectionModel().addSelectionPaths(paths);
1788 }
1789
1790 /**
1791 * Adds the path at the specified row to the current selection.
1792 *
1793 * @param row an integer specifying the row of the node to add,
1794 * where 0 is the first row in the display
1795 */
1796 public void addSelectionRow(int row) {
1797 int[] rows = { row };
1798
1799 addSelectionRows(rows);
1800 }
1801
1802 /**
1803 * Adds the paths at each of the specified rows to the current selection.
1808 public void addSelectionRows(int[] rows) {
1809 TreeUI ui = getUI();
1810
1811 if(ui != null && rows != null) {
1812 int numRows = rows.length;
1813 TreePath[] paths = new TreePath[numRows];
1814
1815 for(int counter = 0; counter < numRows; counter++)
1816 paths[counter] = ui.getPathForRow(this, rows[counter]);
1817 addSelectionPaths(paths);
1818 }
1819 }
1820
1821 /**
1822 * Returns the last path component of the selected path. This is
1823 * a convenience method for
1824 * {@code getSelectionModel().getSelectionPath().getLastPathComponent()}.
1825 * This is typically only useful if the selection has one path.
1826 *
1827 * @return the last path component of the selected path, or
1828 * <code>null</code> if nothing is selected
1829 * @see TreePath#getLastPathComponent
1830 */
1831 public Object getLastSelectedPathComponent() {
1832 TreePath selPath = getSelectionModel().getSelectionPath();
1833
1834 if(selPath != null)
1835 return selPath.getLastPathComponent();
1836 return null;
1837 }
1838
1839 /**
1840 * Returns the path identified as the lead.
1841 * @return path identified as the lead
1842 */
1843 public TreePath getLeadSelectionPath() {
1844 return leadPath;
1845 }
1846
1847 /**
1848 * Returns the path identified as the anchor.
1849 * @return path identified as the anchor
1850 * @since 1.3
1851 */
1852 public TreePath getAnchorSelectionPath() {
1853 return anchorPath;
1854 }
1855
1856 /**
1857 * Returns the path to the first selected node.
1858 *
1859 * @return the <code>TreePath</code> for the first selected node,
1860 * or <code>null</code> if nothing is currently selected
1861 */
1862 public TreePath getSelectionPath() {
1863 return getSelectionModel().getSelectionPath();
1864 }
1865
1866 /**
1867 * Returns the paths of all selected values.
1868 *
1869 * @return an array of <code>TreePath</code> objects indicating the selected
1870 * nodes, or <code>null</code> if nothing is currently selected
1871 */
1872 public TreePath[] getSelectionPaths() {
1873 TreePath[] selectionPaths = getSelectionModel().getSelectionPaths();
1874
1875 return (selectionPaths != null && selectionPaths.length > 0) ? selectionPaths : null;
1876 }
1877
1878 /**
1879 * Returns all of the currently selected rows. This method is simply
1880 * forwarded to the <code>TreeSelectionModel</code>.
1881 * If nothing is selected <code>null</code> or an empty array will
1882 * be returned, based on the <code>TreeSelectionModel</code>
1883 * implementation.
1884 *
1885 * @return an array of integers that identifies all currently selected rows
1886 * where 0 is the first row in the display
1887 */
1888 public int[] getSelectionRows() {
1889 return getSelectionModel().getSelectionRows();
1890 }
1891
1892 /**
1893 * Returns the number of nodes selected.
1894 *
1895 * @return the number of nodes selected
1896 */
1897 public int getSelectionCount() {
1898 return selectionModel.getSelectionCount();
1899 }
1900
1901 /**
1902 * Returns the smallest selected row. If the selection is empty, or
1906 */
1907 public int getMinSelectionRow() {
1908 return getSelectionModel().getMinSelectionRow();
1909 }
1910
1911 /**
1912 * Returns the largest selected row. If the selection is empty, or
1913 * none of the selected paths are viewable, {@code -1} is returned.
1914 *
1915 * @return the largest selected row
1916 */
1917 public int getMaxSelectionRow() {
1918 return getSelectionModel().getMaxSelectionRow();
1919 }
1920
1921 /**
1922 * Returns the row index corresponding to the lead path.
1923 *
1924 * @return an integer giving the row index of the lead path,
1925 * where 0 is the first row in the display; or -1
1926 * if <code>leadPath</code> is <code>null</code>
1927 */
1928 public int getLeadSelectionRow() {
1929 TreePath leadPath = getLeadSelectionPath();
1930
1931 if (leadPath != null) {
1932 return getRowForPath(leadPath);
1933 }
1934 return -1;
1935 }
1936
1937 /**
1938 * Returns true if the item identified by the path is currently selected.
1939 *
1940 * @param path a <code>TreePath</code> identifying a node
1941 * @return true if the node is selected
1942 */
1943 public boolean isPathSelected(TreePath path) {
1944 return getSelectionModel().isPathSelected(path);
1945 }
1946
1947 /**
1948 * Returns true if the node identified by row is selected.
1949 *
1950 * @param row an integer specifying a display row, where 0 is the first
1951 * row in the display
1952 * @return true if the node is selected
1953 */
1954 public boolean isRowSelected(int row) {
1955 return getSelectionModel().isRowSelected(row);
1956 }
1957
1958 /**
1959 * Returns an <code>Enumeration</code> of the descendants of the
1960 * path <code>parent</code> that
1961 * are currently expanded. If <code>parent</code> is not currently
1962 * expanded, this will return <code>null</code>.
1963 * If you expand/collapse nodes while
1964 * iterating over the returned <code>Enumeration</code>
1965 * this may not return all
1966 * the expanded paths, or may return paths that are no longer expanded.
1967 *
1968 * @param parent the path which is to be examined
1969 * @return an <code>Enumeration</code> of the descendents of
1970 * <code>parent</code>, or <code>null</code> if
1971 * <code>parent</code> is not currently expanded
1972 */
1973 public Enumeration<TreePath> getExpandedDescendants(TreePath parent) {
1974 if(!isExpanded(parent))
1975 return null;
1976
1977 Enumeration<TreePath> toggledPaths = expandedState.keys();
1978 Vector<TreePath> elements = null;
1979 TreePath path;
1980 Object value;
1981
1982 if(toggledPaths != null) {
1983 while(toggledPaths.hasMoreElements()) {
1984 path = toggledPaths.nextElement();
1985 value = expandedState.get(path);
1986 // Add the path if it is expanded, a descendant of parent,
1987 // and it is visible (all parents expanded). This is rather
1988 // expensive!
1989 if(path != parent && value != null &&
1990 ((Boolean)value).booleanValue() &&
1991 parent.isDescendant(path) && isVisible(path)) {
1992 if (elements == null) {
1993 elements = new Vector<TreePath>();
1994 }
1995 elements.addElement(path);
1996 }
1997 }
1998 }
1999 if (elements == null) {
2000 Set<TreePath> empty = Collections.emptySet();
2001 return Collections.enumeration(empty);
2002 }
2003 return elements.elements();
2004 }
2005
2006 /**
2007 * Returns true if the node identified by the path has ever been
2008 * expanded.
2009 *
2010 * @param path a {@code TreePath} identifying a node
2011 * @return true if the <code>path</code> has ever been expanded
2012 */
2013 public boolean hasBeenExpanded(TreePath path) {
2014 return (path != null && expandedState.get(path) != null);
2015 }
2016
2017 /**
2018 * Returns true if the node identified by the path is currently expanded,
2019 *
2020 * @param path the <code>TreePath</code> specifying the node to check
2021 * @return false if any of the nodes in the node's path are collapsed,
2022 * true if all nodes in the path are expanded
2023 */
2024 public boolean isExpanded(TreePath path) {
2025
2026 if(path == null)
2027 return false;
2028 Object value;
2029
2030 do{
2031 value = expandedState.get(path);
2032 if(value == null || !((Boolean)value).booleanValue())
2033 return false;
2034 } while( (path=path.getParentPath())!=null );
2035
2036 return true;
2037 }
2038
2039 /**
2040 * Returns true if the node at the specified display row is currently
2047 public boolean isExpanded(int row) {
2048 TreeUI tree = getUI();
2049
2050 if(tree != null) {
2051 TreePath path = tree.getPathForRow(this, row);
2052
2053 if(path != null) {
2054 Boolean value = expandedState.get(path);
2055
2056 return (value != null && value.booleanValue());
2057 }
2058 }
2059 return false;
2060 }
2061
2062 /**
2063 * Returns true if the value identified by path is currently collapsed,
2064 * this will return false if any of the values in path are currently
2065 * not being displayed.
2066 *
2067 * @param path the <code>TreePath</code> to check
2068 * @return true if any of the nodes in the node's path are collapsed,
2069 * false if all nodes in the path are expanded
2070 */
2071 public boolean isCollapsed(TreePath path) {
2072 return !isExpanded(path);
2073 }
2074
2075 /**
2076 * Returns true if the node at the specified display row is collapsed.
2077 *
2078 * @param row the row to check, where 0 is the first row in the
2079 * display
2080 * @return true if the node is currently collapsed, otherwise false
2081 */
2082 public boolean isCollapsed(int row) {
2083 return !isExpanded(row);
2084 }
2085
2086 /**
2087 * Ensures that the node identified by path is currently viewable.
2088 *
2089 * @param path the <code>TreePath</code> to make visible
2090 */
2091 public void makeVisible(TreePath path) {
2092 if(path != null) {
2093 TreePath parentPath = path.getParentPath();
2094
2095 if(parentPath != null) {
2096 expandPath(parentPath);
2097 }
2098 }
2099 }
2100
2101 /**
2102 * Returns true if the value identified by path is currently viewable,
2103 * which means it is either the root or all of its parents are expanded.
2104 * Otherwise, this method returns false.
2105 *
2106 * @param path {@code TreePath} identifying a node
2107 * @return true if the node is viewable, otherwise false
2108 */
2109 public boolean isVisible(TreePath path) {
2110 if(path != null) {
2111 TreePath parentPath = path.getParentPath();
2112
2113 if(parentPath != null)
2114 return isExpanded(parentPath);
2115 // Root.
2116 return true;
2117 }
2118 return false;
2119 }
2120
2121 /**
2122 * Returns the <code>Rectangle</code> that the specified node will be drawn
2123 * into. Returns <code>null</code> if any component in the path is hidden
2124 * (under a collapsed parent).
2125 * <p>
2126 * Note:<br>
2127 * This method returns a valid rectangle, even if the specified
2128 * node is not currently displayed.
2129 *
2130 * @param path the <code>TreePath</code> identifying the node
2131 * @return the <code>Rectangle</code> the node is drawn in,
2132 * or <code>null</code>
2133 */
2134 public Rectangle getPathBounds(TreePath path) {
2135 TreeUI tree = getUI();
2136
2137 if(tree != null)
2138 return tree.getPathBounds(this, path);
2139 return null;
2140 }
2141
2142 /**
2143 * Returns the <code>Rectangle</code> that the node at the specified row is
2144 * drawn in.
2145 *
2146 * @param row the row to be drawn, where 0 is the first row in the
2147 * display
2148 * @return the <code>Rectangle</code> the node is drawn in
2149 */
2150 public Rectangle getRowBounds(int row) {
2151 return getPathBounds(getPathForRow(row));
2152 }
2153
2154 /**
2155 * Makes sure all the path components in path are expanded (except
2156 * for the last path component) and scrolls so that the
2157 * node identified by the path is displayed. Only works when this
2158 * <code>JTree</code> is contained in a <code>JScrollPane</code>.
2159 *
2160 * @param path the <code>TreePath</code> identifying the node to
2161 * bring into view
2162 */
2163 public void scrollPathToVisible(TreePath path) {
2164 if(path != null) {
2165 makeVisible(path);
2166
2167 Rectangle bounds = getPathBounds(path);
2168
2169 if(bounds != null) {
2170 scrollRectToVisible(bounds);
2171 if (accessibleContext != null) {
2172 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
2173 }
2174 }
2175 }
2176 }
2177
2178 /**
2179 * Scrolls the item identified by row until it is displayed. The minimum
2180 * of amount of scrolling necessary to bring the row into view
2181 * is performed. Only works when this <code>JTree</code> is contained in a
2182 * <code>JScrollPane</code>.
2183 *
2184 * @param row an integer specifying the row to scroll, where 0 is the
2185 * first row in the display
2186 */
2187 public void scrollRowToVisible(int row) {
2188 scrollPathToVisible(getPathForRow(row));
2189 }
2190
2191 /**
2192 * Returns the path for the specified row. If <code>row</code> is
2193 * not visible, or a {@code TreeUI} has not been set, <code>null</code>
2194 * is returned.
2195 *
2196 * @param row an integer specifying a row
2197 * @return the <code>TreePath</code> to the specified node,
2198 * <code>null</code> if <code>row < 0</code>
2199 * or <code>row >= getRowCount()</code>
2200 */
2201 public TreePath getPathForRow(int row) {
2202 TreeUI tree = getUI();
2203
2204 if(tree != null)
2205 return tree.getPathForRow(this, row);
2206 return null;
2207 }
2208
2209 /**
2210 * Returns the row that displays the node identified by the specified
2211 * path.
2212 *
2213 * @param path the <code>TreePath</code> identifying a node
2214 * @return an integer specifying the display row, where 0 is the first
2215 * row in the display, or -1 if any of the elements in path
2216 * are hidden under a collapsed parent.
2217 */
2218 public int getRowForPath(TreePath path) {
2219 TreeUI tree = getUI();
2220
2221 if(tree != null)
2222 return tree.getRowForPath(this, path);
2223 return -1;
2224 }
2225
2226 /**
2227 * Ensures that the node identified by the specified path is
2228 * expanded and viewable. If the last item in the path is a
2229 * leaf, this will have no effect.
2230 *
2231 * @param path the <code>TreePath</code> identifying a node
2232 */
2233 public void expandPath(TreePath path) {
2234 // Only expand if not leaf!
2235 TreeModel model = getModel();
2236
2237 if(path != null && model != null &&
2238 !model.isLeaf(path.getLastPathComponent())) {
2239 setExpandedState(path, true);
2240 }
2241 }
2242
2243 /**
2244 * Ensures that the node in the specified row is expanded and
2245 * viewable.
2246 * <p>
2247 * If <code>row</code> is < 0 or >= <code>getRowCount</code> this
2248 * will have no effect.
2249 *
2250 * @param row an integer specifying a display row, where 0 is the
2251 * first row in the display
2252 */
2253 public void expandRow(int row) {
2254 expandPath(getPathForRow(row));
2255 }
2256
2257 /**
2258 * Ensures that the node identified by the specified path is
2259 * collapsed and viewable.
2260 *
2261 * @param path the <code>TreePath</code> identifying a node
2262 */
2263 public void collapsePath(TreePath path) {
2264 setExpandedState(path, false);
2265 }
2266
2267 /**
2268 * Ensures that the node in the specified row is collapsed.
2269 * <p>
2270 * If <code>row</code> is < 0 or >= <code>getRowCount</code> this
2271 * will have no effect.
2272 *
2273 * @param row an integer specifying a display row, where 0 is the
2274 * first row in the display
2275 */
2276 public void collapseRow(int row) {
2277 collapsePath(getPathForRow(row));
2278 }
2279
2280 /**
2281 * Returns the path for the node at the specified location.
2282 *
2283 * @param x an integer giving the number of pixels horizontally from
2284 * the left edge of the display area, minus any left margin
2285 * @param y an integer giving the number of pixels vertically from
2286 * the top of the display area, minus any top margin
2287 * @return the <code>TreePath</code> for the node at that location
2288 */
2289 public TreePath getPathForLocation(int x, int y) {
2290 TreePath closestPath = getClosestPathForLocation(x, y);
2291
2292 if(closestPath != null) {
2293 Rectangle pathBounds = getPathBounds(closestPath);
2294
2295 if(pathBounds != null &&
2296 x >= pathBounds.x && x < (pathBounds.x + pathBounds.width) &&
2297 y >= pathBounds.y && y < (pathBounds.y + pathBounds.height))
2298 return closestPath;
2299 }
2300 return null;
2301 }
2302
2303 /**
2304 * Returns the row for the specified location.
2305 *
2306 * @param x an integer giving the number of pixels horizontally from
2307 * the left edge of the display area, minus any left margin
2308 * @param y an integer giving the number of pixels vertically from
2309 * the top of the display area, minus any top margin
2310 * @return the row corresponding to the location, or -1 if the
2311 * location is not within the bounds of a displayed cell
2312 * @see #getClosestRowForLocation
2313 */
2314 public int getRowForLocation(int x, int y) {
2315 return getRowForPath(getPathForLocation(x, y));
2316 }
2317
2318 /**
2319 * Returns the path to the node that is closest to x,y. If
2320 * no nodes are currently viewable, or there is no model, returns
2321 * <code>null</code>, otherwise it always returns a valid path. To test if
2322 * the node is exactly at x, y, get the node's bounds and
2323 * test x, y against that.
2324 *
2325 * @param x an integer giving the number of pixels horizontally from
2326 * the left edge of the display area, minus any left margin
2327 * @param y an integer giving the number of pixels vertically from
2328 * the top of the display area, minus any top margin
2329 * @return the <code>TreePath</code> for the node closest to that location,
2330 * <code>null</code> if nothing is viewable or there is no model
2331 *
2332 * @see #getPathForLocation
2333 * @see #getPathBounds
2334 */
2335 public TreePath getClosestPathForLocation(int x, int y) {
2336 TreeUI tree = getUI();
2337
2338 if(tree != null)
2339 return tree.getClosestPathForLocation(this, x, y);
2340 return null;
2341 }
2342
2343 /**
2344 * Returns the row to the node that is closest to x,y. If no nodes
2345 * are viewable or there is no model, returns -1. Otherwise,
2346 * it always returns a valid row. To test if the returned object is
2347 * exactly at x, y, get the bounds for the node at the returned
2348 * row and test x, y against that.
2349 *
2350 * @param x an integer giving the number of pixels horizontally from
2351 * the left edge of the display area, minus any left margin
2352 * @param y an integer giving the number of pixels vertically from
2353 * the top of the display area, minus any top margin
2354 * @return the row closest to the location, -1 if nothing is
2355 * viewable or there is no model
2356 *
2357 * @see #getRowForLocation
2358 * @see #getRowBounds
2359 */
2360 public int getClosestRowForLocation(int x, int y) {
2361 return getRowForPath(getClosestPathForLocation(x, y));
2362 }
2363
2364 /**
2365 * Returns true if the tree is being edited. The item that is being
2366 * edited can be obtained using <code>getSelectionPath</code>.
2367 *
2368 * @return true if the user is currently editing a node
2369 * @see #getSelectionPath
2370 */
2371 public boolean isEditing() {
2372 TreeUI tree = getUI();
2373
2374 if(tree != null)
2375 return tree.isEditing(this);
2376 return false;
2377 }
2378
2379 /**
2380 * Ends the current editing session.
2381 * (The <code>DefaultTreeCellEditor</code>
2382 * object saves any edits that are currently in progress on a cell.
2383 * Other implementations may operate differently.)
2384 * Has no effect if the tree isn't being edited.
2385 * <blockquote>
2386 * <b>Note:</b><br>
2387 * To make edit-saves automatic whenever the user changes
2388 * their position in the tree, use {@link #setInvokesStopCellEditing}.
2389 * </blockquote>
2390 *
2391 * @return true if editing was in progress and is now stopped,
2392 * false if editing was not in progress
2393 */
2394 public boolean stopEditing() {
2395 TreeUI tree = getUI();
2396
2397 if(tree != null)
2398 return tree.stopEditing(this);
2399 return false;
2400 }
2401
2402 /**
2403 * Cancels the current editing session. Has no effect if the
2404 * tree isn't being edited.
2405 */
2406 public void cancelEditing() {
2407 TreeUI tree = getUI();
2408
2409 if(tree != null)
2410 tree.cancelEditing(this);
2411 }
2412
2413 /**
2414 * Selects the node identified by the specified path and initiates
2415 * editing. The edit-attempt fails if the <code>CellEditor</code>
2416 * does not allow
2417 * editing for the specified item.
2418 *
2419 * @param path the <code>TreePath</code> identifying a node
2420 */
2421 public void startEditingAtPath(TreePath path) {
2422 TreeUI tree = getUI();
2423
2424 if(tree != null)
2425 tree.startEditingAtPath(this, path);
2426 }
2427
2428 /**
2429 * Returns the path to the element that is currently being edited.
2430 *
2431 * @return the <code>TreePath</code> for the node being edited
2432 */
2433 public TreePath getEditingPath() {
2434 TreeUI tree = getUI();
2435
2436 if(tree != null)
2437 return tree.getEditingPath(this);
2438 return null;
2439 }
2440
2441 //
2442 // Following are primarily convenience methods for mapping from
2443 // row based selections to path selections. Sometimes it is
2444 // easier to deal with these than paths (mouse downs, key downs
2445 // usually just deal with index based selections).
2446 // Since row based selections require a UI many of these won't work
2447 // without one.
2448 //
2449
2450 /**
2451 * Sets the tree's selection model. When a <code>null</code> value is
2452 * specified an empty
2453 * <code>selectionModel</code> is used, which does not allow selections.
2454 * <p>
2455 * This is a bound property.
2456 *
2457 * @param selectionModel the <code>TreeSelectionModel</code> to use,
2458 * or <code>null</code> to disable selections
2459 * @see TreeSelectionModel
2460 * @beaninfo
2461 * bound: true
2462 * description: The tree's selection model.
2463 */
2464 public void setSelectionModel(TreeSelectionModel selectionModel) {
2465 if(selectionModel == null)
2466 selectionModel = EmptySelectionModel.sharedInstance();
2467
2468 TreeSelectionModel oldValue = this.selectionModel;
2469
2470 if (this.selectionModel != null && selectionRedirector != null) {
2471 this.selectionModel.removeTreeSelectionListener
2472 (selectionRedirector);
2473 }
2474 if (accessibleContext != null) {
2475 this.selectionModel.removeTreeSelectionListener((TreeSelectionListener)accessibleContext);
2476 selectionModel.addTreeSelectionListener((TreeSelectionListener)accessibleContext);
2477 }
2478
2479 this.selectionModel = selectionModel;
2480 if (selectionRedirector != null) {
2481 this.selectionModel.addTreeSelectionListener(selectionRedirector);
2482 }
2483 firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue,
2484 this.selectionModel);
2485
2486 if (accessibleContext != null) {
2487 accessibleContext.firePropertyChange(
2488 AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
2489 Boolean.valueOf(false), Boolean.valueOf(true));
2490 }
2491 }
2492
2493 /**
2494 * Returns the model for selections. This should always return a
2495 * non-<code>null</code> value. If you don't want to allow anything
2496 * to be selected
2497 * set the selection model to <code>null</code>, which forces an empty
2498 * selection model to be used.
2499 *
2500 * @return the model for selections
2501 * @see #setSelectionModel
2502 */
2503 public TreeSelectionModel getSelectionModel() {
2504 return selectionModel;
2505 }
2506
2507 /**
2508 * Returns the paths (inclusive) between the specified rows. If
2509 * the specified indices are within the viewable set of rows, or
2510 * bound the viewable set of rows, then the indices are
2511 * constrained by the viewable set of rows. If the specified
2512 * indices are not within the viewable set of rows, or do not
2513 * bound the viewable set of rows, then an empty array is
2514 * returned. For example, if the row count is {@code 10}, and this
2515 * method is invoked with {@code -1, 20}, then the specified
2516 * indices are constrained to the viewable set of rows, and this is
2517 * treated as if invoked with {@code 0, 9}. On the other hand, if
2622 * <p>
2623 * The parameters are not order dependent. That is, {@code
2624 * removeSelectionInterval(x, y)} is equivalent to
2625 * {@code removeSelectionInterval(y, x)}.
2626 *
2627 * @param index0 the first row to remove from the selection
2628 * @param index1 the last row to remove from the selection
2629 */
2630 public void removeSelectionInterval(int index0, int index1) {
2631 TreePath[] paths = getPathBetweenRows(index0, index1);
2632
2633 if (paths != null && paths.length > 0) {
2634 this.getSelectionModel().removeSelectionPaths(paths);
2635 }
2636 }
2637
2638 /**
2639 * Removes the node identified by the specified path from the current
2640 * selection.
2641 *
2642 * @param path the <code>TreePath</code> identifying a node
2643 */
2644 public void removeSelectionPath(TreePath path) {
2645 this.getSelectionModel().removeSelectionPath(path);
2646 }
2647
2648 /**
2649 * Removes the nodes identified by the specified paths from the
2650 * current selection.
2651 *
2652 * @param paths an array of <code>TreePath</code> objects that
2653 * specifies the nodes to remove
2654 */
2655 public void removeSelectionPaths(TreePath[] paths) {
2656 this.getSelectionModel().removeSelectionPaths(paths);
2657 }
2658
2659 /**
2660 * Removes the row at the index <code>row</code> from the current
2661 * selection.
2662 *
2663 * @param row the row to remove
2664 */
2665 public void removeSelectionRow(int row) {
2666 int[] rows = { row };
2667
2668 removeSelectionRows(rows);
2669 }
2670
2671 /**
2672 * Removes the rows that are selected at each of the specified
2673 * rows.
2674 *
2675 * @param rows an array of ints specifying display rows, where 0 is
2676 * the first row in the display
2677 */
2678 public void removeSelectionRows(int[] rows) {
2679 TreeUI ui = getUI();
2680
2688 }
2689 }
2690
2691 /**
2692 * Clears the selection.
2693 */
2694 public void clearSelection() {
2695 getSelectionModel().clearSelection();
2696 }
2697
2698 /**
2699 * Returns true if the selection is currently empty.
2700 *
2701 * @return true if the selection is currently empty
2702 */
2703 public boolean isSelectionEmpty() {
2704 return getSelectionModel().isSelectionEmpty();
2705 }
2706
2707 /**
2708 * Adds a listener for <code>TreeExpansion</code> events.
2709 *
2710 * @param tel a TreeExpansionListener that will be notified when
2711 * a tree node is expanded or collapsed (a "negative
2712 * expansion")
2713 */
2714 public void addTreeExpansionListener(TreeExpansionListener tel) {
2715 if (settingUI) {
2716 uiTreeExpansionListener = tel;
2717 }
2718 listenerList.add(TreeExpansionListener.class, tel);
2719 }
2720
2721 /**
2722 * Removes a listener for <code>TreeExpansion</code> events.
2723 *
2724 * @param tel the <code>TreeExpansionListener</code> to remove
2725 */
2726 public void removeTreeExpansionListener(TreeExpansionListener tel) {
2727 listenerList.remove(TreeExpansionListener.class, tel);
2728 if (uiTreeExpansionListener == tel) {
2729 uiTreeExpansionListener = null;
2730 }
2731 }
2732
2733 /**
2734 * Returns an array of all the <code>TreeExpansionListener</code>s added
2735 * to this JTree with addTreeExpansionListener().
2736 *
2737 * @return all of the <code>TreeExpansionListener</code>s added or an empty
2738 * array if no listeners have been added
2739 * @since 1.4
2740 */
2741 public TreeExpansionListener[] getTreeExpansionListeners() {
2742 return listenerList.getListeners(TreeExpansionListener.class);
2743 }
2744
2745 /**
2746 * Adds a listener for <code>TreeWillExpand</code> events.
2747 *
2748 * @param tel a <code>TreeWillExpandListener</code> that will be notified
2749 * when a tree node will be expanded or collapsed (a "negative
2750 * expansion")
2751 */
2752 public void addTreeWillExpandListener(TreeWillExpandListener tel) {
2753 listenerList.add(TreeWillExpandListener.class, tel);
2754 }
2755
2756 /**
2757 * Removes a listener for <code>TreeWillExpand</code> events.
2758 *
2759 * @param tel the <code>TreeWillExpandListener</code> to remove
2760 */
2761 public void removeTreeWillExpandListener(TreeWillExpandListener tel) {
2762 listenerList.remove(TreeWillExpandListener.class, tel);
2763 }
2764
2765 /**
2766 * Returns an array of all the <code>TreeWillExpandListener</code>s added
2767 * to this JTree with addTreeWillExpandListener().
2768 *
2769 * @return all of the <code>TreeWillExpandListener</code>s added or an empty
2770 * array if no listeners have been added
2771 * @since 1.4
2772 */
2773 public TreeWillExpandListener[] getTreeWillExpandListeners() {
2774 return listenerList.getListeners(TreeWillExpandListener.class);
2775 }
2776
2777 /**
2778 * Notifies all listeners that have registered interest for
2779 * notification on this event type. The event instance
2780 * is lazily created using the <code>path</code> parameter.
2781 *
2782 * @param path the <code>TreePath</code> indicating the node that was
2783 * expanded
2784 * @see EventListenerList
2785 */
2786 public void fireTreeExpanded(TreePath path) {
2787 // Guaranteed to return a non-null array
2788 Object[] listeners = listenerList.getListenerList();
2789 TreeExpansionEvent e = null;
2790 if (uiTreeExpansionListener != null) {
2791 e = new TreeExpansionEvent(this, path);
2792 uiTreeExpansionListener.treeExpanded(e);
2793 }
2794 // Process the listeners last to first, notifying
2795 // those that are interested in this event
2796 for (int i = listeners.length-2; i>=0; i-=2) {
2797 if (listeners[i]==TreeExpansionListener.class &&
2798 listeners[i + 1] != uiTreeExpansionListener) {
2799 // Lazily create the event:
2800 if (e == null)
2801 e = new TreeExpansionEvent(this, path);
2802 ((TreeExpansionListener)listeners[i+1]).
2803 treeExpanded(e);
2804 }
2805 }
2806 }
2807
2808 /**
2809 * Notifies all listeners that have registered interest for
2810 * notification on this event type. The event instance
2811 * is lazily created using the <code>path</code> parameter.
2812 *
2813 * @param path the <code>TreePath</code> indicating the node that was
2814 * collapsed
2815 * @see EventListenerList
2816 */
2817 public void fireTreeCollapsed(TreePath path) {
2818 // Guaranteed to return a non-null array
2819 Object[] listeners = listenerList.getListenerList();
2820 TreeExpansionEvent e = null;
2821 if (uiTreeExpansionListener != null) {
2822 e = new TreeExpansionEvent(this, path);
2823 uiTreeExpansionListener.treeCollapsed(e);
2824 }
2825 // Process the listeners last to first, notifying
2826 // those that are interested in this event
2827 for (int i = listeners.length-2; i>=0; i-=2) {
2828 if (listeners[i]==TreeExpansionListener.class &&
2829 listeners[i + 1] != uiTreeExpansionListener) {
2830 // Lazily create the event:
2831 if (e == null)
2832 e = new TreeExpansionEvent(this, path);
2833 ((TreeExpansionListener)listeners[i+1]).
2834 treeCollapsed(e);
2835 }
2836 }
2837 }
2838
2839 /**
2840 * Notifies all listeners that have registered interest for
2841 * notification on this event type. The event instance
2842 * is lazily created using the <code>path</code> parameter.
2843 *
2844 * @param path the <code>TreePath</code> indicating the node that was
2845 * expanded
2846 * @throws ExpandVetoException if the expansion is prevented from occurring
2847 * @see EventListenerList
2848 */
2849 public void fireTreeWillExpand(TreePath path) throws ExpandVetoException {
2850 // Guaranteed to return a non-null array
2851 Object[] listeners = listenerList.getListenerList();
2852 TreeExpansionEvent e = null;
2853 // Process the listeners last to first, notifying
2854 // those that are interested in this event
2855 for (int i = listeners.length-2; i>=0; i-=2) {
2856 if (listeners[i]==TreeWillExpandListener.class) {
2857 // Lazily create the event:
2858 if (e == null)
2859 e = new TreeExpansionEvent(this, path);
2860 ((TreeWillExpandListener)listeners[i+1]).
2861 treeWillExpand(e);
2862 }
2863 }
2864 }
2865
2866 /**
2867 * Notifies all listeners that have registered interest for
2868 * notification on this event type. The event instance
2869 * is lazily created using the <code>path</code> parameter.
2870 *
2871 * @param path the <code>TreePath</code> indicating the node that was
2872 * expanded
2873 * @throws ExpandVetoException if the collapse is prevented from occurring
2874 * @see EventListenerList
2875 */
2876 public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException {
2877 // Guaranteed to return a non-null array
2878 Object[] listeners = listenerList.getListenerList();
2879 TreeExpansionEvent e = null;
2880 // Process the listeners last to first, notifying
2881 // those that are interested in this event
2882 for (int i = listeners.length-2; i>=0; i-=2) {
2883 if (listeners[i]==TreeWillExpandListener.class) {
2884 // Lazily create the event:
2885 if (e == null)
2886 e = new TreeExpansionEvent(this, path);
2887 ((TreeWillExpandListener)listeners[i+1]).
2888 treeWillCollapse(e);
2889 }
2890 }
2891 }
2892
2893 /**
2894 * Adds a listener for <code>TreeSelection</code> events.
2895 *
2896 * @param tsl the <code>TreeSelectionListener</code> that will be notified
2897 * when a node is selected or deselected (a "negative
2898 * selection")
2899 */
2900 public void addTreeSelectionListener(TreeSelectionListener tsl) {
2901 listenerList.add(TreeSelectionListener.class,tsl);
2902 if(listenerList.getListenerCount(TreeSelectionListener.class) != 0
2903 && selectionRedirector == null) {
2904 selectionRedirector = new TreeSelectionRedirector();
2905 selectionModel.addTreeSelectionListener(selectionRedirector);
2906 }
2907 }
2908
2909 /**
2910 * Removes a <code>TreeSelection</code> listener.
2911 *
2912 * @param tsl the <code>TreeSelectionListener</code> to remove
2913 */
2914 public void removeTreeSelectionListener(TreeSelectionListener tsl) {
2915 listenerList.remove(TreeSelectionListener.class,tsl);
2916 if(listenerList.getListenerCount(TreeSelectionListener.class) == 0
2917 && selectionRedirector != null) {
2918 selectionModel.removeTreeSelectionListener
2919 (selectionRedirector);
2920 selectionRedirector = null;
2921 }
2922 }
2923
2924 /**
2925 * Returns an array of all the <code>TreeSelectionListener</code>s added
2926 * to this JTree with addTreeSelectionListener().
2927 *
2928 * @return all of the <code>TreeSelectionListener</code>s added or an empty
2929 * array if no listeners have been added
2930 * @since 1.4
2931 */
2932 public TreeSelectionListener[] getTreeSelectionListeners() {
2933 return listenerList.getListeners(TreeSelectionListener.class);
2934 }
2935
2936 /**
2937 * Notifies all listeners that have registered interest for
2938 * notification on this event type.
2939 *
2940 * @param e the <code>TreeSelectionEvent</code> to be fired;
2941 * generated by the
2942 * <code>TreeSelectionModel</code>
2943 * when a node is selected or deselected
2944 * @see EventListenerList
2945 */
2946 protected void fireValueChanged(TreeSelectionEvent e) {
2947 // Guaranteed to return a non-null array
2948 Object[] listeners = listenerList.getListenerList();
2949 // Process the listeners last to first, notifying
2950 // those that are interested in this event
2951 for (int i = listeners.length-2; i>=0; i-=2) {
2952 // TreeSelectionEvent e = null;
2953 if (listeners[i]==TreeSelectionListener.class) {
2954 // Lazily create the event:
2955 // if (e == null)
2956 // e = new ListSelectionEvent(this, firstIndex, lastIndex);
2957 ((TreeSelectionListener)listeners[i+1]).valueChanged(e);
2958 }
2959 }
2960 }
2961
2962 /**
2963 * Sent when the tree has changed enough that we need to resize
2964 * the bounds, but not enough that we need to remove the
2965 * expanded node set (e.g nodes were expanded or collapsed, or
2966 * nodes were inserted into the tree). You should never have to
2967 * invoke this, the UI will invoke this as it needs to.
2968 */
2969 public void treeDidChange() {
2970 revalidate();
2971 repaint();
2972 }
2973
2974 /**
2975 * Sets the number of rows that are to be displayed.
2976 * This will only work if the tree is contained in a
2977 * <code>JScrollPane</code>,
2978 * and will adjust the preferred size and size of that scrollpane.
2979 * <p>
2980 * This is a bound property.
2981 *
2982 * @param newCount the number of rows to display
2983 * @beaninfo
2984 * bound: true
2985 * description: The number of rows that are to be displayed.
2986 */
2987 public void setVisibleRowCount(int newCount) {
2988 int oldCount = visibleRowCount;
2989
2990 visibleRowCount = newCount;
2991 firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldCount,
2992 visibleRowCount);
2993 invalidate();
2994 if (accessibleContext != null) {
2995 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
2996 }
2997 }
3001 *
3002 * @return the number of rows displayed
3003 */
3004 public int getVisibleRowCount() {
3005 return visibleRowCount;
3006 }
3007
3008 /**
3009 * Expands the root path, assuming the current TreeModel has been set.
3010 */
3011 private void expandRoot() {
3012 TreeModel model = getModel();
3013 if(model != null && model.getRoot() != null) {
3014 expandPath(new TreePath(model.getRoot()));
3015 }
3016 }
3017
3018 /**
3019 * Returns the TreePath to the next tree element that
3020 * begins with a prefix. To handle the conversion of a
3021 * <code>TreePath</code> into a String, <code>convertValueToText</code>
3022 * is used.
3023 *
3024 * @param prefix the string to test for a match
3025 * @param startingRow the row for starting the search
3026 * @param bias the search direction, either
3027 * Position.Bias.Forward or Position.Bias.Backward.
3028 * @return the TreePath of the next tree element that
3029 * starts with the prefix; otherwise null
3030 * @exception IllegalArgumentException if prefix is null
3031 * or startingRow is out of bounds
3032 * @since 1.4
3033 */
3034 public TreePath getNextMatch(String prefix, int startingRow,
3035 Position.Bias bias) {
3036
3037 int max = getRowCount();
3038 if (prefix == null) {
3039 throw new IllegalArgumentException();
3040 }
3041 if (startingRow < 0 || startingRow >= max) {
3202 Object archivePath;
3203
3204 try {
3205 archivePath = getModelIndexsForPath(path);
3206 } catch (Error error) {
3207 archivePath = null;
3208 }
3209 if(archivePath != null) {
3210 state.addElement(archivePath);
3211 state.addElement(expandedState.get(path));
3212 }
3213 }
3214 return state;
3215 }
3216 }
3217 return null;
3218 }
3219
3220 /**
3221 * Updates the expanded state of nodes in the tree based on the
3222 * previously archived state <code>state</code>.
3223 */
3224 private void unarchiveExpandedState(Object state) {
3225 if(state instanceof Vector) {
3226 Vector<?> paths = (Vector)state;
3227
3228 for(int counter = paths.size() - 1; counter >= 0; counter--) {
3229 Boolean eState = (Boolean)paths.elementAt(counter--);
3230 TreePath path;
3231
3232 try {
3233 path = getPathForIndexs((int[])paths.elementAt(counter));
3234 if(path != null)
3235 expandedState.put(path, eState);
3236 } catch (Error error) {}
3237 }
3238 }
3239 }
3240
3241 /**
3242 * Returns an array of integers specifying the indexs of the
3243 * components in the <code>path</code>. If <code>path</code> is
3244 * the root, this will return an empty array. If <code>path</code>
3245 * is <code>null</code>, <code>null</code> will be returned.
3246 */
3247 private int[] getModelIndexsForPath(TreePath path) {
3248 if(path != null) {
3249 TreeModel model = getModel();
3250 int count = path.getPathCount();
3251 int[] indexs = new int[count - 1];
3252 Object parent = model.getRoot();
3253
3254 for(int counter = 1; counter < count; counter++) {
3255 indexs[counter - 1] = model.getIndexOfChild
3256 (parent, path.getPathComponent(counter));
3257 parent = path.getPathComponent(counter);
3258 if(indexs[counter - 1] < 0)
3259 return null;
3260 }
3261 return indexs;
3262 }
3263 return null;
3264 }
3265
3266 /**
3267 * Returns a <code>TreePath</code> created by obtaining the children
3268 * for each of the indices in <code>indexs</code>. If <code>indexs</code>
3269 * or the <code>TreeModel</code> is <code>null</code>, it will return
3270 * <code>null</code>.
3271 */
3272 private TreePath getPathForIndexs(int[] indexs) {
3273 if(indexs == null)
3274 return null;
3275
3276 TreeModel model = getModel();
3277
3278 if(model == null)
3279 return null;
3280
3281 int count = indexs.length;
3282
3283 Object parent = model.getRoot();
3284 if (parent == null)
3285 return null;
3286
3287 TreePath parentPath = new TreePath(parent);
3288 for(int counter = 0; counter < count; counter++) {
3289 parent = model.getChild(parent, indexs[counter]);
3290 if(parent == null)
3291 return null;
3292 parentPath = parentPath.pathByAddingChild(parent);
3293 }
3294 return parentPath;
3295 }
3296
3297 /**
3298 * <code>EmptySelectionModel</code> is a <code>TreeSelectionModel</code>
3299 * that does not allow anything to be selected.
3300 * <p>
3301 * <strong>Warning:</strong>
3302 * Serialized objects of this class will not be compatible with
3303 * future Swing releases. The current serialization support is
3304 * appropriate for short term storage or RMI between applications running
3305 * the same version of Swing. As of 1.4, support for long term storage
3306 * of all JavaBeans™
3307 * has been added to the <code>java.beans</code> package.
3308 * Please see {@link java.beans.XMLEncoder}.
3309 */
3310 @SuppressWarnings("serial")
3311 protected static class EmptySelectionModel extends
3312 DefaultTreeSelectionModel
3313 {
3314 /**
3315 * The single instance of {@code EmptySelectionModel}.
3316 */
3317 protected static final EmptySelectionModel sharedInstance =
3318 new EmptySelectionModel();
3319
3320 /**
3321 * Returns the single instance of {@code EmptySelectionModel}.
3322 *
3323 * @return single instance of {@code EmptySelectionModel}
3324 */
3325 public static EmptySelectionModel sharedInstance() {
3326 return sharedInstance;
3327 }
3399 * @since 1.7
3400 */
3401 public void addPropertyChangeListener(
3402 PropertyChangeListener listener) {
3403 }
3404
3405 /**
3406 * This is overriden to do nothing; {@code EmptySelectionModel}
3407 * does not allow a selection.
3408 *
3409 * @param listener the listener to remove; this is ignored
3410 * @since 1.7
3411 */
3412 public void removePropertyChangeListener(
3413 PropertyChangeListener listener) {
3414 }
3415 }
3416
3417
3418 /**
3419 * Handles creating a new <code>TreeSelectionEvent</code> with the
3420 * <code>JTree</code> as the
3421 * source and passing it off to all the listeners.
3422 * <p>
3423 * <strong>Warning:</strong>
3424 * Serialized objects of this class will not be compatible with
3425 * future Swing releases. The current serialization support is
3426 * appropriate for short term storage or RMI between applications running
3427 * the same version of Swing. As of 1.4, support for long term storage
3428 * of all JavaBeans™
3429 * has been added to the <code>java.beans</code> package.
3430 * Please see {@link java.beans.XMLEncoder}.
3431 */
3432 @SuppressWarnings("serial")
3433 protected class TreeSelectionRedirector implements Serializable,
3434 TreeSelectionListener
3435 {
3436 /**
3437 * Invoked by the <code>TreeSelectionModel</code> when the
3438 * selection changes.
3439 *
3440 * @param e the <code>TreeSelectionEvent</code> generated by the
3441 * <code>TreeSelectionModel</code>
3442 */
3443 public void valueChanged(TreeSelectionEvent e) {
3444 TreeSelectionEvent newE;
3445
3446 newE = (TreeSelectionEvent)e.cloneWithSource(JTree.this);
3447 fireValueChanged(newE);
3448 }
3449 } // End of class JTree.TreeSelectionRedirector
3450
3451 //
3452 // Scrollable interface
3453 //
3454
3455 /**
3456 * Returns the preferred display size of a <code>JTree</code>. The height is
3457 * determined from <code>getVisibleRowCount</code> and the width
3458 * is the current preferred width.
3459 *
3460 * @return a <code>Dimension</code> object containing the preferred size
3461 */
3462 public Dimension getPreferredScrollableViewportSize() {
3463 int width = getPreferredSize().width;
3464 int visRows = getVisibleRowCount();
3465 int height = -1;
3466
3467 if(isFixedRowHeight())
3468 height = visRows * getRowHeight();
3469 else {
3470 TreeUI ui = getUI();
3471
3472 if (ui != null && visRows > 0) {
3473 int rc = ui.getRowCount(this);
3474
3475 if (rc >= visRows) {
3476 Rectangle bounds = getRowBounds(visRows - 1);
3477 if (bounds != null) {
3478 height = bounds.y + bounds.height;
3479 }
3480 }
3482 Rectangle bounds = getRowBounds(0);
3483 if (bounds != null) {
3484 height = bounds.height * visRows;
3485 }
3486 }
3487 }
3488 if (height == -1) {
3489 height = 16 * visRows;
3490 }
3491 }
3492 return new Dimension(width, height);
3493 }
3494
3495 /**
3496 * Returns the amount to increment when scrolling. The amount is
3497 * the height of the first displayed row that isn't completely in view
3498 * or, if it is totally displayed, the height of the next row in the
3499 * scrolling direction.
3500 *
3501 * @param visibleRect the view area visible within the viewport
3502 * @param orientation either <code>SwingConstants.VERTICAL</code>
3503 * or <code>SwingConstants.HORIZONTAL</code>
3504 * @param direction less than zero to scroll up/left,
3505 * greater than zero for down/right
3506 * @return the "unit" increment for scrolling in the specified direction
3507 * @see JScrollBar#setUnitIncrement(int)
3508 */
3509 public int getScrollableUnitIncrement(Rectangle visibleRect,
3510 int orientation, int direction) {
3511 if(orientation == SwingConstants.VERTICAL) {
3512 Rectangle rowBounds;
3513 int firstIndex = getClosestRowForLocation
3514 (0, visibleRect.y);
3515
3516 if(firstIndex != -1) {
3517 rowBounds = getRowBounds(firstIndex);
3518 if(rowBounds.y != visibleRect.y) {
3519 if(direction < 0) {
3520 // UP
3521 return Math.max(0, (visibleRect.y - rowBounds.y));
3522 }
3523 return (rowBounds.y + rowBounds.height - visibleRect.y);
3524 }
3525 if(direction < 0) { // UP
3526 if(firstIndex != 0) {
3527 rowBounds = getRowBounds(firstIndex - 1);
3528 return rowBounds.height;
3529 }
3530 }
3531 else {
3532 return rowBounds.height;
3533 }
3534 }
3535 return 0;
3536 }
3537 return 4;
3538 }
3539
3540
3541 /**
3542 * Returns the amount for a block increment, which is the height or
3543 * width of <code>visibleRect</code>, based on <code>orientation</code>.
3544 *
3545 * @param visibleRect the view area visible within the viewport
3546 * @param orientation either <code>SwingConstants.VERTICAL</code>
3547 * or <code>SwingConstants.HORIZONTAL</code>
3548 * @param direction less than zero to scroll up/left,
3549 * greater than zero for down/right.
3550 * @return the "block" increment for scrolling in the specified direction
3551 * @see JScrollBar#setBlockIncrement(int)
3552 */
3553 public int getScrollableBlockIncrement(Rectangle visibleRect,
3554 int orientation, int direction) {
3555 return (orientation == SwingConstants.VERTICAL) ? visibleRect.height :
3556 visibleRect.width;
3557 }
3558
3559 /**
3560 * Returns false to indicate that the width of the viewport does not
3561 * determine the width of the table, unless the preferred width of
3562 * the tree is smaller than the viewports width. In other words:
3563 * ensure that the tree is never smaller than its viewport.
3564 *
3565 * @return whether the tree should track the width of the viewport
3566 * @see Scrollable#getScrollableTracksViewportWidth
3567 */
3574 }
3575
3576 /**
3577 * Returns false to indicate that the height of the viewport does not
3578 * determine the height of the table, unless the preferred height
3579 * of the tree is smaller than the viewports height. In other words:
3580 * ensure that the tree is never smaller than its viewport.
3581 *
3582 * @return whether the tree should track the height of the viewport
3583 * @see Scrollable#getScrollableTracksViewportHeight
3584 */
3585 public boolean getScrollableTracksViewportHeight() {
3586 Container parent = SwingUtilities.getUnwrappedParent(this);
3587 if (parent instanceof JViewport) {
3588 return parent.getHeight() > getPreferredSize().height;
3589 }
3590 return false;
3591 }
3592
3593 /**
3594 * Sets the expanded state of this <code>JTree</code>.
3595 * If <code>state</code> is
3596 * true, all parents of <code>path</code> and path are marked as
3597 * expanded. If <code>state</code> is false, all parents of
3598 * <code>path</code> are marked EXPANDED, but <code>path</code> itself
3599 * is marked collapsed.<p>
3600 * This will fail if a <code>TreeWillExpandListener</code> vetos it.
3601 *
3602 * @param path a {@code TreePath} identifying a node
3603 * @param state if {@code true}, all parents of @{code path} and path are marked as expanded.
3604 * Otherwise, all parents of {@code path} are marked EXPANDED,
3605 * but {@code path} itself is marked collapsed.
3606 */
3607 protected void setExpandedState(TreePath path, boolean state) {
3608 if(path != null) {
3609 // Make sure all parents of path are expanded.
3610 Stack<TreePath> stack;
3611 TreePath parentPath = path.getParentPath();
3612
3613 if (expandedStack.size() == 0) {
3614 stack = new Stack<TreePath>();
3615 }
3616 else {
3617 stack = expandedStack.pop();
3618 }
3619
3620 try {
3706 * @return the {@code Enumeration} of {@code TreePaths}
3707 */
3708 protected Enumeration<TreePath>
3709 getDescendantToggledPaths(TreePath parent)
3710 {
3711 if(parent == null)
3712 return null;
3713
3714 Vector<TreePath> descendants = new Vector<TreePath>();
3715 Enumeration<TreePath> nodes = expandedState.keys();
3716
3717 while(nodes.hasMoreElements()) {
3718 TreePath path = nodes.nextElement();
3719 if(parent.isDescendant(path))
3720 descendants.addElement(path);
3721 }
3722 return descendants.elements();
3723 }
3724
3725 /**
3726 * Removes any descendants of the <code>TreePaths</code> in
3727 * <code>toRemove</code>
3728 * that have been expanded.
3729 *
3730 * @param toRemove an enumeration of the paths to remove; a value of
3731 * {@code null} is ignored
3732 * @throws ClassCastException if {@code toRemove} contains an
3733 * element that is not a {@code TreePath}; {@code null}
3734 * values are ignored
3735 */
3736 protected void
3737 removeDescendantToggledPaths(Enumeration<TreePath> toRemove)
3738 {
3739 if(toRemove != null) {
3740 while(toRemove.hasMoreElements()) {
3741 Enumeration<?> descendants = getDescendantToggledPaths
3742 (toRemove.nextElement());
3743
3744 if(descendants != null) {
3745 while(descendants.hasMoreElements()) {
3746 expandedState.remove(descendants.nextElement());
3747 }
3748 }
3749 }
3750 }
3751 }
3752
3753 /**
3754 * Clears the cache of toggled tree paths. This does NOT send out
3755 * any <code>TreeExpansionListener</code> events.
3756 */
3757 protected void clearToggledPaths() {
3758 expandedState.clear();
3759 }
3760
3761 /**
3762 * Creates and returns an instance of <code>TreeModelHandler</code>.
3763 * The returned
3764 * object is responsible for updating the expanded state when the
3765 * <code>TreeModel</code> changes.
3766 * <p>
3767 * For more information on what expanded state means, see the
3768 * <a href=#jtree_description>JTree description</a> above.
3769 *
3770 * @return the instance of {@code TreeModelHandler}
3771 */
3772 protected TreeModelListener createTreeModelListener() {
3773 return new TreeModelHandler();
3774 }
3775
3776 /**
3777 * Removes any paths in the selection that are descendants of
3778 * <code>path</code>. If <code>includePath</code> is true and
3779 * <code>path</code> is selected, it will be removed from the selection.
3780 *
3781 * @param path a path
3782 * @param includePath is {@code true} and {@code path} is selected,
3783 * it will be removed from the selection.
3784 * @return true if a descendant was selected
3785 * @since 1.3
3786 */
3787 protected boolean removeDescendantSelectedPaths(TreePath path,
3788 boolean includePath) {
3789 TreePath[] toRemove = getDescendantSelectedPaths(path, includePath);
3790
3791 if (toRemove != null) {
3792 getSelectionModel().removeSelectionPaths(toRemove);
3793 return true;
3794 }
3795 return false;
3796 }
3797
3798 /**
3799 * Returns an array of paths in the selection that are descendants of
3800 * <code>path</code>. The returned array may contain <code>null</code>s.
3801 */
3802 private TreePath[] getDescendantSelectedPaths(TreePath path,
3803 boolean includePath) {
3804 TreeSelectionModel sm = getSelectionModel();
3805 TreePath[] selPaths = (sm != null) ? sm.getSelectionPaths() :
3806 null;
3807
3808 if(selPaths != null) {
3809 boolean shouldRemove = false;
3810
3811 for(int counter = selPaths.length - 1; counter >= 0; counter--) {
3812 if(selPaths[counter] != null &&
3813 path.isDescendant(selPaths[counter]) &&
3814 (!path.equals(selPaths[counter]) || includePath))
3815 shouldRemove = true;
3816 else
3817 selPaths[counter] = null;
3818 }
3819 if(!shouldRemove) {
3820 selPaths = null;
3821 }
3822 return selPaths;
3823 }
3824 return null;
3825 }
3826
3827 /**
3828 * Removes any paths from the selection model that are descendants of
3829 * the nodes identified by in <code>e</code>.
3830 */
3831 void removeDescendantSelectedPaths(TreeModelEvent e) {
3832 TreePath pPath = SwingUtilities2.getTreePath(e, getModel());
3833 Object[] oldChildren = e.getChildren();
3834 TreeSelectionModel sm = getSelectionModel();
3835
3836 if (sm != null && pPath != null && oldChildren != null &&
3837 oldChildren.length > 0) {
3838 for (int counter = oldChildren.length - 1; counter >= 0;
3839 counter--) {
3840 // Might be better to call getDescendantSelectedPaths
3841 // numerous times, then push to the model.
3842 removeDescendantSelectedPaths(pPath.pathByAddingChild
3843 (oldChildren[counter]), true);
3844 }
3845 }
3846 }
3847
3848
3849 /**
3850 * Listens to the model and updates the <code>expandedState</code>
3851 * accordingly when nodes are removed, or changed.
3852 */
3853 protected class TreeModelHandler implements TreeModelListener {
3854 public void treeNodesChanged(TreeModelEvent e) { }
3855
3856 public void treeNodesInserted(TreeModelEvent e) { }
3857
3858 public void treeStructureChanged(TreeModelEvent e) {
3859 if(e == null)
3860 return;
3861
3862 // NOTE: If I change this to NOT remove the descendants
3863 // and update BasicTreeUIs treeStructureChanged method
3864 // to update descendants in response to a treeStructureChanged
3865 // event, all the children of the event won't collapse!
3866 TreePath parent = SwingUtilities2.getTreePath(e, getModel());
3867
3868 if(parent == null)
3869 return;
3870
3915
3916 for(int counter = children.length - 1; counter >= 0; counter--) {
3917 rPath = parent.pathByAddingChild(children[counter]);
3918 if(expandedState.get(rPath) != null)
3919 toRemove.addElement(rPath);
3920 }
3921 if(toRemove.size() > 0)
3922 removeDescendantToggledPaths(toRemove.elements());
3923
3924 TreeModel model = getModel();
3925
3926 if(model == null || model.isLeaf(parent.getLastPathComponent()))
3927 expandedState.remove(parent);
3928
3929 removeDescendantSelectedPaths(e);
3930 }
3931 }
3932
3933
3934 /**
3935 * <code>DynamicUtilTreeNode</code> can wrap
3936 * vectors/hashtables/arrays/strings and
3937 * create the appropriate children tree nodes as necessary. It is
3938 * dynamic in that it will only create the children as necessary.
3939 * <p>
3940 * <strong>Warning:</strong>
3941 * Serialized objects of this class will not be compatible with
3942 * future Swing releases. The current serialization support is
3943 * appropriate for short term storage or RMI between applications running
3944 * the same version of Swing. As of 1.4, support for long term storage
3945 * of all JavaBeans™
3946 * has been added to the <code>java.beans</code> package.
3947 * Please see {@link java.beans.XMLEncoder}.
3948 */
3949 @SuppressWarnings("serial")
3950 public static class DynamicUtilTreeNode extends DefaultMutableTreeNode {
3951 /**
3952 * Does the this <code>JTree</code> have children?
3953 * This property is currently not implemented.
3954 */
3955 protected boolean hasChildren;
3956 /** Value to create children with. */
3957 protected Object childValue;
3958 /** Have the children been loaded yet? */
3959 protected boolean loadedChildren;
3960
3961 /**
3962 * Adds to parent all the children in <code>children</code>.
3963 * If <code>children</code> is an array or vector all of its
3964 * elements are added is children, otherwise if <code>children</code>
3965 * is a hashtable all the key/value pairs are added in the order
3966 * <code>Enumeration</code> returns them.
3967 *
3968 * @param parent the parent node
3969 * @param children the children
3970 */
3971 public static void createChildren(DefaultMutableTreeNode parent,
3972 Object children) {
3973 if(children instanceof Vector) {
3974 Vector<?> childVector = (Vector)children;
3975
3976 for(int counter = 0, maxCounter = childVector.size();
3977 counter < maxCounter; counter++)
3978 parent.add(new DynamicUtilTreeNode
3979 (childVector.elementAt(counter),
3980 childVector.elementAt(counter)));
3981 }
3982 else if(children instanceof Hashtable) {
3983 Hashtable<?,?> childHT = (Hashtable)children;
3984 Enumeration<?> keys = childHT.keys();
3985 Object aKey;
3986
3987 while(keys.hasMoreElements()) {
3988 aKey = keys.nextElement();
3989 parent.add(new DynamicUtilTreeNode(aKey,
3990 childHT.get(aKey)));
3991 }
3992 }
3993 else if(children instanceof Object[]) {
3994 Object[] childArray = (Object[])children;
3995
3996 for(int counter = 0, maxCounter = childArray.length;
3997 counter < maxCounter; counter++)
3998 parent.add(new DynamicUtilTreeNode(childArray[counter],
3999 childArray[counter]));
4000 }
4001 }
4002
4003 /**
4004 * Creates a node with the specified object as its value and
4005 * with the specified children. For the node to allow children,
4006 * the children-object must be an array of objects, a
4007 * <code>Vector</code>, or a <code>Hashtable</code> -- even
4008 * if empty. Otherwise, the node is not
4009 * allowed to have children.
4010 *
4011 * @param value the <code>Object</code> that is the value for the
4012 * new node
4013 * @param children an array of <code>Object</code>s, a
4014 * <code>Vector</code>, or a <code>Hashtable</code>
4015 * used to create the child nodes; if any other
4016 * object is specified, or if the value is
4017 * <code>null</code>,
4018 * then the node is not allowed to have children
4019 */
4020 public DynamicUtilTreeNode(Object value, Object children) {
4021 super(value);
4022 loadedChildren = false;
4023 childValue = children;
4024 if(children != null) {
4025 if(children instanceof Vector)
4026 setAllowsChildren(true);
4027 else if(children instanceof Hashtable)
4028 setAllowsChildren(true);
4029 else if(children instanceof Object[])
4030 setAllowsChildren(true);
4031 else
4032 setAllowsChildren(false);
4033 }
4034 else
4035 setAllowsChildren(false);
4036 }
4037
4041 *
4042 * @return true if this node allows children, false otherwise
4043 * @see JTree.DynamicUtilTreeNode
4044 */
4045 public boolean isLeaf() {
4046 return !getAllowsChildren();
4047 }
4048
4049 /**
4050 * Returns the number of child nodes.
4051 *
4052 * @return the number of child nodes
4053 */
4054 public int getChildCount() {
4055 if(!loadedChildren)
4056 loadChildren();
4057 return super.getChildCount();
4058 }
4059
4060 /**
4061 * Loads the children based on <code>childValue</code>.
4062 * If <code>childValue</code> is a <code>Vector</code>
4063 * or array each element is added as a child,
4064 * if <code>childValue</code> is a <code>Hashtable</code>
4065 * each key/value pair is added in the order that
4066 * <code>Enumeration</code> returns the keys.
4067 */
4068 protected void loadChildren() {
4069 loadedChildren = true;
4070 createChildren(this, childValue);
4071 }
4072
4073 /**
4074 * Subclassed to load the children, if necessary.
4075 */
4076 public TreeNode getChildAt(int index) {
4077 if(!loadedChildren)
4078 loadChildren();
4079 return super.getChildAt(index);
4080 }
4081
4082 /**
4083 * Subclassed to load the children, if necessary.
4084 */
4085 public Enumeration<TreeNode> children() {
4086 if(!loadedChildren)
4095 setRowHeight(((Number)value).intValue());
4096 rowHeightSet = false;
4097 }
4098 } else if (propertyName == "scrollsOnExpand") {
4099 if (!scrollsOnExpandSet) {
4100 setScrollsOnExpand(((Boolean)value).booleanValue());
4101 scrollsOnExpandSet = false;
4102 }
4103 } else if (propertyName == "showsRootHandles") {
4104 if (!showsRootHandlesSet) {
4105 setShowsRootHandles(((Boolean)value).booleanValue());
4106 showsRootHandlesSet = false;
4107 }
4108 } else {
4109 super.setUIProperty(propertyName, value);
4110 }
4111 }
4112
4113
4114 /**
4115 * Returns a string representation of this <code>JTree</code>.
4116 * This method
4117 * is intended to be used only for debugging purposes, and the
4118 * content and format of the returned string may vary between
4119 * implementations. The returned string may be empty but may not
4120 * be <code>null</code>.
4121 *
4122 * @return a string representation of this <code>JTree</code>.
4123 */
4124 protected String paramString() {
4125 String rootVisibleString = (rootVisible ?
4126 "true" : "false");
4127 String showsRootHandlesString = (showsRootHandles ?
4128 "true" : "false");
4129 String editableString = (editable ?
4130 "true" : "false");
4131 String largeModelString = (largeModel ?
4132 "true" : "false");
4133 String invokesStopCellEditingString = (invokesStopCellEditing ?
4134 "true" : "false");
4135 String scrollsOnExpandString = (scrollsOnExpand ?
4136 "true" : "false");
4137
4138 return super.paramString() +
4139 ",editable=" + editableString +
4140 ",invokesStopCellEditing=" + invokesStopCellEditingString +
4141 ",largeModel=" + largeModelString +
4142 ",rootVisible=" + rootVisibleString +
4152 ////////////////
4153
4154 /**
4155 * Gets the AccessibleContext associated with this JTree.
4156 * For JTrees, the AccessibleContext takes the form of an
4157 * AccessibleJTree.
4158 * A new AccessibleJTree instance is created if necessary.
4159 *
4160 * @return an AccessibleJTree that serves as the
4161 * AccessibleContext of this JTree
4162 */
4163 public AccessibleContext getAccessibleContext() {
4164 if (accessibleContext == null) {
4165 accessibleContext = new AccessibleJTree();
4166 }
4167 return accessibleContext;
4168 }
4169
4170 /**
4171 * This class implements accessibility support for the
4172 * <code>JTree</code> class. It provides an implementation of the
4173 * Java Accessibility API appropriate to tree user-interface elements.
4174 * <p>
4175 * <strong>Warning:</strong>
4176 * Serialized objects of this class will not be compatible with
4177 * future Swing releases. The current serialization support is
4178 * appropriate for short term storage or RMI between applications running
4179 * the same version of Swing. As of 1.4, support for long term storage
4180 * of all JavaBeans™
4181 * has been added to the <code>java.beans</code> package.
4182 * Please see {@link java.beans.XMLEncoder}.
4183 */
4184 @SuppressWarnings("serial")
4185 protected class AccessibleJTree extends AccessibleJComponent
4186 implements AccessibleSelection, TreeSelectionListener,
4187 TreeModelListener, TreeExpansionListener {
4188
4189 TreePath leadSelectionPath;
4190 Accessible leadSelectionAccessible;
4191
4192 /**
4193 * Constructs {@code AccessibleJTree}
4194 */
4195 public AccessibleJTree() {
4196 // Add a tree model listener for JTree
4197 TreeModel model = JTree.this.getModel();
4198 if (model != null) {
4199 model.addTreeModelListener(this);
4200 }
4201 JTree.this.addTreeExpansionListener(this);
4387 model.isLeaf(treeRoot), row, hasFocus);
4388 }
4389 }
4390 return null;
4391 }
4392
4393 // Overridden methods from AccessibleJComponent
4394
4395 /**
4396 * Get the role of this object.
4397 *
4398 * @return an instance of AccessibleRole describing the role of the
4399 * object
4400 * @see AccessibleRole
4401 */
4402 public AccessibleRole getAccessibleRole() {
4403 return AccessibleRole.TREE;
4404 }
4405
4406 /**
4407 * Returns the <code>Accessible</code> child, if one exists,
4408 * contained at the local coordinate <code>Point</code>.
4409 * Otherwise returns <code>null</code>.
4410 *
4411 * @param p point in local coordinates of this <code>Accessible</code>
4412 * @return the <code>Accessible</code>, if it exists,
4413 * at the specified location; else <code>null</code>
4414 */
4415 public Accessible getAccessibleAt(Point p) {
4416 TreePath path = getClosestPathForLocation(p.x, p.y);
4417 if (path != null) {
4418 // JTree.this is NOT the parent; parent will get computed later
4419 return new AccessibleJTreeNode(JTree.this, path, null);
4420 } else {
4421 return null;
4422 }
4423 }
4424
4425 /**
4426 * Returns the number of top-level children nodes of this
4427 * JTree. Each of these nodes may in turn have children nodes.
4428 *
4429 * @return the number of accessible children nodes in the tree.
4430 */
4431 public int getAccessibleChildrenCount() {
4432 TreeModel model = JTree.this.getModel();
4433 if (model == null) {
4634 }
4635
4636 /**
4637 * Causes every selected item in the object to be selected
4638 * if the object supports multiple selections.
4639 */
4640 public void selectAllAccessibleSelection() {
4641 TreeModel model = JTree.this.getModel();
4642 if (model != null) {
4643 Object[] objPath = {model.getRoot()};
4644 if (objPath[0] == null)
4645 return;
4646
4647 TreePath path = new TreePath(objPath);
4648 JTree.this.addSelectionPath(path);
4649 }
4650 }
4651
4652 /**
4653 * This class implements accessibility support for the
4654 * <code>JTree</code> child. It provides an implementation of the
4655 * Java Accessibility API appropriate to tree nodes.
4656 */
4657 protected class AccessibleJTreeNode extends AccessibleContext
4658 implements Accessible, AccessibleComponent, AccessibleSelection,
4659 AccessibleAction {
4660
4661 private JTree tree = null;
4662 private TreeModel treeModel = null;
4663 private Object obj = null;
4664 private TreePath path = null;
4665 private Accessible accessibleParent = null;
4666 private int index = 0;
4667 private boolean isLeaf = false;
4668
4669 /**
4670 * Constructs an AccessibleJTreeNode
4671 *
4672 * @param t an instance of {@code JTree}
4673 * @param p an instance of {@code TreePath}
4674 * @param ap an instance of {@code Accessible}
5357 }
5358 }
5359
5360 public Dimension getSize() {
5361 return getBounds().getSize();
5362 }
5363
5364 public void setSize (Dimension d) {
5365 AccessibleContext ac = getCurrentAccessibleContext();
5366 if (ac instanceof AccessibleComponent) {
5367 ((AccessibleComponent) ac).setSize(d);
5368 } else {
5369 Component c = getCurrentComponent();
5370 if (c != null) {
5371 c.setSize(d);
5372 }
5373 }
5374 }
5375
5376 /**
5377 * Returns the <code>Accessible</code> child, if one exists,
5378 * contained at the local coordinate <code>Point</code>.
5379 * Otherwise returns <code>null</code>.
5380 *
5381 * @param p point in local coordinates of this
5382 * <code>Accessible</code>
5383 * @return the <code>Accessible</code>, if it exists,
5384 * at the specified location; else <code>null</code>
5385 */
5386 public Accessible getAccessibleAt(Point p) {
5387 AccessibleContext ac = getCurrentAccessibleContext();
5388 if (ac instanceof AccessibleComponent) {
5389 return ((AccessibleComponent) ac).getAccessibleAt(p);
5390 } else {
5391 return null;
5392 }
5393 }
5394
5395 @SuppressWarnings("deprecation")
5396 public boolean isFocusTraversable() {
5397 AccessibleContext ac = getCurrentAccessibleContext();
5398 if (ac instanceof AccessibleComponent) {
5399 return ((AccessibleComponent) ac).isFocusTraversable();
5400 } else {
5401 Component c = getCurrentComponent();
5402 if (c != null) {
5403 return c.isFocusTraversable();
5404 } else {
|
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"></a>
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://docs.oracle.com/javase/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} (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)} 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} methods use "visible" to mean "displayed":
64 * <ul>
65 * <li>{@code isRootVisible()}
66 * <li>{@code setRootVisible()}
67 * <li>{@code scrollPathToVisible()}
68 * <li>{@code scrollRowToVisible()}
69 * <li>{@code getVisibleRowCount()}
70 * <li>{@code setVisibleRowCount()}
71 * </ul>
72 * The next group of {@code JTree} methods use "visible" to mean
73 * "viewable" (under an expanded parent):
74 * <ul>
75 * <li>{@code isVisible()}
76 * <li>{@code makeVisible()}
77 * </ul>
78 * If you are interested in knowing when the selection changes implement
79 * the {@code TreeSelectionListener} interface and add the instance
80 * using the method {@code addTreeSelectionListener}.
81 * {@code valueChanged} will be invoked when the
82 * selection changes, that is if the user clicks twice on the same
83 * node {@code valueChanged} will only be invoked once.
84 * <p>
85 * If you are interested in detecting either double-click events or when
86 * a user clicks on a node, regardless of whether or not it was selected,
87 * we recommend you do the following:
88 * </p>
89 * <pre>
90 * final JTree tree = ...;
91 *
92 * MouseListener ml = new MouseAdapter() {
93 * public void <b>mousePressed</b>(MouseEvent e) {
94 * int selRow = tree.getRowForLocation(e.getX(), e.getY());
95 * TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
96 * if(selRow != -1) {
97 * if(e.getClickCount() == 1) {
98 * mySingleClick(selRow, selPath);
99 * }
100 * else if(e.getClickCount() == 2) {
101 * myDoubleClick(selRow, selPath);
102 * }
103 * }
104 * }
105 * };
106 * tree.addMouseListener(ml);
107 * </pre>
108 * NOTE: This example obtains both the path and row, but you only need to
109 * get the one you're interested in.
110 * <p>
111 * To use {@code JTree} to display compound nodes
112 * (for example, nodes containing both
113 * a graphic icon and text), subclass {@link TreeCellRenderer} and use
114 * {@link #setCellRenderer} to tell the tree to use it. To edit such nodes,
115 * subclass {@link TreeCellEditor} and use {@link #setCellEditor}.
116 * </p>
117 * <p>
118 * Like all {@code JComponent} 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™
134 * has been added to the {@code java.beans} package.
135 * Please see {@link java.beans.XMLEncoder}.
136 *</p>
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 * @since 1.2
145 */
146 @SuppressWarnings("serial")
147 public class JTree extends JComponent implements Scrollable, Accessible
148 {
149 /**
150 * @see #getUIClassID
151 * @see #readObject
152 */
153 private static final String uiClassID = "TreeUI";
154
155 /**
156 * The model that defines the tree displayed by this object.
157 */
158 protected transient TreeModel treeModel;
159
160 /**
161 * Models the set of selected nodes in this tree.
162 */
163 protected transient TreeSelectionModel selectionModel;
164
165 /**
166 * True if the root node is displayed, false if its children are
167 * the highest visible nodes.
168 */
169 protected boolean rootVisible;
170
171 /**
172 * The cell used to draw nodes. If {@code null}, the UI uses a default
173 * {@code cellRenderer}.
174 */
175 protected transient TreeCellRenderer cellRenderer;
176
177 /**
178 * Height to use for each display row. If this is <= 0 the renderer
179 * determines the height for each row.
180 */
181 protected int rowHeight;
182 private boolean rowHeightSet = false;
183
184 /**
185 * Maps from {@code TreePath} to {@code Boolean}
186 * indicating whether or not the
187 * particular path is expanded. This ONLY indicates whether a
188 * given path is expanded, and NOT if it is visible or not. That
189 * information must be determined by visiting all the parent
190 * paths and seeing if they are visible.
191 */
192 private transient Hashtable<TreePath, Boolean> expandedState;
193
194
195 /**
196 * True if handles are displayed at the topmost level of the tree.
197 * <p>
198 * A handle is a small icon that displays adjacent to the node which
199 * allows the user to click once to expand or collapse the node. A
200 * common interface shows a plus sign (+) for a node which can be
201 * expanded and a minus sign (-) for a node which can be collapsed.
202 * Handles are always shown for nodes below the topmost level.
203 * <p>
204 * If the {@code rootVisible} setting specifies that the root
205 * node is to be displayed, then that is the only node at the topmost
206 * level. If the root node is not displayed, then all of its
207 * children are at the topmost level of the tree. Handles are
208 * always displayed for nodes other than the topmost.
209 * <p>
210 * If the root node isn't visible, it is generally a good to make
211 * this value true. Otherwise, the tree looks exactly like a list,
212 * and users may not know that the "list entries" are actually
213 * tree nodes.
214 *
215 * @see #rootVisible
216 */
217 protected boolean showsRootHandles;
218 private boolean showsRootHandlesSet = false;
219
220 /**
221 * Creates a new event and passed it off the
222 * {@code selectionListeners}.
223 */
224 protected transient TreeSelectionRedirector selectionRedirector;
225
226 /**
227 * Editor for the entries. Default is {@code null}
228 * (tree is not editable).
229 */
230 protected transient TreeCellEditor cellEditor;
231
232 /**
233 * Is the tree editable? Default is false.
234 */
235 protected boolean editable;
236
237 /**
238 * Is this tree a large model? This is a code-optimization setting.
239 * A large model can be used when the cell height is the same for all
240 * nodes. The UI will then cache very little information and instead
241 * continually message the model. Without a large model the UI caches
242 * most of the information, resulting in fewer method calls to the model.
243 * <p>
244 * This value is only a suggestion to the UI. Not all UIs will
245 * take advantage of it. Default value is false.
246 */
247 protected boolean largeModel;
248
249 /**
250 * Number of rows to make visible at one time. This value is used for
251 * the {@code Scrollable} interface. It determines the preferred
252 * size of the display area.
253 */
254 protected int visibleRowCount;
255
256 /**
257 * If true, when editing is to be stopped by way of selection changing,
258 * data in tree changing or other means {@code stopCellEditing}
259 * is invoked, and changes are saved. If false,
260 * {@code cancelCellEditing} is invoked, and changes
261 * are discarded. Default is false.
262 */
263 protected boolean invokesStopCellEditing;
264
265 /**
266 * If true, when a node is expanded, as many of the descendants are
267 * scrolled to be visible.
268 */
269 protected boolean scrollsOnExpand;
270 private boolean scrollsOnExpandSet = false;
271
272 /**
273 * Number of mouse clicks before a node is expanded.
274 */
275 protected int toggleClickCount;
276
277 /**
278 * Updates the {@code expandedState}.
279 */
280 protected transient TreeModelListener treeModelListener;
281
282 /**
283 * Used when {@code setExpandedState} is invoked,
284 * will be a {@code Stack} of {@code Stack}s.
285 */
286 private transient Stack<Stack<TreePath>> expandedStack;
287
288 /**
289 * Lead selection path, may not be {@code null}.
290 */
291 private TreePath leadPath;
292
293 /**
294 * Anchor path.
295 */
296 private TreePath anchorPath;
297
298 /**
299 * True if paths in the selection should be expanded.
300 */
301 private boolean expandsSelectedPaths;
302
303 /**
304 * This is set to true for the life of the {@code setUI} call.
305 */
306 private boolean settingUI;
307
308 /** If true, mouse presses on selections initiate a drag operation. */
309 private boolean dragEnabled;
310
311 /**
312 * The drop mode for this component.
313 */
314 private DropMode dropMode = DropMode.USE_SELECTION;
315
316 /**
317 * The drop location.
318 */
319 private transient DropLocation dropLocation;
320
321 /**
322 * A subclass of {@code TransferHandler.DropLocation} representing
323 * a drop location for a {@code JTree}.
324 *
325 * @see #getDropLocation
326 * @since 1.6
327 */
328 public static final class DropLocation extends TransferHandler.DropLocation {
329 private final TreePath path;
330 private final int index;
331
332 private DropLocation(Point p, TreePath path, int index) {
333 super(p);
334 this.path = path;
335 this.index = index;
336 }
337
338 /**
339 * Returns the index where the dropped data should be inserted
340 * with respect to the path returned by {@code getPath()}.
341 * <p>
342 * For drop modes {@code DropMode.USE_SELECTION} and
343 * {@code DropMode.ON}, this index is unimportant (and it will
344 * always be {@code -1}) as the only interesting data is the
345 * path over which the drop operation occurred.
346 * <p>
347 * For drop mode {@code DropMode.INSERT}, this index
348 * indicates the index at which the data should be inserted into
349 * the parent path represented by {@code getPath()}.
350 * {@code -1} indicates that the drop occurred over the
351 * parent itself, and in most cases should be treated as inserting
352 * into either the beginning or the end of the parent's list of
353 * children.
354 * <p>
355 * For {@code DropMode.ON_OR_INSERT}, this value will be
356 * an insert index, as described above, or {@code -1} if
357 * the drop occurred over the path itself.
358 *
359 * @return the child index
360 * @see #getPath
361 */
362 public int getChildIndex() {
363 return index;
364 }
365
366 /**
367 * Returns the path where dropped data should be placed in the
368 * tree.
369 * <p>
370 * Interpretation of this value depends on the drop mode set on the
371 * component. If the drop mode is {@code DropMode.USE_SELECTION}
372 * or {@code DropMode.ON}, the return value is the path in the
373 * tree over which the data has been (or will be) dropped.
374 * {@code null} indicates that the drop is over empty space,
375 * not associated with a particular path.
376 * <p>
377 * If the drop mode is {@code DropMode.INSERT}, the return value
378 * refers to the path that should become the parent of the new data,
379 * in which case {@code getChildIndex()} indicates where the
380 * new item should be inserted into this parent path. A
381 * {@code null} path indicates that no parent path has been
382 * determined, which can happen for multiple reasons:
383 * <ul>
384 * <li>The tree has no model
385 * <li>There is no root in the tree
386 * <li>The root is collapsed
387 * <li>The root is a leaf node
388 * </ul>
389 * It is up to the developer to decide if and how they wish to handle
390 * the {@code null} case.
391 * <p>
392 * If the drop mode is {@code DropMode.ON_OR_INSERT},
393 * {@code getChildIndex} can be used to determine whether the
394 * drop is on top of the path itself ({@code -1}) or the index
395 * at which it should be inserted into the path (values other than
396 * {@code -1}).
397 *
398 * @return the drop path
399 * @see #getChildIndex
400 */
401 public TreePath getPath() {
402 return path;
403 }
404
405 /**
406 * Returns a string representation of this drop location.
407 * This method is intended to be used for debugging purposes,
408 * and the content and format of the returned string may vary
409 * between implementations.
410 *
411 * @return a string representation of this drop location
412 */
413 public String toString() {
414 return getClass().getName()
415 + "[dropPoint=" + getDropPoint() + ","
416 + "path=" + path + ","
424 private int expandRow = -1;
425
426 @SuppressWarnings("serial")
427 private class TreeTimer extends Timer {
428 public TreeTimer() {
429 super(2000, null);
430 setRepeats(false);
431 }
432
433 public void fireActionPerformed(ActionEvent ae) {
434 JTree.this.expandRow(expandRow);
435 }
436 }
437
438 /**
439 * A timer to expand nodes during drop.
440 */
441 private TreeTimer dropTimer;
442
443 /**
444 * When {@code addTreeExpansionListener} is invoked,
445 * and {@code settingUI} is true, this ivar gets set to the passed in
446 * {@code Listener}. This listener is then notified first in
447 * {@code fireTreeCollapsed} and {@code fireTreeExpanded}.
448 * <p>This is an ugly workaround for a way to have the UI listener
449 * get notified before other listeners.
450 */
451 private transient TreeExpansionListener uiTreeExpansionListener;
452
453 /**
454 * Max number of stacks to keep around.
455 */
456 private static int TEMP_STACK_SIZE = 11;
457
458 //
459 // Bound property names
460 //
461 /** Bound property name for {@code cellRenderer}. */
462 public static final String CELL_RENDERER_PROPERTY = "cellRenderer";
463 /** Bound property name for {@code treeModel}. */
464 public static final String TREE_MODEL_PROPERTY = "model";
465 /** Bound property name for {@code rootVisible}. */
466 public static final String ROOT_VISIBLE_PROPERTY = "rootVisible";
467 /** Bound property name for {@code showsRootHandles}. */
468 public static final String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
469 /** Bound property name for {@code rowHeight}. */
470 public static final String ROW_HEIGHT_PROPERTY = "rowHeight";
471 /** Bound property name for {@code cellEditor}. */
472 public static final String CELL_EDITOR_PROPERTY = "cellEditor";
473 /** Bound property name for {@code editable}. */
474 public static final String EDITABLE_PROPERTY = "editable";
475 /** Bound property name for {@code largeModel}. */
476 public static final String LARGE_MODEL_PROPERTY = "largeModel";
477 /** Bound property name for selectionModel. */
478 public static final String SELECTION_MODEL_PROPERTY = "selectionModel";
479 /** Bound property name for {@code visibleRowCount}. */
480 public static final String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
481 /** Bound property name for {@code messagesStopCellEditing}. */
482 public static final String INVOKES_STOP_CELL_EDITING_PROPERTY = "invokesStopCellEditing";
483 /** Bound property name for {@code scrollsOnExpand}. */
484 public static final String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
485 /** Bound property name for {@code toggleClickCount}. */
486 public static final String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
487 /** Bound property name for {@code leadSelectionPath}.
488 * @since 1.3 */
489 public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
490 /** Bound property name for anchor selection path.
491 * @since 1.3 */
492 public static final String ANCHOR_SELECTION_PATH_PROPERTY = "anchorSelectionPath";
493 /** Bound property name for expands selected paths property
494 * @since 1.3 */
495 public static final String EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths";
496
497
498 /**
499 * Creates and returns a sample {@code TreeModel}.
500 * Used primarily for beanbuilders to show something interesting.
501 *
502 * @return the default {@code TreeModel}
503 */
504 protected static TreeModel getDefaultTreeModel() {
505 DefaultMutableTreeNode root = new DefaultMutableTreeNode("JTree");
506 DefaultMutableTreeNode parent;
507
508 parent = new DefaultMutableTreeNode("colors");
509 root.add(parent);
510 parent.add(new DefaultMutableTreeNode("blue"));
511 parent.add(new DefaultMutableTreeNode("violet"));
512 parent.add(new DefaultMutableTreeNode("red"));
513 parent.add(new DefaultMutableTreeNode("yellow"));
514
515 parent = new DefaultMutableTreeNode("sports");
516 root.add(parent);
517 parent.add(new DefaultMutableTreeNode("basketball"));
518 parent.add(new DefaultMutableTreeNode("soccer"));
519 parent.add(new DefaultMutableTreeNode("football"));
520 parent.add(new DefaultMutableTreeNode("hockey"));
521
522 parent = new DefaultMutableTreeNode("food");
523 root.add(parent);
524 parent.add(new DefaultMutableTreeNode("hot dogs"));
525 parent.add(new DefaultMutableTreeNode("pizza"));
526 parent.add(new DefaultMutableTreeNode("ravioli"));
527 parent.add(new DefaultMutableTreeNode("bananas"));
528 return new DefaultTreeModel(root);
529 }
530
531 /**
532 * Returns a {@code TreeModel} wrapping the specified object.
533 * If the object is:<ul>
534 * <li>an array of {@code Object}s,
535 * <li>a {@code Hashtable}, or
536 * <li>a {@code Vector}
537 * </ul>then a new root node is created with each of the incoming
538 * objects as children. Otherwise, a new root is created with
539 * a value of {@code "root"}.
540 *
541 * @param value the {@code Object} used as the foundation for
542 * the {@code TreeModel}
543 * @return a {@code TreeModel} wrapping the specified object
544 */
545 protected static TreeModel createTreeModel(Object value) {
546 DefaultMutableTreeNode root;
547
548 if((value instanceof Object[]) || (value instanceof Hashtable) ||
549 (value instanceof Vector)) {
550 root = new DefaultMutableTreeNode("root");
551 DynamicUtilTreeNode.createChildren(root, value);
552 }
553 else {
554 root = new DynamicUtilTreeNode("root", value);
555 }
556 return new DefaultTreeModel(root, false);
557 }
558
559 /**
560 * Returns a {@code JTree} with a sample model.
561 * The default model used by the tree defines a leaf node as any node
562 * without children.
563 *
564 * @see DefaultTreeModel#asksAllowsChildren
565 */
566 public JTree() {
567 this(getDefaultTreeModel());
568 }
569
570 /**
571 * Returns a {@code JTree} with each element of the
572 * specified array as the
573 * child of a new root node which is not displayed.
574 * By default, the tree defines a leaf node as any node without
575 * children.
576 *
577 * @param value an array of {@code Object}s
578 * @see DefaultTreeModel#asksAllowsChildren
579 */
580 public JTree(Object[] value) {
581 this(createTreeModel(value));
582 this.setRootVisible(false);
583 this.setShowsRootHandles(true);
584 expandRoot();
585 }
586
587 /**
588 * Returns a {@code JTree} with each element of the specified
589 * {@code Vector} as the
590 * child of a new root node which is not displayed. By default, the
591 * tree defines a leaf node as any node without children.
592 *
593 * @param value a {@code Vector}
594 * @see DefaultTreeModel#asksAllowsChildren
595 */
596 public JTree(Vector<?> value) {
597 this(createTreeModel(value));
598 this.setRootVisible(false);
599 this.setShowsRootHandles(true);
600 expandRoot();
601 }
602
603 /**
604 * Returns a {@code JTree} created from a {@code Hashtable}
605 * which does not display with root.
606 * Each value-half of the key/value pairs in the {@code HashTable}
607 * becomes a child of the new root node. By default, the tree defines
608 * a leaf node as any node without children.
609 *
610 * @param value a {@code Hashtable}
611 * @see DefaultTreeModel#asksAllowsChildren
612 */
613 public JTree(Hashtable<?,?> value) {
614 this(createTreeModel(value));
615 this.setRootVisible(false);
616 this.setShowsRootHandles(true);
617 expandRoot();
618 }
619
620 /**
621 * Returns a {@code JTree} with the specified
622 * {@code TreeNode} as its root,
623 * which displays the root node.
624 * By default, the tree defines a leaf node as any node without children.
625 *
626 * @param root a {@code TreeNode} object
627 * @see DefaultTreeModel#asksAllowsChildren
628 */
629 public JTree(TreeNode root) {
630 this(root, false);
631 }
632
633 /**
634 * Returns a {@code JTree} with the specified {@code TreeNode}
635 * as its root, which
636 * displays the root node and which decides whether a node is a
637 * leaf node in the specified manner.
638 *
639 * @param root a {@code TreeNode} object
640 * @param asksAllowsChildren if false, any node without children is a
641 * leaf node; if true, only nodes that do not allow
642 * children are leaf nodes
643 * @see DefaultTreeModel#asksAllowsChildren
644 */
645 public JTree(TreeNode root, boolean asksAllowsChildren) {
646 this(new DefaultTreeModel(root, asksAllowsChildren));
647 }
648
649 /**
650 * Returns an instance of {@code JTree} which displays the root node
651 * -- the tree is created using the specified data model.
652 *
653 * @param newModel the {@code TreeModel} to use as the data model
654 */
655 @ConstructorProperties({"model"})
656 public JTree(TreeModel newModel) {
657 super();
658 expandedStack = new Stack<Stack<TreePath>>();
659 toggleClickCount = 2;
660 expandedState = new Hashtable<TreePath, Boolean>();
661 setLayout(null);
662 rowHeight = 16;
663 visibleRowCount = 20;
664 rootVisible = true;
665 selectionModel = new DefaultTreeSelectionModel();
666 cellRenderer = null;
667 scrollsOnExpand = true;
668 setOpaque(true);
669 expandsSelectedPaths = true;
670 updateUI();
671 setModel(newModel);
672 }
673
674 /**
675 * Returns the L&F object that renders this component.
676 *
677 * @return the {@code TreeUI} object that renders this component
678 */
679 public TreeUI getUI() {
680 return (TreeUI)ui;
681 }
682
683 /**
684 * Sets the L&F object that renders this component.
685 * <p>
686 * This is a bound property.
687 *
688 * @param ui the {@code TreeUI} L&F object
689 * @see UIDefaults#getUI
690 * @beaninfo
691 * bound: true
692 * hidden: true
693 * attribute: visualUpdate true
694 * description: The UI object that implements the Component's LookAndFeel.
695 */
696 public void setUI(TreeUI ui) {
697 if (this.ui != ui) {
698 settingUI = true;
699 uiTreeExpansionListener = null;
700 try {
701 super.setUI(ui);
702 }
703 finally {
704 settingUI = false;
705 }
706 }
707 }
708
709 /**
710 * Notification from the {@code UIManager} that the L&F has changed.
711 * Replaces the current UI object with the latest version from the
712 * {@code UIManager}.
713 *
714 * @see JComponent#updateUI
715 */
716 public void updateUI() {
717 setUI((TreeUI)UIManager.getUI(this));
718
719 SwingUtilities.updateRendererOrEditorUI(getCellRenderer());
720 SwingUtilities.updateRendererOrEditorUI(getCellEditor());
721 }
722
723
724 /**
725 * Returns the name of the L&F class that renders this component.
726 *
727 * @return the string "TreeUI"
728 * @see JComponent#getUIClassID
729 * @see UIDefaults#getUI
730 */
731 public String getUIClassID() {
732 return uiClassID;
733 }
734
735
736 /**
737 * Returns the current {@code TreeCellRenderer}
738 * that is rendering each cell.
739 *
740 * @return the {@code TreeCellRenderer} that is rendering each cell
741 */
742 public TreeCellRenderer getCellRenderer() {
743 return cellRenderer;
744 }
745
746 /**
747 * Sets the {@code TreeCellRenderer} that will be used to
748 * draw each cell.
749 * <p>
750 * This is a bound property.
751 *
752 * @param x the {@code TreeCellRenderer} that is to render each cell
753 * @beaninfo
754 * bound: true
755 * description: The TreeCellRenderer that will be used to draw
756 * each cell.
757 */
758 public void setCellRenderer(TreeCellRenderer x) {
759 TreeCellRenderer oldValue = cellRenderer;
760
761 cellRenderer = x;
762 firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, cellRenderer);
763 invalidate();
764 }
765
766 /**
767 * Determines whether the tree is editable. Fires a property
768 * change event if the new setting is different from the existing
769 * setting.
770 * <p>
771 * This is a bound property.
772 *
781 this.editable = flag;
782 firePropertyChange(EDITABLE_PROPERTY, oldValue, flag);
783 if (accessibleContext != null) {
784 accessibleContext.firePropertyChange(
785 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
786 (oldValue ? AccessibleState.EDITABLE : null),
787 (flag ? AccessibleState.EDITABLE : null));
788 }
789 }
790
791 /**
792 * Returns true if the tree is editable.
793 *
794 * @return true if the tree is editable
795 */
796 public boolean isEditable() {
797 return editable;
798 }
799
800 /**
801 * Sets the cell editor. A {@code null} value implies that the
802 * tree cannot be edited. If this represents a change in the
803 * {@code cellEditor}, the {@code propertyChange}
804 * method is invoked on all listeners.
805 * <p>
806 * This is a bound property.
807 *
808 * @param cellEditor the {@code TreeCellEditor} to use
809 * @beaninfo
810 * bound: true
811 * description: The cell editor. A null value implies the tree
812 * cannot be edited.
813 */
814 public void setCellEditor(TreeCellEditor cellEditor) {
815 TreeCellEditor oldEditor = this.cellEditor;
816
817 this.cellEditor = cellEditor;
818 firePropertyChange(CELL_EDITOR_PROPERTY, oldEditor, cellEditor);
819 invalidate();
820 }
821
822 /**
823 * Returns the editor used to edit entries in the tree.
824 *
825 * @return the {@code TreeCellEditor} in use,
826 * or {@code null} if the tree cannot be edited
827 */
828 public TreeCellEditor getCellEditor() {
829 return cellEditor;
830 }
831
832 /**
833 * Returns the {@code TreeModel} that is providing the data.
834 *
835 * @return the {@code TreeModel} that is providing the data
836 */
837 public TreeModel getModel() {
838 return treeModel;
839 }
840
841 /**
842 * Sets the {@code TreeModel} that will provide the data.
843 * <p>
844 * This is a bound property.
845 *
846 * @param newModel the {@code TreeModel} that is to provide the data
847 * @beaninfo
848 * bound: true
849 * description: The TreeModel that will provide the data.
850 */
851 public void setModel(TreeModel newModel) {
852 clearSelection();
853
854 TreeModel oldModel = treeModel;
855
856 if(treeModel != null && treeModelListener != null)
857 treeModel.removeTreeModelListener(treeModelListener);
858
859 if (accessibleContext != null) {
860 if (treeModel != null) {
861 treeModel.removeTreeModelListener((TreeModelListener)accessibleContext);
862 }
863 if (newModel != null) {
864 newModel.addTreeModelListener((TreeModelListener)accessibleContext);
865 }
866 }
880 expandedState.put(new TreePath(treeRoot),
881 Boolean.TRUE);
882 }
883 }
884 firePropertyChange(TREE_MODEL_PROPERTY, oldModel, treeModel);
885 invalidate();
886 }
887
888 /**
889 * Returns true if the root node of the tree is displayed.
890 *
891 * @return true if the root node of the tree is displayed
892 * @see #rootVisible
893 */
894 public boolean isRootVisible() {
895 return rootVisible;
896 }
897
898 /**
899 * Determines whether or not the root node from
900 * the {@code TreeModel} is visible.
901 * <p>
902 * This is a bound property.
903 *
904 * @param rootVisible true if the root node of the tree is to be displayed
905 * @see #rootVisible
906 * @beaninfo
907 * bound: true
908 * description: Whether or not the root node
909 * from the TreeModel is visible.
910 */
911 public void setRootVisible(boolean rootVisible) {
912 boolean oldValue = this.rootVisible;
913
914 this.rootVisible = rootVisible;
915 firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, this.rootVisible);
916 if (accessibleContext != null) {
917 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
918 }
919 }
920
921 /**
922 * Sets the value of the {@code showsRootHandles} property,
923 * which specifies whether the node handles should be displayed.
924 * The default value of this property depends on the constructor
925 * used to create the {@code JTree}.
926 * Some look and feels might not support handles;
927 * they will ignore this property.
928 * <p>
929 * This is a bound property.
930 *
931 * @param newValue {@code true} if root handles should be displayed;
932 * otherwise, {@code false}
933 * @see #showsRootHandles
934 * @see #getShowsRootHandles
935 * @beaninfo
936 * bound: true
937 * description: Whether the node handles are to be
938 * displayed.
939 */
940 public void setShowsRootHandles(boolean newValue) {
941 boolean oldValue = showsRootHandles;
942 TreeModel model = getModel();
943
944 showsRootHandles = newValue;
945 showsRootHandlesSet = true;
946 firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue,
947 showsRootHandles);
948 if (accessibleContext != null) {
949 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
950 }
951 invalidate();
952 }
953
954 /**
955 * Returns the value of the {@code showsRootHandles} property.
956 *
957 * @return the value of the {@code showsRootHandles} property
958 * @see #showsRootHandles
959 */
960 public boolean getShowsRootHandles()
961 {
962 return showsRootHandles;
963 }
964
965 /**
966 * Sets the height of each cell, in pixels. If the specified value
967 * is less than or equal to zero the current cell renderer is
968 * queried for each row's height.
969 * <p>
970 * This is a bound property.
971 *
972 * @param rowHeight the height of each cell, in pixels
973 * @beaninfo
974 * bound: true
975 * description: The height of each cell.
976 */
977 public void setRowHeight(int rowHeight)
1023 public void setLargeModel(boolean newValue) {
1024 boolean oldValue = largeModel;
1025
1026 largeModel = newValue;
1027 firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, newValue);
1028 }
1029
1030 /**
1031 * Returns true if the tree is configured for a large model.
1032 *
1033 * @return true if a large model is suggested
1034 * @see #largeModel
1035 */
1036 public boolean isLargeModel() {
1037 return largeModel;
1038 }
1039
1040 /**
1041 * Determines what happens when editing is interrupted by selecting
1042 * another node in the tree, a change in the tree's data, or by some
1043 * other means. Setting this property to {@code true} causes the
1044 * changes to be automatically saved when editing is interrupted.
1045 * <p>
1046 * Fires a property change for the INVOKES_STOP_CELL_EDITING_PROPERTY.
1047 *
1048 * @param newValue true means that {@code stopCellEditing} is invoked
1049 * when editing is interrupted, and data is saved; false means that
1050 * {@code cancelCellEditing} is invoked, and changes are lost
1051 * @beaninfo
1052 * bound: true
1053 * description: Determines what happens when editing is interrupted,
1054 * selecting another node in the tree, a change in the
1055 * tree's data, or some other means.
1056 */
1057 public void setInvokesStopCellEditing(boolean newValue) {
1058 boolean oldValue = invokesStopCellEditing;
1059
1060 invokesStopCellEditing = newValue;
1061 firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, oldValue,
1062 newValue);
1063 }
1064
1065 /**
1066 * Returns the indicator that tells what happens when editing is
1067 * interrupted.
1068 *
1069 * @return the indicator that tells what happens when editing is
1070 * interrupted
1071 * @see #setInvokesStopCellEditing
1072 */
1073 public boolean getInvokesStopCellEditing() {
1074 return invokesStopCellEditing;
1075 }
1076
1077 /**
1078 * Sets the {@code scrollsOnExpand} property,
1079 * which determines whether the
1080 * tree might scroll to show previously hidden children.
1081 * If this property is {@code true} (the default),
1082 * when a node expands
1083 * the tree can use scrolling to make
1084 * the maximum possible number of the node's descendants visible.
1085 * In some look and feels, trees might not need to scroll when expanded;
1086 * those look and feels will ignore this property.
1087 * <p>
1088 * This is a bound property.
1089 *
1090 * @param newValue {@code false} to disable scrolling on expansion;
1091 * {@code true} to enable it
1092 * @see #getScrollsOnExpand
1093 *
1094 * @beaninfo
1095 * bound: true
1096 * description: Indicates if a node descendant should be scrolled when expanded.
1097 */
1098 public void setScrollsOnExpand(boolean newValue) {
1099 boolean oldValue = scrollsOnExpand;
1100
1101 scrollsOnExpand = newValue;
1102 scrollsOnExpandSet = true;
1103 firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue,
1104 newValue);
1105 }
1106
1107 /**
1108 * Returns the value of the {@code scrollsOnExpand} property.
1109 *
1110 * @return the value of the {@code scrollsOnExpand} property
1111 */
1112 public boolean getScrollsOnExpand() {
1113 return scrollsOnExpand;
1114 }
1115
1116 /**
1117 * Sets the number of mouse clicks before a node will expand or close.
1118 * The default is two.
1119 * <p>
1120 * This is a bound property.
1121 *
1122 * @param clickCount the number of mouse clicks to get a node expanded or closed
1123 * @since 1.3
1124 * @beaninfo
1125 * bound: true
1126 * description: Number of clicks before a node will expand/collapse.
1127 */
1128 public void setToggleClickCount(int clickCount) {
1129 int oldCount = toggleClickCount;
1130
1131 toggleClickCount = clickCount;
1132 firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldCount,
1133 clickCount);
1134 }
1135
1136 /**
1137 * Returns the number of mouse clicks needed to expand or close a node.
1138 *
1139 * @return number of mouse clicks before node is expanded
1140 * @since 1.3
1141 */
1142 public int getToggleClickCount() {
1143 return toggleClickCount;
1144 }
1145
1146 /**
1147 * Configures the {@code expandsSelectedPaths} property. If
1148 * true, any time the selection is changed, either via the
1149 * {@code TreeSelectionModel}, or the cover methods provided by
1150 * {@code JTree}, the {@code TreePath}s parents will be
1151 * expanded to make them visible (visible meaning the parent path is
1152 * expanded, not necessarily in the visible rectangle of the
1153 * {@code JTree}). If false, when the selection
1154 * changes the nodes parent is not made visible (all its parents expanded).
1155 * This is useful if you wish to have your selection model maintain paths
1156 * that are not always visible (all parents expanded).
1157 * <p>
1158 * This is a bound property.
1159 *
1160 * @param newValue the new value for {@code expandsSelectedPaths}
1161 *
1162 * @since 1.3
1163 * @beaninfo
1164 * bound: true
1165 * description: Indicates whether changes to the selection should make
1166 * the parent of the path visible.
1167 */
1168 public void setExpandsSelectedPaths(boolean newValue) {
1169 boolean oldValue = expandsSelectedPaths;
1170
1171 expandsSelectedPaths = newValue;
1172 firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue,
1173 newValue);
1174 }
1175
1176 /**
1177 * Returns the {@code expandsSelectedPaths} property.
1178 * @return true if selection changes result in the parent path being
1179 * expanded
1180 * @since 1.3
1181 * @see #setExpandsSelectedPaths
1182 */
1183 public boolean getExpandsSelectedPaths() {
1184 return expandsSelectedPaths;
1185 }
1186
1187 /**
1188 * Turns on or off automatic drag handling. In order to enable automatic
1189 * drag handling, this property should be set to {@code true}, and the
1190 * tree's {@code TransferHandler} needs to be {@code non-null}.
1191 * The default value of the {@code dragEnabled} property is {@code false}.
1192 * <p>
1193 * The job of honoring this property, and recognizing a user drag gesture,
1194 * lies with the look and feel implementation, and in particular, the tree's
1195 * {@code TreeUI}. When automatic drag handling is enabled, most look and
1196 * feels (including those that subclass {@code BasicLookAndFeel}) begin a
1197 * drag and drop operation whenever the user presses the mouse button over
1198 * an item and then moves the mouse a few pixels. Setting this property to
1199 * {@code true} can therefore have a subtle effect on how selections behave.
1200 * <p>
1201 * If a look and feel is used that ignores this property, you can still
1202 * begin a drag and drop operation by calling {@code exportAsDrag} on the
1203 * tree's {@code TransferHandler}.
1204 *
1205 * @param b whether or not to enable automatic drag handling
1206 * @exception HeadlessException if
1207 * {@code b} is {@code true} and
1208 * {@code GraphicsEnvironment.isHeadless()}
1209 * returns {@code true}
1210 * @see java.awt.GraphicsEnvironment#isHeadless
1211 * @see #getDragEnabled
1212 * @see #setTransferHandler
1213 * @see TransferHandler
1214 * @since 1.4
1215 *
1216 * @beaninfo
1217 * description: determines whether automatic drag handling is enabled
1218 * bound: false
1219 */
1220 public void setDragEnabled(boolean b) {
1221 checkDragEnabled(b);
1222 dragEnabled = b;
1223 }
1224
1225 private static void checkDragEnabled(boolean b) {
1226 if (b && GraphicsEnvironment.isHeadless()) {
1227 throw new HeadlessException();
1228 }
1229 }
1230
1231 /**
1232 * Returns whether or not automatic drag handling is enabled.
1233 *
1234 * @return the value of the {@code dragEnabled} property
1235 * @see #setDragEnabled
1236 * @since 1.4
1237 */
1238 public boolean getDragEnabled() {
1239 return dragEnabled;
1240 }
1241
1242 /**
1243 * Sets the drop mode for this component. For backward compatibility,
1244 * the default for this property is {@code DropMode.USE_SELECTION}.
1245 * Usage of one of the other modes is recommended, however, for an
1246 * improved user experience. {@code DropMode.ON}, for instance,
1247 * offers similar behavior of showing items as selected, but does so without
1248 * affecting the actual selection in the tree.
1249 * <p>
1250 * {@code JTree} supports the following drop modes:
1251 * <ul>
1252 * <li>{@code DropMode.USE_SELECTION}</li>
1253 * <li>{@code DropMode.ON}</li>
1254 * <li>{@code DropMode.INSERT}</li>
1255 * <li>{@code DropMode.ON_OR_INSERT}</li>
1256 * </ul>
1257 * <p>
1258 * The drop mode is only meaningful if this component has a
1259 * {@code TransferHandler} that accepts drops.
1260 *
1261 * @param dropMode the drop mode to use
1262 * @throws IllegalArgumentException if the drop mode is unsupported
1263 * or {@code null}
1264 * @see #getDropMode
1265 * @see #getDropLocation
1266 * @see #setTransferHandler
1267 * @see TransferHandler
1268 * @since 1.6
1269 */
1270 public final void setDropMode(DropMode dropMode) {
1271 checkDropMode(dropMode);
1272 this.dropMode = dropMode;
1273 }
1274
1275 private static void checkDropMode(DropMode dropMode) {
1276 if (dropMode != null) {
1277 switch (dropMode) {
1278 case USE_SELECTION:
1279 case ON:
1280 case INSERT:
1281 case ON_OR_INSERT:
1282 return;
1283 }
1286 throw new IllegalArgumentException(dropMode +
1287 ": Unsupported drop mode for tree");
1288 }
1289
1290 /**
1291 * Returns the drop mode for this component.
1292 *
1293 * @return the drop mode for this component
1294 * @see #setDropMode
1295 * @since 1.6
1296 */
1297 public final DropMode getDropMode() {
1298 return dropMode;
1299 }
1300
1301 /**
1302 * Calculates a drop location in this component, representing where a
1303 * drop at the given point should insert data.
1304 *
1305 * @param p the point to calculate a drop location for
1306 * @return the drop location, or {@code null}
1307 */
1308 DropLocation dropLocationForPoint(Point p) {
1309 DropLocation location = null;
1310
1311 int row = getClosestRowForLocation(p.x, p.y);
1312 Rectangle bounds = getRowBounds(row);
1313 TreeModel model = getModel();
1314 Object root = (model == null) ? null : model.getRoot();
1315 TreePath rootPath = (root == null) ? null : new TreePath(root);
1316
1317 TreePath child;
1318 TreePath parent;
1319 boolean outside = row == -1
1320 || p.y < bounds.y
1321 || p.y >= bounds.y + bounds.height;
1322
1323 switch(dropMode) {
1324 case USE_SELECTION:
1325 case ON:
1326 if (outside) {
1408 }
1409
1410 /**
1411 * Called to set or clear the drop location during a DnD operation.
1412 * In some cases, the component may need to use it's internal selection
1413 * temporarily to indicate the drop location. To help facilitate this,
1414 * this method returns and accepts as a parameter a state object.
1415 * This state object can be used to store, and later restore, the selection
1416 * state. Whatever this method returns will be passed back to it in
1417 * future calls, as the state parameter. If it wants the DnD system to
1418 * continue storing the same state, it must pass it back every time.
1419 * Here's how this is used:
1420 * <p>
1421 * Let's say that on the first call to this method the component decides
1422 * to save some state (because it is about to use the selection to show
1423 * a drop index). It can return a state object to the caller encapsulating
1424 * any saved selection state. On a second call, let's say the drop location
1425 * is being changed to something else. The component doesn't need to
1426 * restore anything yet, so it simply passes back the same state object
1427 * to have the DnD system continue storing it. Finally, let's say this
1428 * method is messaged with {@code null}. This means DnD
1429 * is finished with this component for now, meaning it should restore
1430 * state. At this point, it can use the state parameter to restore
1431 * said state, and of course return {@code null} since there's
1432 * no longer anything to store.
1433 *
1434 * @param location the drop location (as calculated by
1435 * {@code dropLocationForPoint}) or {@code null}
1436 * if there's no longer a valid drop location
1437 * @param state the state object saved earlier for this component,
1438 * or {@code null}
1439 * @param forDrop whether or not the method is being called because an
1440 * actual drop occurred
1441 * @return any saved state for this component, or {@code null} if none
1442 */
1443 Object setDropLocation(TransferHandler.DropLocation location,
1444 Object state,
1445 boolean forDrop) {
1446
1447 Object retVal = null;
1448 DropLocation treeLocation = (DropLocation)location;
1449
1450 if (dropMode == DropMode.USE_SELECTION) {
1451 if (treeLocation == null) {
1452 if (!forDrop && state != null) {
1453 setSelectionPaths(((TreePath[][])state)[0]);
1454 setAnchorSelectionPath(((TreePath[][])state)[1][0]);
1455 setLeadSelectionPath(((TreePath[][])state)[1][1]);
1456 }
1457 } else {
1458 if (dropLocation == null) {
1459 TreePath[] paths = getSelectionPaths();
1460 if (paths == null) {
1461 paths = new TreePath[0];
1477
1478 return retVal;
1479 }
1480
1481 /**
1482 * Called to indicate to this component that DnD is done.
1483 * Allows for us to cancel the expand timer.
1484 */
1485 void dndDone() {
1486 cancelDropTimer();
1487 dropTimer = null;
1488 }
1489
1490 /**
1491 * Returns the location that this component should visually indicate
1492 * as the drop location during a DnD operation over the component,
1493 * or {@code null} if no location is to currently be shown.
1494 * <p>
1495 * This method is not meant for querying the drop location
1496 * from a {@code TransferHandler}, as the drop location is only
1497 * set after the {@code TransferHandler}'s {@code canImport}
1498 * has returned and has allowed for the location to be shown.
1499 * <p>
1500 * When this property changes, a property change event with
1501 * name "dropLocation" is fired by the component.
1502 *
1503 * @return the drop location
1504 * @see #setDropMode
1505 * @see TransferHandler#canImport(TransferHandler.TransferSupport)
1506 * @since 1.6
1507 */
1508 public final DropLocation getDropLocation() {
1509 return dropLocation;
1510 }
1511
1512 private void startDropTimer() {
1513 if (dropTimer == null) {
1514 dropTimer = new TreeTimer();
1515 }
1516 dropTimer.start();
1517 }
1518
1519 private void cancelDropTimer() {
1520 if (dropTimer != null && dropTimer.isRunning()) {
1521 expandRow = -1;
1522 dropTimer.stop();
1523 }
1524 }
1525
1526 /**
1527 * Returns {@code isEditable}. This is invoked from the UI before
1528 * editing begins to insure that the given path can be edited. This
1529 * is provided as an entry point for subclassers to add filtered
1530 * editing without having to resort to creating a new editor.
1531 *
1532 * @param path a {@code TreePath} identifying a node
1533 * @return true if every parent node and the node itself is editable
1534 * @see #isEditable
1535 */
1536 public boolean isPathEditable(TreePath path) {
1537 return isEditable();
1538 }
1539
1540 /**
1541 * Overrides {@code JComponent}'s {@code getToolTipText}
1542 * method in order to allow
1543 * renderer's tips to be used if it has text set.
1544 * <p>
1545 * NOTE: For {@code JTree} to properly display tooltips of its
1546 * renderers, {@code JTree} must be a registered component with the
1547 * {@code ToolTipManager}. This can be done by invoking
1548 * {@code ToolTipManager.sharedInstance().registerComponent(tree)}.
1549 * This is not done automatically!
1550 *
1551 * @param event the {@code MouseEvent} that initiated the
1552 * {@code ToolTip} display
1553 * @return a string containing the tooltip or {@code null}
1554 * if {@code event} is null
1555 */
1556 public String getToolTipText(MouseEvent event) {
1557 String tip = null;
1558
1559 if(event != null) {
1560 Point p = event.getPoint();
1561 int selRow = getRowForLocation(p.x, p.y);
1562 TreeCellRenderer r = getCellRenderer();
1563
1564 if(selRow != -1 && r != null) {
1565 TreePath path = getPathForRow(selRow);
1566 Object lastPath = path.getLastPathComponent();
1567 Component rComponent = r.getTreeCellRendererComponent
1568 (this, lastPath, isRowSelected(selRow),
1569 isExpanded(selRow), getModel().isLeaf(lastPath), selRow,
1570 true);
1571
1572 if(rComponent instanceof JComponent) {
1573 MouseEvent newEvent;
1574 Rectangle pathBounds = getPathBounds(path);
1580 p.x, p.y,
1581 event.getXOnScreen(),
1582 event.getYOnScreen(),
1583 event.getClickCount(),
1584 event.isPopupTrigger(),
1585 MouseEvent.NOBUTTON);
1586
1587 tip = ((JComponent)rComponent).getToolTipText(newEvent);
1588 }
1589 }
1590 }
1591 // No tip from the renderer get our own tip
1592 if (tip == null) {
1593 tip = getToolTipText();
1594 }
1595 return tip;
1596 }
1597
1598 /**
1599 * Called by the renderers to convert the specified value to
1600 * text. This implementation returns {@code value.toString}, ignoring
1601 * all other arguments. To control the conversion, subclass this
1602 * method and use any of the arguments you need.
1603 *
1604 * @param value the {@code Object} to convert to text
1605 * @param selected true if the node is selected
1606 * @param expanded true if the node is expanded
1607 * @param leaf true if the node is a leaf node
1608 * @param row an integer specifying the node's display row, where 0 is
1609 * the first row in the display
1610 * @param hasFocus true if the node has the focus
1611 * @return the {@code String} representation of the node's value
1612 */
1613 public String convertValueToText(Object value, boolean selected,
1614 boolean expanded, boolean leaf, int row,
1615 boolean hasFocus) {
1616 if(value != null) {
1617 String sValue = value.toString();
1618 if (sValue != null) {
1619 return sValue;
1620 }
1621 }
1622 return "";
1623 }
1624
1625 //
1626 // The following are convenience methods that get forwarded to the
1627 // current TreeUI.
1628 //
1629
1630 /**
1631 * Returns the number of viewable nodes. A node is viewable if all of its
1632 * parents are expanded. The root is only included in this count if
1633 * {@code isRootVisible()} is {@code true}. This returns {@code 0} if
1634 * the UI has not been set.
1635 *
1636 * @return the number of viewable nodes
1637 */
1638 public int getRowCount() {
1639 TreeUI tree = getUI();
1640
1641 if(tree != null)
1642 return tree.getRowCount(this);
1643 return 0;
1644 }
1645
1646 /**
1647 * Selects the node identified by the specified path. If any
1648 * component of the path is hidden (under a collapsed node), and
1649 * {@code getExpandsSelectedPaths} is true it is
1650 * exposed (made viewable).
1651 *
1652 * @param path the {@code TreePath} specifying the node to select
1653 */
1654 public void setSelectionPath(TreePath path) {
1655 getSelectionModel().setSelectionPath(path);
1656 }
1657
1658 /**
1659 * Selects the nodes identified by the specified array of paths.
1660 * If any component in any of the paths is hidden (under a collapsed
1661 * node), and {@code getExpandsSelectedPaths} is true
1662 * it is exposed (made viewable).
1663 *
1664 * @param paths an array of {@code TreePath} objects that specifies
1665 * the nodes to select
1666 */
1667 public void setSelectionPaths(TreePath[] paths) {
1668 getSelectionModel().setSelectionPaths(paths);
1669 }
1670
1671 /**
1672 * Sets the path identifies as the lead. The lead may not be selected.
1673 * The lead is not maintained by {@code JTree},
1674 * rather the UI will update it.
1675 * <p>
1676 * This is a bound property.
1677 *
1678 * @param newPath the new lead path
1679 * @since 1.3
1680 * @beaninfo
1681 * bound: true
1682 * description: Lead selection path
1683 */
1684 public void setLeadSelectionPath(TreePath newPath) {
1685 TreePath oldValue = leadPath;
1686
1687 leadPath = newPath;
1688 firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, newPath);
1689
1690 // Fire the active descendant property change here since the
1691 // leadPath got set, this is triggered both in case node
1692 // selection changed and node focus changed
1693 if (accessibleContext != null){
1694 ((AccessibleJTree)accessibleContext).
1695 fireActiveDescendantPropertyChange(oldValue, newPath);
1696 }
1697 }
1698
1699 /**
1700 * Sets the path identified as the anchor.
1701 * The anchor is not maintained by {@code JTree}, rather the UI will
1702 * update it.
1703 * <p>
1704 * This is a bound property.
1705 *
1706 * @param newPath the new anchor path
1707 * @since 1.3
1708 * @beaninfo
1709 * bound: true
1710 * description: Anchor selection path
1711 */
1712 public void setAnchorSelectionPath(TreePath newPath) {
1713 TreePath oldValue = anchorPath;
1714
1715 anchorPath = newPath;
1716 firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, newPath);
1717 }
1718
1719 /**
1720 * Selects the node at the specified row in the display.
1721 *
1722 * @param row the row to select, where 0 is the first row in
1723 * the display
1724 */
1725 public void setSelectionRow(int row) {
1726 int[] rows = { row };
1727
1728 setSelectionRows(rows);
1729 }
1730
1731 /**
1732 * Selects the nodes corresponding to each of the specified rows
1733 * in the display. If a particular element of {@code rows} is
1734 * < 0 or >= {@code getRowCount}, it will be ignored.
1735 * If none of the elements
1736 * in {@code rows} are valid rows, the selection will
1737 * be cleared. That is it will be as if {@code clearSelection}
1738 * was invoked.
1739 *
1740 * @param rows an array of ints specifying the rows to select,
1741 * where 0 indicates the first row in the display
1742 */
1743 public void setSelectionRows(int[] rows) {
1744 TreeUI ui = getUI();
1745
1746 if(ui != null && rows != null) {
1747 int numRows = rows.length;
1748 TreePath[] paths = new TreePath[numRows];
1749
1750 for(int counter = 0; counter < numRows; counter++) {
1751 paths[counter] = ui.getPathForRow(this, rows[counter]);
1752 }
1753 setSelectionPaths(paths);
1754 }
1755 }
1756
1757 /**
1758 * Adds the node identified by the specified {@code TreePath}
1759 * to the current selection. If any component of the path isn't
1760 * viewable, and {@code getExpandsSelectedPaths} is true it is
1761 * made viewable.
1762 * <p>
1763 * Note that {@code JTree} does not allow duplicate nodes to
1764 * exist as children under the same parent -- each sibling must be
1765 * a unique object.
1766 *
1767 * @param path the {@code TreePath} to add
1768 */
1769 public void addSelectionPath(TreePath path) {
1770 getSelectionModel().addSelectionPath(path);
1771 }
1772
1773 /**
1774 * Adds each path in the array of paths to the current selection. If
1775 * any component of any of the paths isn't viewable and
1776 * {@code getExpandsSelectedPaths} is true, it is
1777 * made viewable.
1778 * <p>
1779 * Note that {@code JTree} does not allow duplicate nodes to
1780 * exist as children under the same parent -- each sibling must be
1781 * a unique object.
1782 *
1783 * @param paths an array of {@code TreePath} objects that specifies
1784 * the nodes to add
1785 */
1786 public void addSelectionPaths(TreePath[] paths) {
1787 getSelectionModel().addSelectionPaths(paths);
1788 }
1789
1790 /**
1791 * Adds the path at the specified row to the current selection.
1792 *
1793 * @param row an integer specifying the row of the node to add,
1794 * where 0 is the first row in the display
1795 */
1796 public void addSelectionRow(int row) {
1797 int[] rows = { row };
1798
1799 addSelectionRows(rows);
1800 }
1801
1802 /**
1803 * Adds the paths at each of the specified rows to the current selection.
1808 public void addSelectionRows(int[] rows) {
1809 TreeUI ui = getUI();
1810
1811 if(ui != null && rows != null) {
1812 int numRows = rows.length;
1813 TreePath[] paths = new TreePath[numRows];
1814
1815 for(int counter = 0; counter < numRows; counter++)
1816 paths[counter] = ui.getPathForRow(this, rows[counter]);
1817 addSelectionPaths(paths);
1818 }
1819 }
1820
1821 /**
1822 * Returns the last path component of the selected path. This is
1823 * a convenience method for
1824 * {@code getSelectionModel().getSelectionPath().getLastPathComponent()}.
1825 * This is typically only useful if the selection has one path.
1826 *
1827 * @return the last path component of the selected path, or
1828 * {@code null} if nothing is selected
1829 * @see TreePath#getLastPathComponent
1830 */
1831 public Object getLastSelectedPathComponent() {
1832 TreePath selPath = getSelectionModel().getSelectionPath();
1833
1834 if(selPath != null)
1835 return selPath.getLastPathComponent();
1836 return null;
1837 }
1838
1839 /**
1840 * Returns the path identified as the lead.
1841 * @return path identified as the lead
1842 */
1843 public TreePath getLeadSelectionPath() {
1844 return leadPath;
1845 }
1846
1847 /**
1848 * Returns the path identified as the anchor.
1849 * @return path identified as the anchor
1850 * @since 1.3
1851 */
1852 public TreePath getAnchorSelectionPath() {
1853 return anchorPath;
1854 }
1855
1856 /**
1857 * Returns the path to the first selected node.
1858 *
1859 * @return the {@code TreePath} for the first selected node,
1860 * or {@code null} if nothing is currently selected
1861 */
1862 public TreePath getSelectionPath() {
1863 return getSelectionModel().getSelectionPath();
1864 }
1865
1866 /**
1867 * Returns the paths of all selected values.
1868 *
1869 * @return an array of {@code TreePath} objects indicating the selected
1870 * nodes, or {@code null} if nothing is currently selected
1871 */
1872 public TreePath[] getSelectionPaths() {
1873 TreePath[] selectionPaths = getSelectionModel().getSelectionPaths();
1874
1875 return (selectionPaths != null && selectionPaths.length > 0) ? selectionPaths : null;
1876 }
1877
1878 /**
1879 * Returns all of the currently selected rows. This method is simply
1880 * forwarded to the {@code TreeSelectionModel}.
1881 * If nothing is selected {@code null} or an empty array will
1882 * be returned, based on the {@code TreeSelectionModel}
1883 * implementation.
1884 *
1885 * @return an array of integers that identifies all currently selected rows
1886 * where 0 is the first row in the display
1887 */
1888 public int[] getSelectionRows() {
1889 return getSelectionModel().getSelectionRows();
1890 }
1891
1892 /**
1893 * Returns the number of nodes selected.
1894 *
1895 * @return the number of nodes selected
1896 */
1897 public int getSelectionCount() {
1898 return selectionModel.getSelectionCount();
1899 }
1900
1901 /**
1902 * Returns the smallest selected row. If the selection is empty, or
1906 */
1907 public int getMinSelectionRow() {
1908 return getSelectionModel().getMinSelectionRow();
1909 }
1910
1911 /**
1912 * Returns the largest selected row. If the selection is empty, or
1913 * none of the selected paths are viewable, {@code -1} is returned.
1914 *
1915 * @return the largest selected row
1916 */
1917 public int getMaxSelectionRow() {
1918 return getSelectionModel().getMaxSelectionRow();
1919 }
1920
1921 /**
1922 * Returns the row index corresponding to the lead path.
1923 *
1924 * @return an integer giving the row index of the lead path,
1925 * where 0 is the first row in the display; or -1
1926 * if {@code leadPath} is {@code null}
1927 */
1928 public int getLeadSelectionRow() {
1929 TreePath leadPath = getLeadSelectionPath();
1930
1931 if (leadPath != null) {
1932 return getRowForPath(leadPath);
1933 }
1934 return -1;
1935 }
1936
1937 /**
1938 * Returns true if the item identified by the path is currently selected.
1939 *
1940 * @param path a {@code TreePath} identifying a node
1941 * @return true if the node is selected
1942 */
1943 public boolean isPathSelected(TreePath path) {
1944 return getSelectionModel().isPathSelected(path);
1945 }
1946
1947 /**
1948 * Returns true if the node identified by row is selected.
1949 *
1950 * @param row an integer specifying a display row, where 0 is the first
1951 * row in the display
1952 * @return true if the node is selected
1953 */
1954 public boolean isRowSelected(int row) {
1955 return getSelectionModel().isRowSelected(row);
1956 }
1957
1958 /**
1959 * Returns an {@code Enumeration} of the descendants of the
1960 * path {@code parent} that
1961 * are currently expanded. If {@code parent} is not currently
1962 * expanded, this will return {@code null}.
1963 * If you expand/collapse nodes while
1964 * iterating over the returned {@code Enumeration}
1965 * this may not return all
1966 * the expanded paths, or may return paths that are no longer expanded.
1967 *
1968 * @param parent the path which is to be examined
1969 * @return an {@code Enumeration} of the descendents of
1970 * {@code parent}, or {@code null} if
1971 * {@code parent} is not currently expanded
1972 */
1973 public Enumeration<TreePath> getExpandedDescendants(TreePath parent) {
1974 if(!isExpanded(parent))
1975 return null;
1976
1977 Enumeration<TreePath> toggledPaths = expandedState.keys();
1978 Vector<TreePath> elements = null;
1979 TreePath path;
1980 Object value;
1981
1982 if(toggledPaths != null) {
1983 while(toggledPaths.hasMoreElements()) {
1984 path = toggledPaths.nextElement();
1985 value = expandedState.get(path);
1986 // Add the path if it is expanded, a descendant of parent,
1987 // and it is visible (all parents expanded). This is rather
1988 // expensive!
1989 if(path != parent && value != null &&
1990 ((Boolean)value).booleanValue() &&
1991 parent.isDescendant(path) && isVisible(path)) {
1992 if (elements == null) {
1993 elements = new Vector<TreePath>();
1994 }
1995 elements.addElement(path);
1996 }
1997 }
1998 }
1999 if (elements == null) {
2000 Set<TreePath> empty = Collections.emptySet();
2001 return Collections.enumeration(empty);
2002 }
2003 return elements.elements();
2004 }
2005
2006 /**
2007 * Returns true if the node identified by the path has ever been
2008 * expanded.
2009 *
2010 * @param path a {@code TreePath} identifying a node
2011 * @return true if the {@code path} has ever been expanded
2012 */
2013 public boolean hasBeenExpanded(TreePath path) {
2014 return (path != null && expandedState.get(path) != null);
2015 }
2016
2017 /**
2018 * Returns true if the node identified by the path is currently expanded,
2019 *
2020 * @param path the {@code TreePath} specifying the node to check
2021 * @return false if any of the nodes in the node's path are collapsed,
2022 * true if all nodes in the path are expanded
2023 */
2024 public boolean isExpanded(TreePath path) {
2025
2026 if(path == null)
2027 return false;
2028 Object value;
2029
2030 do{
2031 value = expandedState.get(path);
2032 if(value == null || !((Boolean)value).booleanValue())
2033 return false;
2034 } while( (path=path.getParentPath())!=null );
2035
2036 return true;
2037 }
2038
2039 /**
2040 * Returns true if the node at the specified display row is currently
2047 public boolean isExpanded(int row) {
2048 TreeUI tree = getUI();
2049
2050 if(tree != null) {
2051 TreePath path = tree.getPathForRow(this, row);
2052
2053 if(path != null) {
2054 Boolean value = expandedState.get(path);
2055
2056 return (value != null && value.booleanValue());
2057 }
2058 }
2059 return false;
2060 }
2061
2062 /**
2063 * Returns true if the value identified by path is currently collapsed,
2064 * this will return false if any of the values in path are currently
2065 * not being displayed.
2066 *
2067 * @param path the {@code TreePath} to check
2068 * @return true if any of the nodes in the node's path are collapsed,
2069 * false if all nodes in the path are expanded
2070 */
2071 public boolean isCollapsed(TreePath path) {
2072 return !isExpanded(path);
2073 }
2074
2075 /**
2076 * Returns true if the node at the specified display row is collapsed.
2077 *
2078 * @param row the row to check, where 0 is the first row in the
2079 * display
2080 * @return true if the node is currently collapsed, otherwise false
2081 */
2082 public boolean isCollapsed(int row) {
2083 return !isExpanded(row);
2084 }
2085
2086 /**
2087 * Ensures that the node identified by path is currently viewable.
2088 *
2089 * @param path the {@code TreePath} to make visible
2090 */
2091 public void makeVisible(TreePath path) {
2092 if(path != null) {
2093 TreePath parentPath = path.getParentPath();
2094
2095 if(parentPath != null) {
2096 expandPath(parentPath);
2097 }
2098 }
2099 }
2100
2101 /**
2102 * Returns true if the value identified by path is currently viewable,
2103 * which means it is either the root or all of its parents are expanded.
2104 * Otherwise, this method returns false.
2105 *
2106 * @param path {@code TreePath} identifying a node
2107 * @return true if the node is viewable, otherwise false
2108 */
2109 public boolean isVisible(TreePath path) {
2110 if(path != null) {
2111 TreePath parentPath = path.getParentPath();
2112
2113 if(parentPath != null)
2114 return isExpanded(parentPath);
2115 // Root.
2116 return true;
2117 }
2118 return false;
2119 }
2120
2121 /**
2122 * Returns the {@code Rectangle} that the specified node will be drawn
2123 * into. Returns {@code null} if any component in the path is hidden
2124 * (under a collapsed parent).
2125 * <p>
2126 * Note:<br>
2127 * This method returns a valid rectangle, even if the specified
2128 * node is not currently displayed.
2129 *
2130 * @param path the {@code TreePath} identifying the node
2131 * @return the {@code Rectangle} the node is drawn in,
2132 * or {@code null}
2133 */
2134 public Rectangle getPathBounds(TreePath path) {
2135 TreeUI tree = getUI();
2136
2137 if(tree != null)
2138 return tree.getPathBounds(this, path);
2139 return null;
2140 }
2141
2142 /**
2143 * Returns the {@code Rectangle} that the node at the specified row is
2144 * drawn in.
2145 *
2146 * @param row the row to be drawn, where 0 is the first row in the
2147 * display
2148 * @return the {@code Rectangle} the node is drawn in
2149 */
2150 public Rectangle getRowBounds(int row) {
2151 return getPathBounds(getPathForRow(row));
2152 }
2153
2154 /**
2155 * Makes sure all the path components in path are expanded (except
2156 * for the last path component) and scrolls so that the
2157 * node identified by the path is displayed. Only works when this
2158 * {@code JTree} is contained in a {@code JScrollPane}.
2159 *
2160 * @param path the {@code TreePath} identifying the node to
2161 * bring into view
2162 */
2163 public void scrollPathToVisible(TreePath path) {
2164 if(path != null) {
2165 makeVisible(path);
2166
2167 Rectangle bounds = getPathBounds(path);
2168
2169 if(bounds != null) {
2170 scrollRectToVisible(bounds);
2171 if (accessibleContext != null) {
2172 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
2173 }
2174 }
2175 }
2176 }
2177
2178 /**
2179 * Scrolls the item identified by row until it is displayed. The minimum
2180 * of amount of scrolling necessary to bring the row into view
2181 * is performed. Only works when this {@code JTree} is contained in a
2182 * {@code JScrollPane}.
2183 *
2184 * @param row an integer specifying the row to scroll, where 0 is the
2185 * first row in the display
2186 */
2187 public void scrollRowToVisible(int row) {
2188 scrollPathToVisible(getPathForRow(row));
2189 }
2190
2191 /**
2192 * Returns the path for the specified row. If {@code row} is
2193 * not visible, or a {@code TreeUI} has not been set, {@code null}
2194 * is returned.
2195 *
2196 * @param row an integer specifying a row
2197 * @return the {@code TreePath} to the specified node,
2198 * {@code null} if {@code row < 0}
2199 * or {@code row >= getRowCount()}
2200 */
2201 public TreePath getPathForRow(int row) {
2202 TreeUI tree = getUI();
2203
2204 if(tree != null)
2205 return tree.getPathForRow(this, row);
2206 return null;
2207 }
2208
2209 /**
2210 * Returns the row that displays the node identified by the specified
2211 * path.
2212 *
2213 * @param path the {@code TreePath} identifying a node
2214 * @return an integer specifying the display row, where 0 is the first
2215 * row in the display, or -1 if any of the elements in path
2216 * are hidden under a collapsed parent.
2217 */
2218 public int getRowForPath(TreePath path) {
2219 TreeUI tree = getUI();
2220
2221 if(tree != null)
2222 return tree.getRowForPath(this, path);
2223 return -1;
2224 }
2225
2226 /**
2227 * Ensures that the node identified by the specified path is
2228 * expanded and viewable. If the last item in the path is a
2229 * leaf, this will have no effect.
2230 *
2231 * @param path the {@code TreePath} identifying a node
2232 */
2233 public void expandPath(TreePath path) {
2234 // Only expand if not leaf!
2235 TreeModel model = getModel();
2236
2237 if(path != null && model != null &&
2238 !model.isLeaf(path.getLastPathComponent())) {
2239 setExpandedState(path, true);
2240 }
2241 }
2242
2243 /**
2244 * Ensures that the node in the specified row is expanded and
2245 * viewable.
2246 * <p>
2247 * If {@code row} is {@code < 0} or {@code >= getRowCount} this
2248 * will have no effect.
2249 *
2250 * @param row an integer specifying a display row, where 0 is the
2251 * first row in the display
2252 */
2253 public void expandRow(int row) {
2254 expandPath(getPathForRow(row));
2255 }
2256
2257 /**
2258 * Ensures that the node identified by the specified path is
2259 * collapsed and viewable.
2260 *
2261 * @param path the {@code TreePath} identifying a node
2262 */
2263 public void collapsePath(TreePath path) {
2264 setExpandedState(path, false);
2265 }
2266
2267 /**
2268 * Ensures that the node in the specified row is collapsed.
2269 * <p>
2270 * If {@code row} is {@code < 0} or {@code >= getRowCount} this
2271 * will have no effect.
2272 *
2273 * @param row an integer specifying a display row, where 0 is the
2274 * first row in the display
2275 */
2276 public void collapseRow(int row) {
2277 collapsePath(getPathForRow(row));
2278 }
2279
2280 /**
2281 * Returns the path for the node at the specified location.
2282 *
2283 * @param x an integer giving the number of pixels horizontally from
2284 * the left edge of the display area, minus any left margin
2285 * @param y an integer giving the number of pixels vertically from
2286 * the top of the display area, minus any top margin
2287 * @return the {@code TreePath} for the node at that location
2288 */
2289 public TreePath getPathForLocation(int x, int y) {
2290 TreePath closestPath = getClosestPathForLocation(x, y);
2291
2292 if(closestPath != null) {
2293 Rectangle pathBounds = getPathBounds(closestPath);
2294
2295 if(pathBounds != null &&
2296 x >= pathBounds.x && x < (pathBounds.x + pathBounds.width) &&
2297 y >= pathBounds.y && y < (pathBounds.y + pathBounds.height))
2298 return closestPath;
2299 }
2300 return null;
2301 }
2302
2303 /**
2304 * Returns the row for the specified location.
2305 *
2306 * @param x an integer giving the number of pixels horizontally from
2307 * the left edge of the display area, minus any left margin
2308 * @param y an integer giving the number of pixels vertically from
2309 * the top of the display area, minus any top margin
2310 * @return the row corresponding to the location, or -1 if the
2311 * location is not within the bounds of a displayed cell
2312 * @see #getClosestRowForLocation
2313 */
2314 public int getRowForLocation(int x, int y) {
2315 return getRowForPath(getPathForLocation(x, y));
2316 }
2317
2318 /**
2319 * Returns the path to the node that is closest to x,y. If
2320 * no nodes are currently viewable, or there is no model, returns
2321 * {@code null}, otherwise it always returns a valid path. To test if
2322 * the node is exactly at x, y, get the node's bounds and
2323 * test x, y against that.
2324 *
2325 * @param x an integer giving the number of pixels horizontally from
2326 * the left edge of the display area, minus any left margin
2327 * @param y an integer giving the number of pixels vertically from
2328 * the top of the display area, minus any top margin
2329 * @return the {@code TreePath} for the node closest to that location,
2330 * {@code null} if nothing is viewable or there is no model
2331 *
2332 * @see #getPathForLocation
2333 * @see #getPathBounds
2334 */
2335 public TreePath getClosestPathForLocation(int x, int y) {
2336 TreeUI tree = getUI();
2337
2338 if(tree != null)
2339 return tree.getClosestPathForLocation(this, x, y);
2340 return null;
2341 }
2342
2343 /**
2344 * Returns the row to the node that is closest to x,y. If no nodes
2345 * are viewable or there is no model, returns -1. Otherwise,
2346 * it always returns a valid row. To test if the returned object is
2347 * exactly at x, y, get the bounds for the node at the returned
2348 * row and test x, y against that.
2349 *
2350 * @param x an integer giving the number of pixels horizontally from
2351 * the left edge of the display area, minus any left margin
2352 * @param y an integer giving the number of pixels vertically from
2353 * the top of the display area, minus any top margin
2354 * @return the row closest to the location, -1 if nothing is
2355 * viewable or there is no model
2356 *
2357 * @see #getRowForLocation
2358 * @see #getRowBounds
2359 */
2360 public int getClosestRowForLocation(int x, int y) {
2361 return getRowForPath(getClosestPathForLocation(x, y));
2362 }
2363
2364 /**
2365 * Returns true if the tree is being edited. The item that is being
2366 * edited can be obtained using {@code getSelectionPath}.
2367 *
2368 * @return true if the user is currently editing a node
2369 * @see #getSelectionPath
2370 */
2371 public boolean isEditing() {
2372 TreeUI tree = getUI();
2373
2374 if(tree != null)
2375 return tree.isEditing(this);
2376 return false;
2377 }
2378
2379 /**
2380 * Ends the current editing session.
2381 * (The {@code DefaultTreeCellEditor}
2382 * object saves any edits that are currently in progress on a cell.
2383 * Other implementations may operate differently.)
2384 * Has no effect if the tree isn't being edited.
2385 * <blockquote>
2386 * <b>Note:</b><br>
2387 * To make edit-saves automatic whenever the user changes
2388 * their position in the tree, use {@link #setInvokesStopCellEditing}.
2389 * </blockquote>
2390 *
2391 * @return true if editing was in progress and is now stopped,
2392 * false if editing was not in progress
2393 */
2394 public boolean stopEditing() {
2395 TreeUI tree = getUI();
2396
2397 if(tree != null)
2398 return tree.stopEditing(this);
2399 return false;
2400 }
2401
2402 /**
2403 * Cancels the current editing session. Has no effect if the
2404 * tree isn't being edited.
2405 */
2406 public void cancelEditing() {
2407 TreeUI tree = getUI();
2408
2409 if(tree != null)
2410 tree.cancelEditing(this);
2411 }
2412
2413 /**
2414 * Selects the node identified by the specified path and initiates
2415 * editing. The edit-attempt fails if the {@code CellEditor}
2416 * does not allow
2417 * editing for the specified item.
2418 *
2419 * @param path the {@code TreePath} identifying a node
2420 */
2421 public void startEditingAtPath(TreePath path) {
2422 TreeUI tree = getUI();
2423
2424 if(tree != null)
2425 tree.startEditingAtPath(this, path);
2426 }
2427
2428 /**
2429 * Returns the path to the element that is currently being edited.
2430 *
2431 * @return the {@code TreePath} for the node being edited
2432 */
2433 public TreePath getEditingPath() {
2434 TreeUI tree = getUI();
2435
2436 if(tree != null)
2437 return tree.getEditingPath(this);
2438 return null;
2439 }
2440
2441 //
2442 // Following are primarily convenience methods for mapping from
2443 // row based selections to path selections. Sometimes it is
2444 // easier to deal with these than paths (mouse downs, key downs
2445 // usually just deal with index based selections).
2446 // Since row based selections require a UI many of these won't work
2447 // without one.
2448 //
2449
2450 /**
2451 * Sets the tree's selection model. When a {@code null} value is
2452 * specified an empty
2453 * {@code selectionModel} is used, which does not allow selections.
2454 * <p>
2455 * This is a bound property.
2456 *
2457 * @param selectionModel the {@code TreeSelectionModel} to use,
2458 * or {@code null} to disable selections
2459 * @see TreeSelectionModel
2460 * @beaninfo
2461 * bound: true
2462 * description: The tree's selection model.
2463 */
2464 public void setSelectionModel(TreeSelectionModel selectionModel) {
2465 if(selectionModel == null)
2466 selectionModel = EmptySelectionModel.sharedInstance();
2467
2468 TreeSelectionModel oldValue = this.selectionModel;
2469
2470 if (this.selectionModel != null && selectionRedirector != null) {
2471 this.selectionModel.removeTreeSelectionListener
2472 (selectionRedirector);
2473 }
2474 if (accessibleContext != null) {
2475 this.selectionModel.removeTreeSelectionListener((TreeSelectionListener)accessibleContext);
2476 selectionModel.addTreeSelectionListener((TreeSelectionListener)accessibleContext);
2477 }
2478
2479 this.selectionModel = selectionModel;
2480 if (selectionRedirector != null) {
2481 this.selectionModel.addTreeSelectionListener(selectionRedirector);
2482 }
2483 firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue,
2484 this.selectionModel);
2485
2486 if (accessibleContext != null) {
2487 accessibleContext.firePropertyChange(
2488 AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
2489 Boolean.valueOf(false), Boolean.valueOf(true));
2490 }
2491 }
2492
2493 /**
2494 * Returns the model for selections. This should always return a
2495 * non-{@code null} value. If you don't want to allow anything
2496 * to be selected
2497 * set the selection model to {@code null}, which forces an empty
2498 * selection model to be used.
2499 *
2500 * @return the model for selections
2501 * @see #setSelectionModel
2502 */
2503 public TreeSelectionModel getSelectionModel() {
2504 return selectionModel;
2505 }
2506
2507 /**
2508 * Returns the paths (inclusive) between the specified rows. If
2509 * the specified indices are within the viewable set of rows, or
2510 * bound the viewable set of rows, then the indices are
2511 * constrained by the viewable set of rows. If the specified
2512 * indices are not within the viewable set of rows, or do not
2513 * bound the viewable set of rows, then an empty array is
2514 * returned. For example, if the row count is {@code 10}, and this
2515 * method is invoked with {@code -1, 20}, then the specified
2516 * indices are constrained to the viewable set of rows, and this is
2517 * treated as if invoked with {@code 0, 9}. On the other hand, if
2622 * <p>
2623 * The parameters are not order dependent. That is, {@code
2624 * removeSelectionInterval(x, y)} is equivalent to
2625 * {@code removeSelectionInterval(y, x)}.
2626 *
2627 * @param index0 the first row to remove from the selection
2628 * @param index1 the last row to remove from the selection
2629 */
2630 public void removeSelectionInterval(int index0, int index1) {
2631 TreePath[] paths = getPathBetweenRows(index0, index1);
2632
2633 if (paths != null && paths.length > 0) {
2634 this.getSelectionModel().removeSelectionPaths(paths);
2635 }
2636 }
2637
2638 /**
2639 * Removes the node identified by the specified path from the current
2640 * selection.
2641 *
2642 * @param path the {@code TreePath} identifying a node
2643 */
2644 public void removeSelectionPath(TreePath path) {
2645 this.getSelectionModel().removeSelectionPath(path);
2646 }
2647
2648 /**
2649 * Removes the nodes identified by the specified paths from the
2650 * current selection.
2651 *
2652 * @param paths an array of {@code TreePath} objects that
2653 * specifies the nodes to remove
2654 */
2655 public void removeSelectionPaths(TreePath[] paths) {
2656 this.getSelectionModel().removeSelectionPaths(paths);
2657 }
2658
2659 /**
2660 * Removes the row at the index {@code row} from the current
2661 * selection.
2662 *
2663 * @param row the row to remove
2664 */
2665 public void removeSelectionRow(int row) {
2666 int[] rows = { row };
2667
2668 removeSelectionRows(rows);
2669 }
2670
2671 /**
2672 * Removes the rows that are selected at each of the specified
2673 * rows.
2674 *
2675 * @param rows an array of ints specifying display rows, where 0 is
2676 * the first row in the display
2677 */
2678 public void removeSelectionRows(int[] rows) {
2679 TreeUI ui = getUI();
2680
2688 }
2689 }
2690
2691 /**
2692 * Clears the selection.
2693 */
2694 public void clearSelection() {
2695 getSelectionModel().clearSelection();
2696 }
2697
2698 /**
2699 * Returns true if the selection is currently empty.
2700 *
2701 * @return true if the selection is currently empty
2702 */
2703 public boolean isSelectionEmpty() {
2704 return getSelectionModel().isSelectionEmpty();
2705 }
2706
2707 /**
2708 * Adds a listener for {@code TreeExpansion} events.
2709 *
2710 * @param tel a TreeExpansionListener that will be notified when
2711 * a tree node is expanded or collapsed (a "negative
2712 * expansion")
2713 */
2714 public void addTreeExpansionListener(TreeExpansionListener tel) {
2715 if (settingUI) {
2716 uiTreeExpansionListener = tel;
2717 }
2718 listenerList.add(TreeExpansionListener.class, tel);
2719 }
2720
2721 /**
2722 * Removes a listener for {@code TreeExpansion} events.
2723 *
2724 * @param tel the {@code TreeExpansionListener} to remove
2725 */
2726 public void removeTreeExpansionListener(TreeExpansionListener tel) {
2727 listenerList.remove(TreeExpansionListener.class, tel);
2728 if (uiTreeExpansionListener == tel) {
2729 uiTreeExpansionListener = null;
2730 }
2731 }
2732
2733 /**
2734 * Returns an array of all the {@code TreeExpansionListener}s added
2735 * to this JTree with addTreeExpansionListener().
2736 *
2737 * @return all of the {@code TreeExpansionListener}s added or an empty
2738 * array if no listeners have been added
2739 * @since 1.4
2740 */
2741 public TreeExpansionListener[] getTreeExpansionListeners() {
2742 return listenerList.getListeners(TreeExpansionListener.class);
2743 }
2744
2745 /**
2746 * Adds a listener for {@code TreeWillExpand} events.
2747 *
2748 * @param tel a {@code TreeWillExpandListener} that will be notified
2749 * when a tree node will be expanded or collapsed (a "negative
2750 * expansion")
2751 */
2752 public void addTreeWillExpandListener(TreeWillExpandListener tel) {
2753 listenerList.add(TreeWillExpandListener.class, tel);
2754 }
2755
2756 /**
2757 * Removes a listener for {@code TreeWillExpand} events.
2758 *
2759 * @param tel the {@code TreeWillExpandListener} to remove
2760 */
2761 public void removeTreeWillExpandListener(TreeWillExpandListener tel) {
2762 listenerList.remove(TreeWillExpandListener.class, tel);
2763 }
2764
2765 /**
2766 * Returns an array of all the {@code TreeWillExpandListener}s added
2767 * to this JTree with addTreeWillExpandListener().
2768 *
2769 * @return all of the {@code TreeWillExpandListener}s added or an empty
2770 * array if no listeners have been added
2771 * @since 1.4
2772 */
2773 public TreeWillExpandListener[] getTreeWillExpandListeners() {
2774 return listenerList.getListeners(TreeWillExpandListener.class);
2775 }
2776
2777 /**
2778 * Notifies all listeners that have registered interest for
2779 * notification on this event type. The event instance
2780 * is lazily created using the {@code path} parameter.
2781 *
2782 * @param path the {@code TreePath} indicating the node that was
2783 * expanded
2784 * @see EventListenerList
2785 */
2786 public void fireTreeExpanded(TreePath path) {
2787 // Guaranteed to return a non-null array
2788 Object[] listeners = listenerList.getListenerList();
2789 TreeExpansionEvent e = null;
2790 if (uiTreeExpansionListener != null) {
2791 e = new TreeExpansionEvent(this, path);
2792 uiTreeExpansionListener.treeExpanded(e);
2793 }
2794 // Process the listeners last to first, notifying
2795 // those that are interested in this event
2796 for (int i = listeners.length-2; i>=0; i-=2) {
2797 if (listeners[i]==TreeExpansionListener.class &&
2798 listeners[i + 1] != uiTreeExpansionListener) {
2799 // Lazily create the event:
2800 if (e == null)
2801 e = new TreeExpansionEvent(this, path);
2802 ((TreeExpansionListener)listeners[i+1]).
2803 treeExpanded(e);
2804 }
2805 }
2806 }
2807
2808 /**
2809 * Notifies all listeners that have registered interest for
2810 * notification on this event type. The event instance
2811 * is lazily created using the {@code path} parameter.
2812 *
2813 * @param path the {@code TreePath} indicating the node that was
2814 * collapsed
2815 * @see EventListenerList
2816 */
2817 public void fireTreeCollapsed(TreePath path) {
2818 // Guaranteed to return a non-null array
2819 Object[] listeners = listenerList.getListenerList();
2820 TreeExpansionEvent e = null;
2821 if (uiTreeExpansionListener != null) {
2822 e = new TreeExpansionEvent(this, path);
2823 uiTreeExpansionListener.treeCollapsed(e);
2824 }
2825 // Process the listeners last to first, notifying
2826 // those that are interested in this event
2827 for (int i = listeners.length-2; i>=0; i-=2) {
2828 if (listeners[i]==TreeExpansionListener.class &&
2829 listeners[i + 1] != uiTreeExpansionListener) {
2830 // Lazily create the event:
2831 if (e == null)
2832 e = new TreeExpansionEvent(this, path);
2833 ((TreeExpansionListener)listeners[i+1]).
2834 treeCollapsed(e);
2835 }
2836 }
2837 }
2838
2839 /**
2840 * Notifies all listeners that have registered interest for
2841 * notification on this event type. The event instance
2842 * is lazily created using the {@code path} parameter.
2843 *
2844 * @param path the {@code TreePath} indicating the node that was
2845 * expanded
2846 * @throws ExpandVetoException if the expansion is prevented from occurring
2847 * @see EventListenerList
2848 */
2849 public void fireTreeWillExpand(TreePath path) throws ExpandVetoException {
2850 // Guaranteed to return a non-null array
2851 Object[] listeners = listenerList.getListenerList();
2852 TreeExpansionEvent e = null;
2853 // Process the listeners last to first, notifying
2854 // those that are interested in this event
2855 for (int i = listeners.length-2; i>=0; i-=2) {
2856 if (listeners[i]==TreeWillExpandListener.class) {
2857 // Lazily create the event:
2858 if (e == null)
2859 e = new TreeExpansionEvent(this, path);
2860 ((TreeWillExpandListener)listeners[i+1]).
2861 treeWillExpand(e);
2862 }
2863 }
2864 }
2865
2866 /**
2867 * Notifies all listeners that have registered interest for
2868 * notification on this event type. The event instance
2869 * is lazily created using the {@code path} parameter.
2870 *
2871 * @param path the {@code TreePath} indicating the node that was
2872 * expanded
2873 * @throws ExpandVetoException if the collapse is prevented from occurring
2874 * @see EventListenerList
2875 */
2876 public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException {
2877 // Guaranteed to return a non-null array
2878 Object[] listeners = listenerList.getListenerList();
2879 TreeExpansionEvent e = null;
2880 // Process the listeners last to first, notifying
2881 // those that are interested in this event
2882 for (int i = listeners.length-2; i>=0; i-=2) {
2883 if (listeners[i]==TreeWillExpandListener.class) {
2884 // Lazily create the event:
2885 if (e == null)
2886 e = new TreeExpansionEvent(this, path);
2887 ((TreeWillExpandListener)listeners[i+1]).
2888 treeWillCollapse(e);
2889 }
2890 }
2891 }
2892
2893 /**
2894 * Adds a listener for {@code TreeSelection} events.
2895 *
2896 * @param tsl the {@code TreeSelectionListener} that will be notified
2897 * when a node is selected or deselected (a "negative
2898 * selection")
2899 */
2900 public void addTreeSelectionListener(TreeSelectionListener tsl) {
2901 listenerList.add(TreeSelectionListener.class,tsl);
2902 if(listenerList.getListenerCount(TreeSelectionListener.class) != 0
2903 && selectionRedirector == null) {
2904 selectionRedirector = new TreeSelectionRedirector();
2905 selectionModel.addTreeSelectionListener(selectionRedirector);
2906 }
2907 }
2908
2909 /**
2910 * Removes a {@code TreeSelection} listener.
2911 *
2912 * @param tsl the {@code TreeSelectionListener} to remove
2913 */
2914 public void removeTreeSelectionListener(TreeSelectionListener tsl) {
2915 listenerList.remove(TreeSelectionListener.class,tsl);
2916 if(listenerList.getListenerCount(TreeSelectionListener.class) == 0
2917 && selectionRedirector != null) {
2918 selectionModel.removeTreeSelectionListener
2919 (selectionRedirector);
2920 selectionRedirector = null;
2921 }
2922 }
2923
2924 /**
2925 * Returns an array of all the {@code TreeSelectionListener}s added
2926 * to this JTree with addTreeSelectionListener().
2927 *
2928 * @return all of the {@code TreeSelectionListener}s added or an empty
2929 * array if no listeners have been added
2930 * @since 1.4
2931 */
2932 public TreeSelectionListener[] getTreeSelectionListeners() {
2933 return listenerList.getListeners(TreeSelectionListener.class);
2934 }
2935
2936 /**
2937 * Notifies all listeners that have registered interest for
2938 * notification on this event type.
2939 *
2940 * @param e the {@code TreeSelectionEvent} to be fired;
2941 * generated by the
2942 * {@code TreeSelectionModel}
2943 * when a node is selected or deselected
2944 * @see EventListenerList
2945 */
2946 protected void fireValueChanged(TreeSelectionEvent e) {
2947 // Guaranteed to return a non-null array
2948 Object[] listeners = listenerList.getListenerList();
2949 // Process the listeners last to first, notifying
2950 // those that are interested in this event
2951 for (int i = listeners.length-2; i>=0; i-=2) {
2952 // TreeSelectionEvent e = null;
2953 if (listeners[i]==TreeSelectionListener.class) {
2954 // Lazily create the event:
2955 // if (e == null)
2956 // e = new ListSelectionEvent(this, firstIndex, lastIndex);
2957 ((TreeSelectionListener)listeners[i+1]).valueChanged(e);
2958 }
2959 }
2960 }
2961
2962 /**
2963 * Sent when the tree has changed enough that we need to resize
2964 * the bounds, but not enough that we need to remove the
2965 * expanded node set (e.g nodes were expanded or collapsed, or
2966 * nodes were inserted into the tree). You should never have to
2967 * invoke this, the UI will invoke this as it needs to.
2968 */
2969 public void treeDidChange() {
2970 revalidate();
2971 repaint();
2972 }
2973
2974 /**
2975 * Sets the number of rows that are to be displayed.
2976 * This will only work if the tree is contained in a
2977 * {@code JScrollPane},
2978 * and will adjust the preferred size and size of that scrollpane.
2979 * <p>
2980 * This is a bound property.
2981 *
2982 * @param newCount the number of rows to display
2983 * @beaninfo
2984 * bound: true
2985 * description: The number of rows that are to be displayed.
2986 */
2987 public void setVisibleRowCount(int newCount) {
2988 int oldCount = visibleRowCount;
2989
2990 visibleRowCount = newCount;
2991 firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldCount,
2992 visibleRowCount);
2993 invalidate();
2994 if (accessibleContext != null) {
2995 ((AccessibleJTree)accessibleContext).fireVisibleDataPropertyChange();
2996 }
2997 }
3001 *
3002 * @return the number of rows displayed
3003 */
3004 public int getVisibleRowCount() {
3005 return visibleRowCount;
3006 }
3007
3008 /**
3009 * Expands the root path, assuming the current TreeModel has been set.
3010 */
3011 private void expandRoot() {
3012 TreeModel model = getModel();
3013 if(model != null && model.getRoot() != null) {
3014 expandPath(new TreePath(model.getRoot()));
3015 }
3016 }
3017
3018 /**
3019 * Returns the TreePath to the next tree element that
3020 * begins with a prefix. To handle the conversion of a
3021 * {@code TreePath} into a String, {@code convertValueToText}
3022 * is used.
3023 *
3024 * @param prefix the string to test for a match
3025 * @param startingRow the row for starting the search
3026 * @param bias the search direction, either
3027 * Position.Bias.Forward or Position.Bias.Backward.
3028 * @return the TreePath of the next tree element that
3029 * starts with the prefix; otherwise null
3030 * @exception IllegalArgumentException if prefix is null
3031 * or startingRow is out of bounds
3032 * @since 1.4
3033 */
3034 public TreePath getNextMatch(String prefix, int startingRow,
3035 Position.Bias bias) {
3036
3037 int max = getRowCount();
3038 if (prefix == null) {
3039 throw new IllegalArgumentException();
3040 }
3041 if (startingRow < 0 || startingRow >= max) {
3202 Object archivePath;
3203
3204 try {
3205 archivePath = getModelIndexsForPath(path);
3206 } catch (Error error) {
3207 archivePath = null;
3208 }
3209 if(archivePath != null) {
3210 state.addElement(archivePath);
3211 state.addElement(expandedState.get(path));
3212 }
3213 }
3214 return state;
3215 }
3216 }
3217 return null;
3218 }
3219
3220 /**
3221 * Updates the expanded state of nodes in the tree based on the
3222 * previously archived state {@code state}.
3223 */
3224 private void unarchiveExpandedState(Object state) {
3225 if(state instanceof Vector) {
3226 Vector<?> paths = (Vector)state;
3227
3228 for(int counter = paths.size() - 1; counter >= 0; counter--) {
3229 Boolean eState = (Boolean)paths.elementAt(counter--);
3230 TreePath path;
3231
3232 try {
3233 path = getPathForIndexs((int[])paths.elementAt(counter));
3234 if(path != null)
3235 expandedState.put(path, eState);
3236 } catch (Error error) {}
3237 }
3238 }
3239 }
3240
3241 /**
3242 * Returns an array of integers specifying the indexs of the
3243 * components in the {@code path}. If {@code path} is
3244 * the root, this will return an empty array. If {@code path}
3245 * is {@code null}, {@code null} will be returned.
3246 */
3247 private int[] getModelIndexsForPath(TreePath path) {
3248 if(path != null) {
3249 TreeModel model = getModel();
3250 int count = path.getPathCount();
3251 int[] indexs = new int[count - 1];
3252 Object parent = model.getRoot();
3253
3254 for(int counter = 1; counter < count; counter++) {
3255 indexs[counter - 1] = model.getIndexOfChild
3256 (parent, path.getPathComponent(counter));
3257 parent = path.getPathComponent(counter);
3258 if(indexs[counter - 1] < 0)
3259 return null;
3260 }
3261 return indexs;
3262 }
3263 return null;
3264 }
3265
3266 /**
3267 * Returns a {@code TreePath} created by obtaining the children
3268 * for each of the indices in {@code indexs}. If {@code indexs}
3269 * or the {@code TreeModel} is {@code null}, it will return
3270 * {@code null}.
3271 */
3272 private TreePath getPathForIndexs(int[] indexs) {
3273 if(indexs == null)
3274 return null;
3275
3276 TreeModel model = getModel();
3277
3278 if(model == null)
3279 return null;
3280
3281 int count = indexs.length;
3282
3283 Object parent = model.getRoot();
3284 if (parent == null)
3285 return null;
3286
3287 TreePath parentPath = new TreePath(parent);
3288 for(int counter = 0; counter < count; counter++) {
3289 parent = model.getChild(parent, indexs[counter]);
3290 if(parent == null)
3291 return null;
3292 parentPath = parentPath.pathByAddingChild(parent);
3293 }
3294 return parentPath;
3295 }
3296
3297 /**
3298 * {@code EmptySelectionModel} is a {@code TreeSelectionModel}
3299 * that does not allow anything to be selected.
3300 * <p>
3301 * <strong>Warning:</strong>
3302 * Serialized objects of this class will not be compatible with
3303 * future Swing releases. The current serialization support is
3304 * appropriate for short term storage or RMI between applications running
3305 * the same version of Swing. As of 1.4, support for long term storage
3306 * of all JavaBeans™
3307 * has been added to the {@code java.beans} package.
3308 * Please see {@link java.beans.XMLEncoder}.
3309 */
3310 @SuppressWarnings("serial")
3311 protected static class EmptySelectionModel extends
3312 DefaultTreeSelectionModel
3313 {
3314 /**
3315 * The single instance of {@code EmptySelectionModel}.
3316 */
3317 protected static final EmptySelectionModel sharedInstance =
3318 new EmptySelectionModel();
3319
3320 /**
3321 * Returns the single instance of {@code EmptySelectionModel}.
3322 *
3323 * @return single instance of {@code EmptySelectionModel}
3324 */
3325 public static EmptySelectionModel sharedInstance() {
3326 return sharedInstance;
3327 }
3399 * @since 1.7
3400 */
3401 public void addPropertyChangeListener(
3402 PropertyChangeListener listener) {
3403 }
3404
3405 /**
3406 * This is overriden to do nothing; {@code EmptySelectionModel}
3407 * does not allow a selection.
3408 *
3409 * @param listener the listener to remove; this is ignored
3410 * @since 1.7
3411 */
3412 public void removePropertyChangeListener(
3413 PropertyChangeListener listener) {
3414 }
3415 }
3416
3417
3418 /**
3419 * Handles creating a new {@code TreeSelectionEvent} with the
3420 * {@code JTree} as the
3421 * source and passing it off to all the listeners.
3422 * <p>
3423 * <strong>Warning:</strong>
3424 * Serialized objects of this class will not be compatible with
3425 * future Swing releases. The current serialization support is
3426 * appropriate for short term storage or RMI between applications running
3427 * the same version of Swing. As of 1.4, support for long term storage
3428 * of all JavaBeans™
3429 * has been added to the {@code java.beans} package.
3430 * Please see {@link java.beans.XMLEncoder}.
3431 */
3432 @SuppressWarnings("serial")
3433 protected class TreeSelectionRedirector implements Serializable,
3434 TreeSelectionListener
3435 {
3436 /**
3437 * Invoked by the {@code TreeSelectionModel} when the
3438 * selection changes.
3439 *
3440 * @param e the {@code TreeSelectionEvent} generated by the
3441 * {@code TreeSelectionModel}
3442 */
3443 public void valueChanged(TreeSelectionEvent e) {
3444 TreeSelectionEvent newE;
3445
3446 newE = (TreeSelectionEvent)e.cloneWithSource(JTree.this);
3447 fireValueChanged(newE);
3448 }
3449 } // End of class JTree.TreeSelectionRedirector
3450
3451 //
3452 // Scrollable interface
3453 //
3454
3455 /**
3456 * Returns the preferred display size of a {@code JTree}. The height is
3457 * determined from {@code getVisibleRowCount} and the width
3458 * is the current preferred width.
3459 *
3460 * @return a {@code Dimension} object containing the preferred size
3461 */
3462 public Dimension getPreferredScrollableViewportSize() {
3463 int width = getPreferredSize().width;
3464 int visRows = getVisibleRowCount();
3465 int height = -1;
3466
3467 if(isFixedRowHeight())
3468 height = visRows * getRowHeight();
3469 else {
3470 TreeUI ui = getUI();
3471
3472 if (ui != null && visRows > 0) {
3473 int rc = ui.getRowCount(this);
3474
3475 if (rc >= visRows) {
3476 Rectangle bounds = getRowBounds(visRows - 1);
3477 if (bounds != null) {
3478 height = bounds.y + bounds.height;
3479 }
3480 }
3482 Rectangle bounds = getRowBounds(0);
3483 if (bounds != null) {
3484 height = bounds.height * visRows;
3485 }
3486 }
3487 }
3488 if (height == -1) {
3489 height = 16 * visRows;
3490 }
3491 }
3492 return new Dimension(width, height);
3493 }
3494
3495 /**
3496 * Returns the amount to increment when scrolling. The amount is
3497 * the height of the first displayed row that isn't completely in view
3498 * or, if it is totally displayed, the height of the next row in the
3499 * scrolling direction.
3500 *
3501 * @param visibleRect the view area visible within the viewport
3502 * @param orientation either {@code SwingConstants.VERTICAL}
3503 * or {@code SwingConstants.HORIZONTAL}
3504 * @param direction less than zero to scroll up/left,
3505 * greater than zero for down/right
3506 * @return the "unit" increment for scrolling in the specified direction
3507 * @see JScrollBar#setUnitIncrement(int)
3508 */
3509 public int getScrollableUnitIncrement(Rectangle visibleRect,
3510 int orientation, int direction) {
3511 if(orientation == SwingConstants.VERTICAL) {
3512 Rectangle rowBounds;
3513 int firstIndex = getClosestRowForLocation
3514 (0, visibleRect.y);
3515
3516 if(firstIndex != -1) {
3517 rowBounds = getRowBounds(firstIndex);
3518 if(rowBounds.y != visibleRect.y) {
3519 if(direction < 0) {
3520 // UP
3521 return Math.max(0, (visibleRect.y - rowBounds.y));
3522 }
3523 return (rowBounds.y + rowBounds.height - visibleRect.y);
3524 }
3525 if(direction < 0) { // UP
3526 if(firstIndex != 0) {
3527 rowBounds = getRowBounds(firstIndex - 1);
3528 return rowBounds.height;
3529 }
3530 }
3531 else {
3532 return rowBounds.height;
3533 }
3534 }
3535 return 0;
3536 }
3537 return 4;
3538 }
3539
3540
3541 /**
3542 * Returns the amount for a block increment, which is the height or
3543 * width of {@code visibleRect}, based on {@code orientation}.
3544 *
3545 * @param visibleRect the view area visible within the viewport
3546 * @param orientation either {@code SwingConstants.VERTICAL}
3547 * or {@code SwingConstants.HORIZONTAL}
3548 * @param direction less than zero to scroll up/left,
3549 * greater than zero for down/right.
3550 * @return the "block" increment for scrolling in the specified direction
3551 * @see JScrollBar#setBlockIncrement(int)
3552 */
3553 public int getScrollableBlockIncrement(Rectangle visibleRect,
3554 int orientation, int direction) {
3555 return (orientation == SwingConstants.VERTICAL) ? visibleRect.height :
3556 visibleRect.width;
3557 }
3558
3559 /**
3560 * Returns false to indicate that the width of the viewport does not
3561 * determine the width of the table, unless the preferred width of
3562 * the tree is smaller than the viewports width. In other words:
3563 * ensure that the tree is never smaller than its viewport.
3564 *
3565 * @return whether the tree should track the width of the viewport
3566 * @see Scrollable#getScrollableTracksViewportWidth
3567 */
3574 }
3575
3576 /**
3577 * Returns false to indicate that the height of the viewport does not
3578 * determine the height of the table, unless the preferred height
3579 * of the tree is smaller than the viewports height. In other words:
3580 * ensure that the tree is never smaller than its viewport.
3581 *
3582 * @return whether the tree should track the height of the viewport
3583 * @see Scrollable#getScrollableTracksViewportHeight
3584 */
3585 public boolean getScrollableTracksViewportHeight() {
3586 Container parent = SwingUtilities.getUnwrappedParent(this);
3587 if (parent instanceof JViewport) {
3588 return parent.getHeight() > getPreferredSize().height;
3589 }
3590 return false;
3591 }
3592
3593 /**
3594 * Sets the expanded state of this {@code JTree}.
3595 * If {@code state} is
3596 * true, all parents of {@code path} and path are marked as
3597 * expanded. If {@code state} is false, all parents of
3598 * {@code path} are marked EXPANDED, but {@code path} itself
3599 * is marked collapsed.<p>
3600 * This will fail if a {@code TreeWillExpandListener} vetos it.
3601 *
3602 * @param path a {@code TreePath} identifying a node
3603 * @param state if {@code true}, all parents of @{code path} and path are marked as expanded.
3604 * Otherwise, all parents of {@code path} are marked EXPANDED,
3605 * but {@code path} itself is marked collapsed.
3606 */
3607 protected void setExpandedState(TreePath path, boolean state) {
3608 if(path != null) {
3609 // Make sure all parents of path are expanded.
3610 Stack<TreePath> stack;
3611 TreePath parentPath = path.getParentPath();
3612
3613 if (expandedStack.size() == 0) {
3614 stack = new Stack<TreePath>();
3615 }
3616 else {
3617 stack = expandedStack.pop();
3618 }
3619
3620 try {
3706 * @return the {@code Enumeration} of {@code TreePaths}
3707 */
3708 protected Enumeration<TreePath>
3709 getDescendantToggledPaths(TreePath parent)
3710 {
3711 if(parent == null)
3712 return null;
3713
3714 Vector<TreePath> descendants = new Vector<TreePath>();
3715 Enumeration<TreePath> nodes = expandedState.keys();
3716
3717 while(nodes.hasMoreElements()) {
3718 TreePath path = nodes.nextElement();
3719 if(parent.isDescendant(path))
3720 descendants.addElement(path);
3721 }
3722 return descendants.elements();
3723 }
3724
3725 /**
3726 * Removes any descendants of the {@code TreePaths} in
3727 * {@code toRemove}
3728 * that have been expanded.
3729 *
3730 * @param toRemove an enumeration of the paths to remove; a value of
3731 * {@code null} is ignored
3732 * @throws ClassCastException if {@code toRemove} contains an
3733 * element that is not a {@code TreePath}; {@code null}
3734 * values are ignored
3735 */
3736 protected void
3737 removeDescendantToggledPaths(Enumeration<TreePath> toRemove)
3738 {
3739 if(toRemove != null) {
3740 while(toRemove.hasMoreElements()) {
3741 Enumeration<?> descendants = getDescendantToggledPaths
3742 (toRemove.nextElement());
3743
3744 if(descendants != null) {
3745 while(descendants.hasMoreElements()) {
3746 expandedState.remove(descendants.nextElement());
3747 }
3748 }
3749 }
3750 }
3751 }
3752
3753 /**
3754 * Clears the cache of toggled tree paths. This does NOT send out
3755 * any {@code TreeExpansionListener} events.
3756 */
3757 protected void clearToggledPaths() {
3758 expandedState.clear();
3759 }
3760
3761 /**
3762 * Creates and returns an instance of {@code TreeModelHandler}.
3763 * The returned
3764 * object is responsible for updating the expanded state when the
3765 * {@code TreeModel} changes.
3766 * <p>
3767 * For more information on what expanded state means, see the
3768 * <a href=#jtree_description>JTree description</a> above.
3769 *
3770 * @return the instance of {@code TreeModelHandler}
3771 */
3772 protected TreeModelListener createTreeModelListener() {
3773 return new TreeModelHandler();
3774 }
3775
3776 /**
3777 * Removes any paths in the selection that are descendants of
3778 * {@code path}. If {@code includePath} is true and
3779 * {@code path} is selected, it will be removed from the selection.
3780 *
3781 * @param path a path
3782 * @param includePath is {@code true} and {@code path} is selected,
3783 * it will be removed from the selection.
3784 * @return true if a descendant was selected
3785 * @since 1.3
3786 */
3787 protected boolean removeDescendantSelectedPaths(TreePath path,
3788 boolean includePath) {
3789 TreePath[] toRemove = getDescendantSelectedPaths(path, includePath);
3790
3791 if (toRemove != null) {
3792 getSelectionModel().removeSelectionPaths(toRemove);
3793 return true;
3794 }
3795 return false;
3796 }
3797
3798 /**
3799 * Returns an array of paths in the selection that are descendants of
3800 * {@code path}. The returned array may contain {@code null}s.
3801 */
3802 private TreePath[] getDescendantSelectedPaths(TreePath path,
3803 boolean includePath) {
3804 TreeSelectionModel sm = getSelectionModel();
3805 TreePath[] selPaths = (sm != null) ? sm.getSelectionPaths() :
3806 null;
3807
3808 if(selPaths != null) {
3809 boolean shouldRemove = false;
3810
3811 for(int counter = selPaths.length - 1; counter >= 0; counter--) {
3812 if(selPaths[counter] != null &&
3813 path.isDescendant(selPaths[counter]) &&
3814 (!path.equals(selPaths[counter]) || includePath))
3815 shouldRemove = true;
3816 else
3817 selPaths[counter] = null;
3818 }
3819 if(!shouldRemove) {
3820 selPaths = null;
3821 }
3822 return selPaths;
3823 }
3824 return null;
3825 }
3826
3827 /**
3828 * Removes any paths from the selection model that are descendants of
3829 * the nodes identified by in {@code e}.
3830 */
3831 void removeDescendantSelectedPaths(TreeModelEvent e) {
3832 TreePath pPath = SwingUtilities2.getTreePath(e, getModel());
3833 Object[] oldChildren = e.getChildren();
3834 TreeSelectionModel sm = getSelectionModel();
3835
3836 if (sm != null && pPath != null && oldChildren != null &&
3837 oldChildren.length > 0) {
3838 for (int counter = oldChildren.length - 1; counter >= 0;
3839 counter--) {
3840 // Might be better to call getDescendantSelectedPaths
3841 // numerous times, then push to the model.
3842 removeDescendantSelectedPaths(pPath.pathByAddingChild
3843 (oldChildren[counter]), true);
3844 }
3845 }
3846 }
3847
3848
3849 /**
3850 * Listens to the model and updates the {@code expandedState}
3851 * accordingly when nodes are removed, or changed.
3852 */
3853 protected class TreeModelHandler implements TreeModelListener {
3854 public void treeNodesChanged(TreeModelEvent e) { }
3855
3856 public void treeNodesInserted(TreeModelEvent e) { }
3857
3858 public void treeStructureChanged(TreeModelEvent e) {
3859 if(e == null)
3860 return;
3861
3862 // NOTE: If I change this to NOT remove the descendants
3863 // and update BasicTreeUIs treeStructureChanged method
3864 // to update descendants in response to a treeStructureChanged
3865 // event, all the children of the event won't collapse!
3866 TreePath parent = SwingUtilities2.getTreePath(e, getModel());
3867
3868 if(parent == null)
3869 return;
3870
3915
3916 for(int counter = children.length - 1; counter >= 0; counter--) {
3917 rPath = parent.pathByAddingChild(children[counter]);
3918 if(expandedState.get(rPath) != null)
3919 toRemove.addElement(rPath);
3920 }
3921 if(toRemove.size() > 0)
3922 removeDescendantToggledPaths(toRemove.elements());
3923
3924 TreeModel model = getModel();
3925
3926 if(model == null || model.isLeaf(parent.getLastPathComponent()))
3927 expandedState.remove(parent);
3928
3929 removeDescendantSelectedPaths(e);
3930 }
3931 }
3932
3933
3934 /**
3935 * {@code DynamicUtilTreeNode} can wrap
3936 * vectors/hashtables/arrays/strings and
3937 * create the appropriate children tree nodes as necessary. It is
3938 * dynamic in that it will only create the children as necessary.
3939 * <p>
3940 * <strong>Warning:</strong>
3941 * Serialized objects of this class will not be compatible with
3942 * future Swing releases. The current serialization support is
3943 * appropriate for short term storage or RMI between applications running
3944 * the same version of Swing. As of 1.4, support for long term storage
3945 * of all JavaBeans™
3946 * has been added to the {@code java.beans} package.
3947 * Please see {@link java.beans.XMLEncoder}.
3948 */
3949 @SuppressWarnings("serial")
3950 public static class DynamicUtilTreeNode extends DefaultMutableTreeNode {
3951 /**
3952 * Does the this {@code JTree} have children?
3953 * This property is currently not implemented.
3954 */
3955 protected boolean hasChildren;
3956 /** Value to create children with. */
3957 protected Object childValue;
3958 /** Have the children been loaded yet? */
3959 protected boolean loadedChildren;
3960
3961 /**
3962 * Adds to parent all the children in {@code children}.
3963 * If {@code children} is an array or vector all of its
3964 * elements are added is children, otherwise if {@code children}
3965 * is a hashtable all the key/value pairs are added in the order
3966 * {@code Enumeration} returns them.
3967 *
3968 * @param parent the parent node
3969 * @param children the children
3970 */
3971 public static void createChildren(DefaultMutableTreeNode parent,
3972 Object children) {
3973 if(children instanceof Vector) {
3974 Vector<?> childVector = (Vector)children;
3975
3976 for(int counter = 0, maxCounter = childVector.size();
3977 counter < maxCounter; counter++)
3978 parent.add(new DynamicUtilTreeNode
3979 (childVector.elementAt(counter),
3980 childVector.elementAt(counter)));
3981 }
3982 else if(children instanceof Hashtable) {
3983 Hashtable<?,?> childHT = (Hashtable)children;
3984 Enumeration<?> keys = childHT.keys();
3985 Object aKey;
3986
3987 while(keys.hasMoreElements()) {
3988 aKey = keys.nextElement();
3989 parent.add(new DynamicUtilTreeNode(aKey,
3990 childHT.get(aKey)));
3991 }
3992 }
3993 else if(children instanceof Object[]) {
3994 Object[] childArray = (Object[])children;
3995
3996 for(int counter = 0, maxCounter = childArray.length;
3997 counter < maxCounter; counter++)
3998 parent.add(new DynamicUtilTreeNode(childArray[counter],
3999 childArray[counter]));
4000 }
4001 }
4002
4003 /**
4004 * Creates a node with the specified object as its value and
4005 * with the specified children. For the node to allow children,
4006 * the children-object must be an array of objects, a
4007 * {@code Vector}, or a {@code Hashtable} -- even
4008 * if empty. Otherwise, the node is not
4009 * allowed to have children.
4010 *
4011 * @param value the {@code Object} that is the value for the
4012 * new node
4013 * @param children an array of {@code Object}s, a
4014 * {@code Vector}, or a {@code Hashtable}
4015 * used to create the child nodes; if any other
4016 * object is specified, or if the value is
4017 * {@code null},
4018 * then the node is not allowed to have children
4019 */
4020 public DynamicUtilTreeNode(Object value, Object children) {
4021 super(value);
4022 loadedChildren = false;
4023 childValue = children;
4024 if(children != null) {
4025 if(children instanceof Vector)
4026 setAllowsChildren(true);
4027 else if(children instanceof Hashtable)
4028 setAllowsChildren(true);
4029 else if(children instanceof Object[])
4030 setAllowsChildren(true);
4031 else
4032 setAllowsChildren(false);
4033 }
4034 else
4035 setAllowsChildren(false);
4036 }
4037
4041 *
4042 * @return true if this node allows children, false otherwise
4043 * @see JTree.DynamicUtilTreeNode
4044 */
4045 public boolean isLeaf() {
4046 return !getAllowsChildren();
4047 }
4048
4049 /**
4050 * Returns the number of child nodes.
4051 *
4052 * @return the number of child nodes
4053 */
4054 public int getChildCount() {
4055 if(!loadedChildren)
4056 loadChildren();
4057 return super.getChildCount();
4058 }
4059
4060 /**
4061 * Loads the children based on {@code childValue}.
4062 * If {@code childValue} is a {@code Vector}
4063 * or array each element is added as a child,
4064 * if {@code childValue} is a {@code Hashtable}
4065 * each key/value pair is added in the order that
4066 * {@code Enumeration} returns the keys.
4067 */
4068 protected void loadChildren() {
4069 loadedChildren = true;
4070 createChildren(this, childValue);
4071 }
4072
4073 /**
4074 * Subclassed to load the children, if necessary.
4075 */
4076 public TreeNode getChildAt(int index) {
4077 if(!loadedChildren)
4078 loadChildren();
4079 return super.getChildAt(index);
4080 }
4081
4082 /**
4083 * Subclassed to load the children, if necessary.
4084 */
4085 public Enumeration<TreeNode> children() {
4086 if(!loadedChildren)
4095 setRowHeight(((Number)value).intValue());
4096 rowHeightSet = false;
4097 }
4098 } else if (propertyName == "scrollsOnExpand") {
4099 if (!scrollsOnExpandSet) {
4100 setScrollsOnExpand(((Boolean)value).booleanValue());
4101 scrollsOnExpandSet = false;
4102 }
4103 } else if (propertyName == "showsRootHandles") {
4104 if (!showsRootHandlesSet) {
4105 setShowsRootHandles(((Boolean)value).booleanValue());
4106 showsRootHandlesSet = false;
4107 }
4108 } else {
4109 super.setUIProperty(propertyName, value);
4110 }
4111 }
4112
4113
4114 /**
4115 * Returns a string representation of this {@code JTree}.
4116 * This method
4117 * is intended to be used only for debugging purposes, and the
4118 * content and format of the returned string may vary between
4119 * implementations. The returned string may be empty but may not
4120 * be {@code null}.
4121 *
4122 * @return a string representation of this {@code JTree}.
4123 */
4124 protected String paramString() {
4125 String rootVisibleString = (rootVisible ?
4126 "true" : "false");
4127 String showsRootHandlesString = (showsRootHandles ?
4128 "true" : "false");
4129 String editableString = (editable ?
4130 "true" : "false");
4131 String largeModelString = (largeModel ?
4132 "true" : "false");
4133 String invokesStopCellEditingString = (invokesStopCellEditing ?
4134 "true" : "false");
4135 String scrollsOnExpandString = (scrollsOnExpand ?
4136 "true" : "false");
4137
4138 return super.paramString() +
4139 ",editable=" + editableString +
4140 ",invokesStopCellEditing=" + invokesStopCellEditingString +
4141 ",largeModel=" + largeModelString +
4142 ",rootVisible=" + rootVisibleString +
4152 ////////////////
4153
4154 /**
4155 * Gets the AccessibleContext associated with this JTree.
4156 * For JTrees, the AccessibleContext takes the form of an
4157 * AccessibleJTree.
4158 * A new AccessibleJTree instance is created if necessary.
4159 *
4160 * @return an AccessibleJTree that serves as the
4161 * AccessibleContext of this JTree
4162 */
4163 public AccessibleContext getAccessibleContext() {
4164 if (accessibleContext == null) {
4165 accessibleContext = new AccessibleJTree();
4166 }
4167 return accessibleContext;
4168 }
4169
4170 /**
4171 * This class implements accessibility support for the
4172 * {@code JTree} class. It provides an implementation of the
4173 * Java Accessibility API appropriate to tree user-interface elements.
4174 * <p>
4175 * <strong>Warning:</strong>
4176 * Serialized objects of this class will not be compatible with
4177 * future Swing releases. The current serialization support is
4178 * appropriate for short term storage or RMI between applications running
4179 * the same version of Swing. As of 1.4, support for long term storage
4180 * of all JavaBeans™
4181 * has been added to the {@code java.beans} package.
4182 * Please see {@link java.beans.XMLEncoder}.
4183 */
4184 @SuppressWarnings("serial")
4185 protected class AccessibleJTree extends AccessibleJComponent
4186 implements AccessibleSelection, TreeSelectionListener,
4187 TreeModelListener, TreeExpansionListener {
4188
4189 TreePath leadSelectionPath;
4190 Accessible leadSelectionAccessible;
4191
4192 /**
4193 * Constructs {@code AccessibleJTree}
4194 */
4195 public AccessibleJTree() {
4196 // Add a tree model listener for JTree
4197 TreeModel model = JTree.this.getModel();
4198 if (model != null) {
4199 model.addTreeModelListener(this);
4200 }
4201 JTree.this.addTreeExpansionListener(this);
4387 model.isLeaf(treeRoot), row, hasFocus);
4388 }
4389 }
4390 return null;
4391 }
4392
4393 // Overridden methods from AccessibleJComponent
4394
4395 /**
4396 * Get the role of this object.
4397 *
4398 * @return an instance of AccessibleRole describing the role of the
4399 * object
4400 * @see AccessibleRole
4401 */
4402 public AccessibleRole getAccessibleRole() {
4403 return AccessibleRole.TREE;
4404 }
4405
4406 /**
4407 * Returns the {@code Accessible} child, if one exists,
4408 * contained at the local coordinate {@code Point}.
4409 * Otherwise returns {@code null}.
4410 *
4411 * @param p point in local coordinates of this {@code Accessible}
4412 * @return the {@code Accessible}, if it exists,
4413 * at the specified location; else {@code null}
4414 */
4415 public Accessible getAccessibleAt(Point p) {
4416 TreePath path = getClosestPathForLocation(p.x, p.y);
4417 if (path != null) {
4418 // JTree.this is NOT the parent; parent will get computed later
4419 return new AccessibleJTreeNode(JTree.this, path, null);
4420 } else {
4421 return null;
4422 }
4423 }
4424
4425 /**
4426 * Returns the number of top-level children nodes of this
4427 * JTree. Each of these nodes may in turn have children nodes.
4428 *
4429 * @return the number of accessible children nodes in the tree.
4430 */
4431 public int getAccessibleChildrenCount() {
4432 TreeModel model = JTree.this.getModel();
4433 if (model == null) {
4634 }
4635
4636 /**
4637 * Causes every selected item in the object to be selected
4638 * if the object supports multiple selections.
4639 */
4640 public void selectAllAccessibleSelection() {
4641 TreeModel model = JTree.this.getModel();
4642 if (model != null) {
4643 Object[] objPath = {model.getRoot()};
4644 if (objPath[0] == null)
4645 return;
4646
4647 TreePath path = new TreePath(objPath);
4648 JTree.this.addSelectionPath(path);
4649 }
4650 }
4651
4652 /**
4653 * This class implements accessibility support for the
4654 * {@code JTree} child. It provides an implementation of the
4655 * Java Accessibility API appropriate to tree nodes.
4656 */
4657 protected class AccessibleJTreeNode extends AccessibleContext
4658 implements Accessible, AccessibleComponent, AccessibleSelection,
4659 AccessibleAction {
4660
4661 private JTree tree = null;
4662 private TreeModel treeModel = null;
4663 private Object obj = null;
4664 private TreePath path = null;
4665 private Accessible accessibleParent = null;
4666 private int index = 0;
4667 private boolean isLeaf = false;
4668
4669 /**
4670 * Constructs an AccessibleJTreeNode
4671 *
4672 * @param t an instance of {@code JTree}
4673 * @param p an instance of {@code TreePath}
4674 * @param ap an instance of {@code Accessible}
5357 }
5358 }
5359
5360 public Dimension getSize() {
5361 return getBounds().getSize();
5362 }
5363
5364 public void setSize (Dimension d) {
5365 AccessibleContext ac = getCurrentAccessibleContext();
5366 if (ac instanceof AccessibleComponent) {
5367 ((AccessibleComponent) ac).setSize(d);
5368 } else {
5369 Component c = getCurrentComponent();
5370 if (c != null) {
5371 c.setSize(d);
5372 }
5373 }
5374 }
5375
5376 /**
5377 * Returns the {@code Accessible} child, if one exists,
5378 * contained at the local coordinate {@code Point}.
5379 * Otherwise returns {@code null}.
5380 *
5381 * @param p point in local coordinates of this
5382 * {@code Accessible}
5383 * @return the {@code Accessible}, if it exists,
5384 * at the specified location; else {@code null}
5385 */
5386 public Accessible getAccessibleAt(Point p) {
5387 AccessibleContext ac = getCurrentAccessibleContext();
5388 if (ac instanceof AccessibleComponent) {
5389 return ((AccessibleComponent) ac).getAccessibleAt(p);
5390 } else {
5391 return null;
5392 }
5393 }
5394
5395 @SuppressWarnings("deprecation")
5396 public boolean isFocusTraversable() {
5397 AccessibleContext ac = getCurrentAccessibleContext();
5398 if (ac instanceof AccessibleComponent) {
5399 return ((AccessibleComponent) ac).isFocusTraversable();
5400 } else {
5401 Component c = getCurrentComponent();
5402 if (c != null) {
5403 return c.isFocusTraversable();
5404 } else {
|