History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/JTree.java (1 of 18352 files). Show all

Unified · Split

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

504 more lines aren’t shown here.