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