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