2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
40 import java.awt.Color;
41 import java.awt.Cursor;
42 import java.awt.Dimension;
44 import java.awt.FontMetrics;
45 import java.awt.Point;
46 import java.awt.Rectangle;
47 import java.awt.event.FocusListener;
48 import java.beans.PropertyChangeListener;
49 import java.io.Serializable;
50 import java.util.Enumeration;
51 import java.util.Hashtable;
52 import java.util.Iterator;
53 import java.util.Locale;
54 import java.util.Vector;
56 import javax.accessibility.Accessible;
57 import javax.accessibility.AccessibleAction;
58 import javax.accessibility.AccessibleComponent;
59 import javax.accessibility.AccessibleContext;
60 import javax.accessibility.AccessibleRole;
61 import javax.accessibility.AccessibleSelection;
62 import javax.accessibility.AccessibleState;
63 import javax.accessibility.AccessibleStateSet;
64 import javax.accessibility.AccessibleText;
65 import javax.accessibility.AccessibleValue;
66 import javax.swing.event.TreeExpansionEvent;
67 import javax.swing.event.TreeExpansionListener;
68 import javax.swing.event.TreeModelEvent;
69 import javax.swing.event.TreeModelListener;
70 import javax.swing.event.TreeSelectionEvent;
71 import javax.swing.event.TreeSelectionListener;
72 import javax.swing.event.TreeWillExpandListener;
73 import javax.swing.plaf.TreeUI;
74 import javax.swing.text.Position;
75 import javax.swing.tree.DefaultMutableTreeNode;
76 import javax.swing.tree.DefaultTreeModel;
77 import javax.swing.tree.DefaultTreeSelectionModel;
78 import javax.swing.tree.ExpandVetoException;
79 import javax.swing.tree.TreeCellEditor;
80 import javax.swing.tree.TreeCellRenderer;
81 import javax.swing.tree.TreeModel;
82 import javax.swing.tree.TreeNode;
83 import javax.swing.tree.TreePath;
84 import javax.swing.tree.TreeSelectionModel;
86 public class JTree extends JComponent implements Scrollable, Accessible
90 * This class implements accessibility support for the JTree class. It
91 * provides an implementation of the Java Accessibility API appropriate
92 * to tree user-interface elements.
94 protected class AccessibleJTree extends JComponent.AccessibleJComponent
95 implements AccessibleSelection, TreeSelectionListener, TreeModelListener,
100 * This class implements accessibility support for the JTree child. It provides
101 * an implementation of the Java Accessibility API appropriate to tree nodes.
103 protected class AccessibleJTreeNode extends AccessibleContext
104 implements Accessible, AccessibleComponent, AccessibleSelection,
110 private Accessible acc;
111 private AccessibleStateSet states;
112 private Vector selectionList;
113 private Vector actionList;
114 private TreeModel mod;
115 private Cursor cursor;
118 * Constructs an AccessibleJTreeNode
120 * @param t - the current tree
121 * @param p - the current path to be dealt with
122 * @param ap - the accessible object to use
124 public AccessibleJTreeNode(JTree t, TreePath p, Accessible ap)
126 states = new AccessibleStateSet();
127 selectionList = new Vector();
128 actionList = new Vector();
129 mod = tree.getModel();
130 cursor = JTree.this.getCursor();
136 // Add all the children of this path that may already be
137 // selected to the selection list.
138 TreePath[] selected = tree.getSelectionPaths();
139 for (int i = 0; i < selected.length; i++)
141 TreePath sel = selected[i];
142 if ((sel.getParentPath()).equals(tp))
143 selectionList.add(sel);
146 // Add all the actions available for a node to
148 actionList.add("EXPAND");
149 actionList.add("COLLAPSE");
150 actionList.add("EDIT");
151 actionList.add("SELECT");
152 actionList.add("DESELECT");
156 * Adds the specified selected item in the object to the object's
159 * @param i - the i-th child of this node.
161 public void addAccessibleSelection(int i)
165 Object child = mod.getChild(tp.getLastPathComponent(), i);
168 if (!states.contains(AccessibleState.MULTISELECTABLE))
169 clearAccessibleSelection();
170 selectionList.add(child);
171 tree.addSelectionPath(tp.pathByAddingChild(child));
177 * Adds the specified focus listener to receive focus events
178 * from this component.
180 * @param l - the new focus listener
182 public void addFocusListener(FocusListener l)
184 tree.addFocusListener(l);
188 * Add a PropertyChangeListener to the listener list.
190 * @param l - the new property change listener
192 public void addPropertyChangeListener(PropertyChangeListener l)
194 // Nothing to do here.
198 * Clears the selection in the object, so that nothing in the
199 * object is selected.
201 public void clearAccessibleSelection()
203 selectionList.clear();
207 * Checks whether the specified point is within this object's
208 * bounds, where the point's x and y coordinates are defined to be
209 * relative to the coordinate system of the object.
211 * @param p - the point to check
212 * @return true if p is in the bounds
214 public boolean contains(Point p)
216 return getBounds().contains(p);
220 * Perform the specified Action on the tree node.
222 * @param i - the i-th action to perform
223 * @return true if the the action was performed; else false.
225 public boolean doAccessibleAction(int i)
227 if (i >= actionList.size() || i < 0)
230 if (actionList.get(i).equals("EXPAND"))
232 else if (actionList.get(i).equals("COLLAPSE"))
233 tree.collapsePath(tp);
234 else if (actionList.get(i).equals("SELECT"))
235 tree.addSelectionPath(tp);
236 else if (actionList.get(i).equals("DESELECT"))
237 tree.removeSelectionPath(tp);
238 else if (actionList.get(i).equals("EDIT"))
239 tree.startEditingAtPath(tp);
246 * Get the AccessibleAction associated with this object.
250 public AccessibleAction getAccessibleAction()
256 * Returns the number of accessible actions available in this tree node.
258 * @return the number of actions
260 public int getAccessibleActionCount()
262 return actionList.size();
266 * Return a description of the specified action of the tree node.
268 * @param i - the i-th action's description
269 * @return a description of the action
271 public String getAccessibleActionDescription(int i)
273 if (i < 0 || i >= actionList.size())
274 return (actionList.get(i)).toString();
275 return super.getAccessibleDescription();
279 * Returns the Accessible child, if one exists, contained at the
280 * local coordinate Point.
282 * @param p - the point of the accessible
283 * @return the accessible at point p if it exists
285 public Accessible getAccessibleAt(Point p)
287 TreePath acc = tree.getClosestPathForLocation(p.x, p.y);
289 return new AccessibleJTreeNode(tree, acc, this);
294 * Return the specified Accessible child of the object.
296 * @param i - the i-th child of the current path
297 * @return the child if it exists
299 public Accessible getAccessibleChild(int i)
303 Object child = mod.getChild(tp.getLastPathComponent(), i);
305 return new AccessibleJTreeNode(tree, tp.pathByAddingChild(child),
312 * Returns the number of accessible children in the object.
314 * @return the number of children the current node has
316 public int getAccessibleChildrenCount()
318 TreeModel mod = getModel();
320 return mod.getChildCount(tp.getLastPathComponent());
325 * Get the AccessibleComponent associated with this object.
327 * @return the accessible component if it is supported.
329 public AccessibleComponent getAccessibleComponent()
335 * Get the AccessibleContext associated with this tree node.
337 * @return an instance of this class
339 public AccessibleContext getAccessibleContext()
345 * Get the accessible description of this object.
347 * @return the accessible description
349 public String getAccessibleDescription()
351 return super.getAccessibleDescription();
355 * Get the index of this object in its accessible parent.
357 * @return the index of this in the parent.
359 public int getAccessibleIndexInParent()
361 AccessibleContext parent = getAccessibleParent().getAccessibleContext();
363 for (int i = 0; i < parent.getAccessibleChildrenCount(); i++)
365 if ((parent.getAccessibleChild(i)).equals(this))
372 * Get the accessible name of this object.
374 * @return the accessible name
376 public String getAccessibleName()
378 return super.getAccessibleName();
382 * Get the Accessible parent of this object.
384 * @return the accessible parent if it exists.
386 public Accessible getAccessibleParent()
388 return super.getAccessibleParent();
392 * Get the role of this object.
394 * @return the accessible role
396 public AccessibleRole getAccessibleRole()
398 return AccessibleJTree.this.getAccessibleRole();
402 * Get the AccessibleSelection associated with this object if one exists.
404 * @return the accessible selection for this.
406 public AccessibleSelection getAccessibleSelection()
412 * Returns an Accessible representing the specified selected item
415 * @return the accessible representing a certain selected item.
417 public Accessible getAccessibleSelection(int i)
419 if (i > 0 && i < getAccessibleSelectionCount())
420 return new AccessibleJTreeNode(tree,
421 tp.pathByAddingChild(selectionList.get(i)), acc);
426 * Returns the number of items currently selected.
428 * @return the number of items selected.
430 public int getAccessibleSelectionCount()
432 return selectionList.size();
436 * Get the state set of this object.
438 * @return the state set for this object
440 public AccessibleStateSet getAccessibleStateSet()
443 states.add(AccessibleState.VISIBLE);
444 if (tree.isCollapsed(tp))
445 states.add(AccessibleState.COLLAPSED);
446 if (tree.isEditable())
447 states.add(AccessibleState.EDITABLE);
449 !mod.isLeaf(tp.getLastPathComponent()))
450 states.add(AccessibleState.EXPANDABLE);
451 if (tree.isExpanded(tp))
452 states.add(AccessibleState.EXPANDED);
454 states.add(AccessibleState.FOCUSABLE);
456 states.add(AccessibleState.FOCUSED);
457 if (tree.getSelectionModel().getSelectionMode() !=
458 TreeSelectionModel.SINGLE_TREE_SELECTION)
459 states.add(AccessibleState.MULTISELECTABLE);
461 states.add(AccessibleState.OPAQUE);
462 if (tree.isPathSelected(tp))
463 states.add(AccessibleState.SELECTED);
465 states.add(AccessibleState.SHOWING);
467 states.add(AccessibleState.SELECTABLE);
472 * Get the AccessibleText associated with this object if one exists.
474 * @return the accessible text
476 public AccessibleText getAccessibleText()
478 return super.getAccessibleText();
482 * Get the AccessibleValue associated with this object if one exists.
484 * @return the accessible value if it exists
486 public AccessibleValue getAccessibleValue()
488 return super.getAccessibleValue();
492 * Get the background color of this object.
494 * @return the color of the background.
496 public Color getBackground()
498 return tree.getBackground();
502 * Gets the bounds of this object in the form of a Rectangle object.
504 * @return the bounds of the current node.
506 public Rectangle getBounds()
508 return tree.getPathBounds(tp);
512 * Gets the Cursor of this object.
514 * @return the cursor for the current node
516 public Cursor getCursor()
522 * Gets the Font of this object.
524 * @return the font for the current node
526 public Font getFont()
528 return tree.getFont();
532 * Gets the FontMetrics of this object.
534 * @param f - the current font.
535 * @return the font metrics for the given font.
537 public FontMetrics getFontMetrics(Font f)
539 return tree.getFontMetrics(f);
543 * Get the foreground color of this object.
545 * @return the foreground for this object.
547 public Color getForeground()
549 return tree.getForeground();
553 * Gets the locale of the component.
555 * @return the locale of the component.
557 public Locale getLocale()
559 return tree.getLocale();
563 * Gets the location of the object relative to the
564 * parent in the form of a point specifying the object's
565 * top-left corner in the screen's coordinate space.
567 * @return the location of the current node.
569 public Point getLocation()
571 return getLocationInJTree();
575 * Returns the location in the tree.
577 * @return the location in the JTree.
579 protected Point getLocationInJTree()
581 Rectangle bounds = tree.getPathBounds(tp);
582 return new Point(bounds.x, bounds.y);
586 * Returns the location of the object on the screen.
588 * @return the location of the object on the screen.
590 public Point getLocationOnScreen()
592 Point loc = getLocation();
593 SwingUtilities.convertPointToScreen(loc, tree);
598 * Returns the size of this object in the form of a Dimension object.
600 * @return the size of the object
602 public Dimension getSize()
604 Rectangle b = getBounds();
609 * Returns true if the current child of this object is selected.
611 * @param i - the child of the current node
612 * @return true if the child is selected.
614 public boolean isAccessibleChildSelected(int i)
616 Object child = mod.getChild(tp.getLastPathComponent(), i);
618 return tree.isPathSelected(tp.pathByAddingChild(child));
623 * Determines if the object is enabled.
625 * @return true if the tree is enabled
627 public boolean isEnabled()
629 return tree.isEnabled();
633 * Returns whether this object can accept focus or not.
635 * @return true, it is always focus traversable
637 public boolean isFocusTraversable()
643 * Determines if the object is showing.
645 * @return true if the object is visible and the
648 public boolean isShowing()
650 return isVisible() && tree.isShowing();
654 * Determines if the object is visible.
656 * @return true if the object is visible.
658 public boolean isVisible()
660 return tree.isVisible(tp);
664 * Removes the specified selected item in the object from the
665 * object's selection.
667 * @param i - the specified item to remove
669 public void removeAccessibleSelection(int i)
673 Object child = mod.getChild(tp.getLastPathComponent(), i);
676 if (!states.contains(AccessibleState.MULTISELECTABLE))
677 clearAccessibleSelection();
678 if (selectionList.contains(child))
680 selectionList.remove(child);
681 tree.removeSelectionPath(tp.pathByAddingChild(child));
688 * Removes the specified focus listener so it no longer receives focus
689 * events from this component.
691 * @param l - the focus listener to remove
693 public void removeFocusListener(FocusListener l)
695 tree.removeFocusListener(l);
699 * Remove a PropertyChangeListener from the listener list.
701 * @param l - the property change listener to remove.
703 public void removePropertyChangeListener(PropertyChangeListener l)
705 // Nothing to do here.
709 * Requests focus for this object.
711 public void requestFocus()
717 * Causes every selected item in the object to be selected if the object
718 * supports multiple selections.
720 public void selectAllAccessibleSelection()
722 Object parent = tp.getLastPathComponent();
725 for (int i = 0; i < mod.getChildCount(parent); i++)
727 Object child = mod.getChild(parent, i);
730 if (!states.contains(AccessibleState.MULTISELECTABLE))
731 clearAccessibleSelection();
732 if (selectionList.contains(child))
734 selectionList.add(child);
735 tree.addSelectionPath(tp.pathByAddingChild(child));
743 * Set the accessible description of this object.
745 * @param s - the string to set the accessible description to.
747 public void setAccessibleDescription(String s)
749 super.setAccessibleDescription(s);
753 * Set the localized accessible name of this object.
755 * @param s - the string to set the accessible name to.
757 public void setAccessibleName(String s)
759 super.setAccessibleName(s);
763 * Set the background color of this object.
765 * @param c - the color to set the background to.
767 public void setBackground(Color c)
769 // Nothing to do here.
773 * Sets the bounds of this object in the form of a Rectangle object.
775 * @param r - the bounds to set the object o
777 public void setBounds(Rectangle r)
779 // Nothing to do here.
783 * Sets the Cursor of this object.
785 * @param c - the new cursor
787 public void setCursor(Cursor c)
793 * Sets the enabled state of the object.
795 * @param b - boolean to enable or disable object
797 public void setEnabled(boolean b)
799 // Nothing to do here.
803 * Sets the Font of this object.
805 * @param f - the new font.
807 public void setFont(Font f)
809 // Nothing to do here.
813 * Sets the foreground color of this object.
815 * @param c - the new foreground color.
817 public void setForeground(Color c)
819 // Nothing to do here.
823 * Sets the location of the object relative to the parent.
825 * @param p - the new location for the object.
827 public void setLocation(Point p)
829 // Nothing to do here.
833 * Resizes this object so that it has width and height.
835 * @param d - the new size for the object.
837 public void setSize(Dimension d)
839 // Nothing to do here.
843 * Sets the visible state of the object.
845 * @param b - sets the objects visibility.
847 public void setVisible(boolean b)
849 // Nothing to do here.
856 public AccessibleJTree()
858 // Nothing to do here.
862 * Adds the specified selected item in the object to the object's selection.
864 * @param i - the row to add to the tree's selection
866 public void addAccessibleSelection(int i)
868 addSelectionInterval(i, i);
872 * Clears the selection in the object, so that nothing in the object is selected.
874 public void clearAccessibleSelection()
880 * Fire a visible data property change notification.
882 public void fireVisibleDataPropertyChange()
888 * Returns the Accessible child, if one exists, contained at the local
891 * @param p - the point of the accessible to get.
892 * @return the accessible at point p.
894 public Accessible getAccessibleAt(Point p)
896 TreePath tp = getClosestPathForLocation(p.x, p.y);
898 return new AccessibleJTreeNode(JTree.this, tp, null);
903 * Return the nth Accessible child of the object.
905 * @param i - the accessible child to get
906 * @return the i-th child
908 public Accessible getAccessibleChild(int i)
914 * Returns the number of top-level children nodes of this JTree.
916 * @return the number of top-level children
918 public int getAccessibleChildrenCount()
920 TreeModel model = getModel();
922 return model.getChildCount(model.getRoot());
927 * Get the index of this object in its accessible parent.
929 * @return the index of this object.
931 public int getAccessibleIndexInParent()
937 * Get the role of this object.
939 * @return the role of this object
941 public AccessibleRole getAccessibleRole()
943 return AccessibleRole.TREE;
947 * Get the AccessibleSelection associated with this object.
949 * @return the accessible selection of the tree
951 public AccessibleSelection getAccessibleSelection()
953 TreeModel mod = getModel();
955 return (new AccessibleJTreeNode(JTree.this,
956 new TreePath(mod.getRoot()), null)).getAccessibleSelection();
961 * Returns an Accessible representing the specified selected item in the object.
963 * @return the i-th accessible in the selection
965 public Accessible getAccessibleSelection(int i)
967 TreeModel mod = getModel();
969 return (new AccessibleJTreeNode(JTree.this,
970 new TreePath(mod.getRoot()), null)).getAccessibleSelection(i);
975 * Returns the number of items currently selected.
977 * @return the number of selected accessibles.
979 public int getAccessibleSelectionCount()
981 return getSelectionCount();
985 * Returns true if the current child of this object is selected.
987 * @param i - the child of this object
988 * @return true if the i-th child is selected.
990 public boolean isAccessibleChildSelected(int i)
992 // Nothing to do here.
997 * Removes the specified selected item in the object from the object's
1000 * @param i - the i-th selected item to remove
1002 public void removeAccessibleSelection(int i)
1004 removeSelectionInterval(i, i);
1008 * Causes every selected item in the object to be selected if the object
1009 * supports multiple selections.
1011 public void selectAllAccessibleSelection()
1013 if (getSelectionModel().getSelectionMode() !=
1014 TreeSelectionModel.SINGLE_TREE_SELECTION)
1015 addSelectionInterval(0, getVisibleRowCount());
1019 * Tree Collapsed notification
1021 * @param e - the event
1023 public void treeCollapsed(TreeExpansionEvent e)
1025 fireTreeCollapsed(e.getPath());
1029 * Tree Model Expansion notification.
1031 * @param e - the event
1033 public void treeExpanded(TreeExpansionEvent e)
1035 fireTreeExpanded(e.getPath());
1039 * Tree Model Node change notification.
1041 * @param e - the event
1043 public void treeNodesChanged(TreeModelEvent e)
1045 // Nothing to do here.
1049 * Tree Model Node change notification.
1051 * @param e - the event
1053 public void treeNodesInserted(TreeModelEvent e)
1055 // Nothing to do here.
1059 * Tree Model Node change notification.
1061 * @param e - the event
1063 public void treeNodesRemoved(TreeModelEvent e)
1065 // Nothing to do here.
1069 * Tree Model structure change change notification.
1071 * @param e - the event
1073 public void treeStructureChanged(TreeModelEvent e)
1075 // Nothing to do here.
1079 * Tree Selection Listener value change method.
1081 * @param e - the event
1083 public void valueChanged(TreeSelectionEvent e)
1085 fireValueChanged(e);
1089 public static class DynamicUtilTreeNode extends DefaultMutableTreeNode
1091 protected Object childValue;
1093 protected boolean loadedChildren;
1096 * Currently not set or used by this class. It might be set and used in
1097 * later versions of this class.
1099 protected boolean hasChildren;
1101 public DynamicUtilTreeNode(Object value, Object children)
1104 childValue = children;
1105 loadedChildren = false;
1108 public int getChildCount()
1111 return super.getChildCount();
1114 protected void loadChildren()
1116 if (!loadedChildren)
1118 createChildren(this, childValue);
1119 loadedChildren = true;
1123 public Enumeration children()
1126 return super.children();
1130 * Returns the child node at position <code>pos</code>. Subclassed
1131 * here to load the children if necessary.
1133 * @param pos the position of the child node to fetch
1135 * @return the childnode at the specified position
1137 public TreeNode getChildAt(int pos)
1140 return super.getChildAt(pos);
1143 public boolean isLeaf()
1145 return childValue == null || !(childValue instanceof Hashtable
1146 || childValue instanceof Vector
1147 || childValue.getClass().isArray());
1150 public static void createChildren(DefaultMutableTreeNode parent,
1153 if (children instanceof Hashtable)
1155 Hashtable tab = (Hashtable) children;
1156 Enumeration e = tab.keys();
1157 while (e.hasMoreElements())
1159 Object key = e.nextElement();
1160 Object val = tab.get(key);
1161 parent.add(new DynamicUtilTreeNode(key, val));
1164 else if (children instanceof Vector)
1166 Iterator i = ((Vector) children).iterator();
1169 Object n = i.next();
1170 parent.add(new DynamicUtilTreeNode(n, n));
1173 else if (children != null && children.getClass().isArray())
1175 Object[] arr = (Object[]) children;
1176 for (int i = 0; i < arr.length; ++i)
1177 parent.add(new DynamicUtilTreeNode(arr[i], arr[i]));
1183 * Listens to the model of the JTree and updates the property
1184 * <code>expandedState</code> if nodes are removed or changed.
1186 protected class TreeModelHandler implements TreeModelListener
1190 * Creates a new instance of TreeModelHandler.
1192 protected TreeModelHandler()
1194 // Nothing to do here.
1198 * Notifies when a node has changed in some ways. This does not include
1199 * that a node has changed its location or changed it's children. It
1200 * only means that some attributes of the node have changed that might
1201 * affect its presentation.
1203 * This method is called after the actual change occured.
1205 * @param ev the TreeModelEvent describing the change
1207 public void treeNodesChanged(TreeModelEvent ev)
1209 // Nothing to do here.
1213 * Notifies when a node is inserted into the tree.
1215 * This method is called after the actual change occured.
1217 * @param ev the TreeModelEvent describing the change
1219 public void treeNodesInserted(TreeModelEvent ev)
1221 // nothing to do here
1225 * Notifies when a node is removed from the tree.
1227 * This method is called after the actual change occured.
1229 * @param ev the TreeModelEvent describing the change
1231 public void treeNodesRemoved(TreeModelEvent ev)
1233 // TODO: The API docs suggest that this method should do something
1234 // but I cannot really see what has to be done here ...
1238 * Notifies when the structure of the tree is changed.
1240 * This method is called after the actual change occured.
1242 * @param ev the TreeModelEvent describing the change
1244 public void treeStructureChanged(TreeModelEvent ev)
1246 // Set state of new path.
1247 TreePath path = ev.getTreePath();
1248 setExpandedState(path, isExpanded(path));
1253 * This redirects TreeSelectionEvents and rewrites the source of it to be
1254 * this JTree. This is typically done when the tree model generates an
1255 * event, but the JTree object associated with that model should be listed
1256 * as the actual source of the event.
1258 protected class TreeSelectionRedirector implements TreeSelectionListener,
1261 /** The serial version UID. */
1262 private static final long serialVersionUID = -3505069663646241664L;
1265 * Creates a new instance of TreeSelectionRedirector
1267 protected TreeSelectionRedirector()
1269 // Nothing to do here.
1273 * Notifies when the tree selection changes.
1275 * @param ev the TreeSelectionEvent that describes the change
1277 public void valueChanged(TreeSelectionEvent ev)
1279 TreeSelectionEvent rewritten =
1280 (TreeSelectionEvent) ev.cloneWithSource(JTree.this);
1281 fireValueChanged(rewritten);
1283 // Only repaint the changed nodes.
1284 TreePath[] changed = ev.getPaths();
1285 for (int i = 0; i < changed.length; i++)
1287 repaint(getPathBounds(changed[i]));
1293 * A TreeModel that does not allow anything to be selected.
1295 protected static class EmptySelectionModel extends DefaultTreeSelectionModel
1297 /** The serial version UID. */
1298 private static final long serialVersionUID = -5815023306225701477L;
1301 * The shared instance of this model.
1303 protected static final EmptySelectionModel sharedInstance =
1304 new EmptySelectionModel();
1307 * Creates a new instance of EmptySelectionModel.
1309 protected EmptySelectionModel()
1311 // Nothing to do here.
1315 * Returns the shared instance of EmptySelectionModel.
1317 * @return the shared instance of EmptySelectionModel
1319 public static EmptySelectionModel sharedInstance()
1321 return sharedInstance;
1325 * This catches attempts to set a selection and sets nothing instead.
1327 * @param paths not used here
1329 public void setSelectionPaths(TreePath[] paths)
1331 // We don't allow selections in this class.
1335 * This catches attempts to add something to the selection.
1337 * @param paths not used here
1339 public void addSelectionPaths(TreePath[] paths)
1341 // We don't allow selections in this class.
1345 * This catches attempts to remove something from the selection.
1347 * @param paths not used here
1349 public void removeSelectionPaths(TreePath[] paths)
1351 // We don't allow selections in this class.
1355 private static final long serialVersionUID = 7559816092864483649L;
1357 public static final String CELL_EDITOR_PROPERTY = "cellEditor";
1359 public static final String CELL_RENDERER_PROPERTY = "cellRenderer";
1361 public static final String EDITABLE_PROPERTY = "editable";
1363 public static final String INVOKES_STOP_CELL_EDITING_PROPERTY =
1364 "invokesStopCellEditing";
1366 public static final String LARGE_MODEL_PROPERTY = "largeModel";
1368 public static final String ROOT_VISIBLE_PROPERTY = "rootVisible";
1370 public static final String ROW_HEIGHT_PROPERTY = "rowHeight";
1372 public static final String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
1374 public static final String SELECTION_MODEL_PROPERTY = "selectionModel";
1376 public static final String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
1378 public static final String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
1380 public static final String TREE_MODEL_PROPERTY = "model";
1382 public static final String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
1385 public static final String ANCHOR_SELECTION_PATH_PROPERTY =
1386 "anchorSelectionPath";
1389 public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
1392 public static final String EXPANDS_SELECTED_PATHS_PROPERTY =
1393 "expandsSelectedPaths";
1395 private static final Object EXPANDED = new Object();
1397 private static final Object COLLAPSED = new Object();
1399 private boolean dragEnabled;
1401 private boolean expandsSelectedPaths;
1403 private TreePath anchorSelectionPath;
1406 * This contains the state of all nodes in the tree. Al/ entries map the
1407 * TreePath of a note to to its state. Valid states are EXPANDED and
1408 * COLLAPSED. Nodes not in this Hashtable are assumed state COLLAPSED.
1410 private Hashtable nodeStates = new Hashtable();
1412 protected transient TreeCellEditor cellEditor;
1414 protected transient TreeCellRenderer cellRenderer;
1416 protected boolean editable;
1418 protected boolean invokesStopCellEditing;
1420 protected boolean largeModel;
1422 protected boolean rootVisible;
1424 protected int rowHeight;
1426 protected boolean scrollsOnExpand;
1428 protected transient TreeSelectionModel selectionModel;
1430 protected boolean showsRootHandles;
1432 protected int toggleClickCount;
1434 protected transient TreeModel treeModel;
1436 protected int visibleRowCount;
1439 * Handles TreeModelEvents to update the expandedState.
1441 protected transient TreeModelListener treeModelListener;
1444 * Redirects TreeSelectionEvents so that the source is this JTree.
1446 protected TreeSelectionRedirector selectionRedirector =
1447 new TreeSelectionRedirector();
1450 * Indicates if the rowHeight property has been set by a client
1451 * program or by the UI.
1453 * @see #setUIProperty(String, Object)
1454 * @see LookAndFeel#installProperty(JComponent, String, Object)
1456 private boolean clientRowHeightSet = false;
1459 * Indicates if the scrollsOnExpand property has been set by a client
1460 * program or by the UI.
1462 * @see #setUIProperty(String, Object)
1463 * @see LookAndFeel#installProperty(JComponent, String, Object)
1465 private boolean clientScrollsOnExpandSet = false;
1468 * Indicates if the showsRootHandles property has been set by a client
1469 * program or by the UI.
1471 * @see #setUIProperty(String, Object)
1472 * @see LookAndFeel#installProperty(JComponent, String, Object)
1474 private boolean clientShowsRootHandlesSet = false;
1477 * Creates a new <code>JTree</code> object.
1481 this(getDefaultTreeModel());
1485 * Creates a new <code>JTree</code> object.
1487 * @param value the initial nodes in the tree
1489 public JTree(Hashtable value)
1491 this(createTreeModel(value));
1495 * Creates a new <code>JTree</code> object.
1497 * @param value the initial nodes in the tree
1499 public JTree(Object[] value)
1501 this(createTreeModel(value));
1505 * Creates a new <code>JTree</code> object.
1507 * @param model the model to use
1509 public JTree(TreeModel model)
1511 setRootVisible(true);
1512 setSelectionModel(new EmptySelectionModel());
1513 selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
1515 // The root node appears expanded by default.
1516 nodeStates = new Hashtable();
1518 // Install the UI before installing the model. This way we avoid double
1519 // initialization of lots of UI and model stuff inside the UI and related
1520 // classes. The necessary UI updates are performed via property change
1521 // events to the UI.
1527 * Creates a new <code>JTree</code> object.
1529 * @param root the root node
1531 public JTree(TreeNode root)
1537 * Creates a new <code>JTree</code> object.
1539 * @param root the root node
1540 * @param asksAllowChildren if false, all nodes without children are leaf
1541 * nodes. If true, only nodes that do not allow children are leaf
1544 public JTree(TreeNode root, boolean asksAllowChildren)
1546 this(new DefaultTreeModel(root, asksAllowChildren));
1550 * Creates a new <code>JTree</code> object.
1552 * @param value the initial nodes in the tree
1554 public JTree(Vector value)
1556 this(createTreeModel(value));
1559 public int getRowForPath(TreePath path)
1561 TreeUI ui = getUI();
1564 return ui.getRowForPath(this, path);
1569 public TreePath getPathForRow(int row)
1571 TreeUI ui = getUI();
1572 return ui != null ? ui.getPathForRow(this, row) : null;
1576 * Get the pathes that are displayes between the two given rows.
1578 * @param index0 the starting row, inclusive
1579 * @param index1 the ending row, inclusive
1581 * @return the array of the tree pathes
1583 protected TreePath[] getPathBetweenRows(int index0, int index1)
1585 TreeUI ui = getUI();
1590 int minIndex = Math.min(index0, index1);
1591 int maxIndex = Math.max(index0, index1);
1592 TreePath[] paths = new TreePath[maxIndex - minIndex + 1];
1594 for (int i = minIndex; i <= maxIndex; ++i)
1595 paths[i - minIndex] = ui.getPathForRow(this, i);
1601 * Creates a new <code>TreeModel</code> object.
1603 * @param value the values stored in the model
1605 protected static TreeModel createTreeModel(Object value)
1607 return new DefaultTreeModel(new DynamicUtilTreeNode(value, value));
1611 * Return the UI associated with this <code>JTree</code> object.
1613 * @return the associated <code>TreeUI</code> object
1615 public TreeUI getUI()
1621 * Sets the UI associated with this <code>JTree</code> object.
1623 * @param ui the <code>TreeUI</code> to associate
1625 public void setUI(TreeUI ui)
1631 * This method resets the UI used to the Look and Feel defaults..
1633 public void updateUI()
1635 setUI((TreeUI) UIManager.getUI(this));
1639 * This method returns the String ID of the UI class of Separator.
1641 * @return The UI class' String ID.
1643 public String getUIClassID()
1649 * Gets the AccessibleContext associated with this
1650 * <code>JTree</code>.
1652 * @return the associated context
1654 public AccessibleContext getAccessibleContext()
1656 return new AccessibleJTree();
1660 * Returns the preferred viewport size.
1662 * @return the preferred size
1664 public Dimension getPreferredScrollableViewportSize()
1666 return getPreferredSize();
1670 * Return the preferred scrolling amount (in pixels) for the given scrolling
1671 * direction and orientation. This method handles a partially exposed row by
1672 * returning the distance required to completely expose the item.
1674 * @param visibleRect the currently visible part of the component.
1675 * @param orientation the scrolling orientation
1676 * @param direction the scrolling direction (negative - up, positive -down).
1677 * The values greater than one means that more mouse wheel or similar
1678 * events were generated, and hence it is better to scroll the longer
1680 * @author Audrius Meskauskas (audriusa@bioinformatics.org)
1682 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
1687 // Round so that the top would start from the row boundary
1688 if (orientation == SwingConstants.VERTICAL)
1690 // One pixel down, otherwise picks another row too high.
1691 int row = getClosestRowForLocation(visibleRect.x, visibleRect.y + 1);
1692 row = row + direction;
1696 Rectangle newTop = getRowBounds(row);
1697 delta = newTop.y - visibleRect.y;
1700 delta = direction * rowHeight == 0 ? 20 : rowHeight;
1704 public int getScrollableBlockIncrement(Rectangle visibleRect,
1705 int orientation, int direction)
1707 return getScrollableUnitIncrement(visibleRect, orientation, direction);
1710 public boolean getScrollableTracksViewportHeight()
1712 if (getParent() instanceof JViewport)
1713 return ((JViewport) getParent()).getHeight() > getPreferredSize().height;
1717 public boolean getScrollableTracksViewportWidth()
1719 if (getParent() instanceof JViewport)
1720 return ((JViewport) getParent()).getWidth() > getPreferredSize().width;
1725 * Adds a <code>TreeExpansionListener</code> object to the tree.
1727 * @param listener the listener to add
1729 public void addTreeExpansionListener(TreeExpansionListener listener)
1731 listenerList.add(TreeExpansionListener.class, listener);
1735 * Removes a <code>TreeExpansionListener</code> object from the tree.
1737 * @param listener the listener to remove
1739 public void removeTreeExpansionListener(TreeExpansionListener listener)
1741 listenerList.remove(TreeExpansionListener.class, listener);
1745 * Returns all added <code>TreeExpansionListener</code> objects.
1747 * @return an array of listeners
1749 public TreeExpansionListener[] getTreeExpansionListeners()
1751 return (TreeExpansionListener[]) getListeners(TreeExpansionListener.class);
1755 * Notifies all listeners that the tree was collapsed.
1757 * @param path the path to the node that was collapsed
1759 public void fireTreeCollapsed(TreePath path)
1761 TreeExpansionEvent event = new TreeExpansionEvent(this, path);
1762 TreeExpansionListener[] listeners = getTreeExpansionListeners();
1764 for (int index = 0; index < listeners.length; ++index)
1765 listeners[index].treeCollapsed(event);
1769 * Notifies all listeners that the tree was expanded.
1771 * @param path the path to the node that was expanded
1773 public void fireTreeExpanded(TreePath path)
1775 TreeExpansionEvent event = new TreeExpansionEvent(this, path);
1776 TreeExpansionListener[] listeners = getTreeExpansionListeners();
1778 for (int index = 0; index < listeners.length; ++index)
1779 listeners[index].treeExpanded(event);
1783 * Adds a <code>TreeSelctionListener</code> object to the tree.
1785 * @param listener the listener to add
1787 public void addTreeSelectionListener(TreeSelectionListener listener)
1789 listenerList.add(TreeSelectionListener.class, listener);
1793 * Removes a <code>TreeSelectionListener</code> object from the tree.
1795 * @param listener the listener to remove
1797 public void removeTreeSelectionListener(TreeSelectionListener listener)
1799 listenerList.remove(TreeSelectionListener.class, listener);
1803 * Returns all added <code>TreeSelectionListener</code> objects.
1805 * @return an array of listeners
1807 public TreeSelectionListener[] getTreeSelectionListeners()
1809 return (TreeSelectionListener[])
1810 getListeners(TreeSelectionListener.class);
1814 * Notifies all listeners when the selection of the tree changed.
1816 * @param event the event to send
1818 protected void fireValueChanged(TreeSelectionEvent event)
1820 TreeSelectionListener[] listeners = getTreeSelectionListeners();
1822 for (int index = 0; index < listeners.length; ++index)
1823 listeners[index].valueChanged(event);
1827 * Adds a <code>TreeWillExpandListener</code> object to the tree.
1829 * @param listener the listener to add
1831 public void addTreeWillExpandListener(TreeWillExpandListener listener)
1833 listenerList.add(TreeWillExpandListener.class, listener);
1837 * Removes a <code>TreeWillExpandListener</code> object from the tree.
1839 * @param listener the listener to remove
1841 public void removeTreeWillExpandListener(TreeWillExpandListener listener)
1843 listenerList.remove(TreeWillExpandListener.class, listener);
1847 * Returns all added <code>TreeWillExpandListener</code> objects.
1849 * @return an array of listeners
1851 public TreeWillExpandListener[] getTreeWillExpandListeners()
1853 return (TreeWillExpandListener[])
1854 getListeners(TreeWillExpandListener.class);
1858 * Notifies all listeners that the tree will collapse.
1860 * @param path the path to the node that will collapse
1862 public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException
1864 TreeExpansionEvent event = new TreeExpansionEvent(this, path);
1865 TreeWillExpandListener[] listeners = getTreeWillExpandListeners();
1867 for (int index = 0; index < listeners.length; ++index)
1868 listeners[index].treeWillCollapse(event);
1872 * Notifies all listeners that the tree will expand.
1874 * @param path the path to the node that will expand
1876 public void fireTreeWillExpand(TreePath path) throws ExpandVetoException
1878 TreeExpansionEvent event = new TreeExpansionEvent(this, path);
1879 TreeWillExpandListener[] listeners = getTreeWillExpandListeners();
1881 for (int index = 0; index < listeners.length; ++index)
1882 listeners[index].treeWillExpand(event);
1886 * Returns the model of this <code>JTree</code> object.
1888 * @return the associated <code>TreeModel</code>
1890 public TreeModel getModel()
1896 * Sets the model to use in <code>JTree</code>.
1898 * @param model the <code>TreeModel</code> to use
1900 public void setModel(TreeModel model)
1902 if (treeModel == model)
1905 // Remove listeners from old model.
1906 if (treeModel != null && treeModelListener != null)
1907 treeModel.removeTreeModelListener(treeModelListener);
1909 // add treeModelListener to the new model
1910 if (treeModelListener == null)
1911 treeModelListener = createTreeModelListener();
1912 if (model != null) // as setModel(null) is allowed
1913 model.addTreeModelListener(treeModelListener);
1915 TreeModel oldValue = treeModel;
1917 clearToggledPaths();
1919 if (treeModel != null)
1921 if (treeModelListener == null)
1922 treeModelListener = createTreeModelListener();
1923 if (treeModelListener != null)
1924 treeModel.addTreeModelListener(treeModelListener);
1925 Object root = treeModel.getRoot();
1926 if (root != null && !treeModel.isLeaf(root))
1928 nodeStates.put(new TreePath(root), Boolean.TRUE);
1932 firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model);
1936 * Checks if this <code>JTree</code> object is editable.
1938 * @return <code>true</code> if this tree object is editable,
1939 * <code>false</code> otherwise
1941 public boolean isEditable()
1947 * Sets the <code>editable</code> property.
1949 * @param flag <code>true</code> to make this tree object editable,
1950 * <code>false</code> otherwise
1952 public void setEditable(boolean flag)
1954 if (editable == flag)
1957 boolean oldValue = editable;
1959 firePropertyChange(EDITABLE_PROPERTY, oldValue, editable);
1963 * Checks if the root element is visible.
1965 * @return <code>true</code> if the root element is visible,
1966 * <code>false</code> otherwise
1968 public boolean isRootVisible()
1973 public void setRootVisible(boolean flag)
1975 if (rootVisible == flag)
1978 // If the root is currently selected, unselect it
1979 if (rootVisible && !flag)
1981 TreeSelectionModel model = getSelectionModel();
1982 // The root is always shown in the first row
1983 TreePath rootPath = getPathForRow(0);
1984 model.removeSelectionPath(rootPath);
1987 boolean oldValue = rootVisible;
1989 firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag);
1993 public boolean getShowsRootHandles()
1995 return showsRootHandles;
1998 public void setShowsRootHandles(boolean flag)
2000 clientShowsRootHandlesSet = true;
2002 if (showsRootHandles == flag)
2005 boolean oldValue = showsRootHandles;
2006 showsRootHandles = flag;
2007 firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue, flag);
2010 public TreeCellEditor getCellEditor()
2015 public void setCellEditor(TreeCellEditor editor)
2017 if (cellEditor == editor)
2020 TreeCellEditor oldValue = cellEditor;
2021 cellEditor = editor;
2022 firePropertyChange(CELL_EDITOR_PROPERTY, oldValue, editor);
2025 public TreeCellRenderer getCellRenderer()
2027 return cellRenderer;
2030 public void setCellRenderer(TreeCellRenderer newRenderer)
2032 if (cellRenderer == newRenderer)
2035 TreeCellRenderer oldValue = cellRenderer;
2036 cellRenderer = newRenderer;
2037 firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, newRenderer);
2040 public TreeSelectionModel getSelectionModel()
2042 return selectionModel;
2045 public void setSelectionModel(TreeSelectionModel model)
2047 if (selectionModel == model)
2050 if (selectionModel != null)
2051 selectionModel.removeTreeSelectionListener(selectionRedirector);
2053 TreeSelectionModel oldValue = selectionModel;
2054 selectionModel = model;
2056 if (selectionModel != null)
2057 selectionModel.addTreeSelectionListener(selectionRedirector);
2059 firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, model);
2064 public int getVisibleRowCount()
2066 return visibleRowCount;
2069 public void setVisibleRowCount(int rows)
2071 if (visibleRowCount == rows)
2074 int oldValue = visibleRowCount;
2075 visibleRowCount = rows;
2076 firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldValue, rows);
2079 public boolean isLargeModel()
2084 public void setLargeModel(boolean large)
2086 if (largeModel == large)
2089 boolean oldValue = largeModel;
2091 firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, large);
2094 public int getRowHeight()
2099 public void setRowHeight(int height)
2101 clientRowHeightSet = true;
2103 if (rowHeight == height)
2106 int oldValue = rowHeight;
2108 firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, height);
2111 public boolean isFixedRowHeight()
2113 return rowHeight > 0;
2116 public boolean getInvokesStopCellEditing()
2118 return invokesStopCellEditing;
2121 public void setInvokesStopCellEditing(boolean invoke)
2123 if (invokesStopCellEditing == invoke)
2126 boolean oldValue = invokesStopCellEditing;
2127 invokesStopCellEditing = invoke;
2128 firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY,
2135 public int getToggleClickCount()
2137 return toggleClickCount;
2143 public void setToggleClickCount(int count)
2145 if (toggleClickCount == count)
2148 int oldValue = toggleClickCount;
2149 toggleClickCount = count;
2150 firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldValue, count);
2153 public void scrollPathToVisible(TreePath path)
2157 Rectangle rect = getPathBounds(path);
2158 scrollRectToVisible(rect);
2161 public void scrollRowToVisible(int row)
2163 scrollPathToVisible(getPathForRow(row));
2166 public boolean getScrollsOnExpand()
2168 return scrollsOnExpand;
2171 public void setScrollsOnExpand(boolean scroll)
2173 clientScrollsOnExpandSet = true;
2174 if (scrollsOnExpand == scroll)
2177 boolean oldValue = scrollsOnExpand;
2178 scrollsOnExpand = scroll;
2179 firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll);
2182 public void setSelectionPath(TreePath path)
2184 selectionModel.setSelectionPath(path);
2187 public void setSelectionPaths(TreePath[] paths)
2189 selectionModel.setSelectionPaths(paths);
2192 public void setSelectionRow(int row)
2194 TreePath path = getPathForRow(row);
2197 selectionModel.setSelectionPath(path);
2200 public void setSelectionRows(int[] rows)
2202 // Make sure we have an UI so getPathForRow() does not return null.
2203 if (rows == null || getUI() == null)
2206 TreePath[] paths = new TreePath[rows.length];
2208 for (int i = rows.length - 1; i >= 0; --i)
2209 paths[i] = getPathForRow(rows[i]);
2211 setSelectionPaths(paths);
2214 public void setSelectionInterval(int index0, int index1)
2216 TreePath[] paths = getPathBetweenRows(index0, index1);
2219 setSelectionPaths(paths);
2222 public void addSelectionPath(TreePath path)
2224 selectionModel.addSelectionPath(path);
2227 public void addSelectionPaths(TreePath[] paths)
2229 selectionModel.addSelectionPaths(paths);
2232 public void addSelectionRow(int row)
2234 TreePath path = getPathForRow(row);
2237 selectionModel.addSelectionPath(path);
2240 public void addSelectionRows(int[] rows)
2242 // Make sure we have an UI so getPathForRow() does not return null.
2243 if (rows == null || getUI() == null)
2246 TreePath[] paths = new TreePath[rows.length];
2248 for (int i = rows.length - 1; i >= 0; --i)
2249 paths[i] = getPathForRow(rows[i]);
2251 addSelectionPaths(paths);
2255 * Select all rows between the two given indexes, inclusive. The method
2256 * will not select the inner leaves and braches of the currently collapsed
2257 * nodes in this interval.
2259 * @param index0 the starting row, inclusive
2260 * @param index1 the ending row, inclusive
2262 public void addSelectionInterval(int index0, int index1)
2264 TreePath[] paths = getPathBetweenRows(index0, index1);
2267 addSelectionPaths(paths);
2270 public void removeSelectionPath(TreePath path)
2272 selectionModel.removeSelectionPath(path);
2275 public void removeSelectionPaths(TreePath[] paths)
2277 selectionModel.removeSelectionPaths(paths);
2280 public void removeSelectionRow(int row)
2282 TreePath path = getPathForRow(row);
2285 selectionModel.removeSelectionPath(path);
2288 public void removeSelectionRows(int[] rows)
2290 if (rows == null || getUI() == null)
2293 TreePath[] paths = new TreePath[rows.length];
2295 for (int i = rows.length - 1; i >= 0; --i)
2296 paths[i] = getPathForRow(rows[i]);
2298 removeSelectionPaths(paths);
2301 public void removeSelectionInterval(int index0, int index1)
2303 TreePath[] paths = getPathBetweenRows(index0, index1);
2306 removeSelectionPaths(paths);
2309 public void clearSelection()
2311 selectionModel.clearSelection();
2312 setLeadSelectionPath(null);
2315 public TreePath getLeadSelectionPath()
2317 if (selectionModel == null)
2320 return selectionModel.getLeadSelectionPath();
2326 public void setLeadSelectionPath(TreePath path)
2328 if (selectionModel != null)
2330 TreePath oldValue = selectionModel.getLeadSelectionPath();
2331 if (path.equals(oldValue))
2334 // Repaint the previous and current rows with the lead selection path.
2337 repaint(getPathBounds(path));
2338 selectionModel.addSelectionPath(path);
2341 if (oldValue != null)
2342 repaint(getPathBounds(oldValue));
2344 firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path);
2351 public TreePath getAnchorSelectionPath()
2353 return anchorSelectionPath;
2359 public void setAnchorSelectionPath(TreePath path)
2361 if (anchorSelectionPath == path)
2364 TreePath oldValue = anchorSelectionPath;
2365 anchorSelectionPath = path;
2366 firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, path);
2369 public int getLeadSelectionRow()
2371 return selectionModel.getLeadSelectionRow();
2374 public int getMaxSelectionRow()
2376 return selectionModel.getMaxSelectionRow();
2379 public int getMinSelectionRow()
2381 return selectionModel.getMinSelectionRow();
2384 public int getSelectionCount()
2386 return selectionModel.getSelectionCount();
2389 public TreePath getSelectionPath()
2391 return selectionModel.getSelectionPath();
2394 public TreePath[] getSelectionPaths()
2396 return selectionModel.getSelectionPaths();
2399 public int[] getSelectionRows()
2401 return selectionModel.getSelectionRows();
2404 public boolean isPathSelected(TreePath path)
2406 return selectionModel.isPathSelected(path);
2409 public boolean isRowSelected(int row)
2411 return selectionModel.isPathSelected(getPathForRow(row));
2414 public boolean isSelectionEmpty()
2416 return selectionModel.isSelectionEmpty();
2420 * Return the value of the <code>dragEnabled</code> property.
2426 public boolean getDragEnabled()
2432 * Set the <code>dragEnabled</code> property.
2434 * @param enabled new value
2438 public void setDragEnabled(boolean enabled)
2440 dragEnabled = enabled;
2443 public int getRowCount()
2445 TreeUI ui = getUI();
2448 return ui.getRowCount(this);
2453 public void collapsePath(TreePath path)
2457 fireTreeWillCollapse(path);
2459 catch (ExpandVetoException ev)
2461 // We do nothing if attempt has been vetoed.
2463 setExpandedState(path, false);
2464 fireTreeCollapsed(path);
2467 public void collapseRow(int row)
2469 if (row < 0 || row >= getRowCount())
2472 TreePath path = getPathForRow(row);
2478 public void expandPath(TreePath path)
2480 // Don't expand if path is null
2481 // or is already expanded.
2482 if (path == null || isExpanded(path))
2487 fireTreeWillExpand(path);
2489 catch (ExpandVetoException ev)
2491 // We do nothing if attempt has been vetoed.
2494 setExpandedState(path, true);
2495 fireTreeExpanded(path);
2498 public void expandRow(int row)
2500 if (row < 0 || row >= getRowCount())
2503 TreePath path = getPathForRow(row);
2509 public boolean isCollapsed(TreePath path)
2511 return !isExpanded(path);
2514 public boolean isCollapsed(int row)
2516 if (row < 0 || row >= getRowCount())
2519 TreePath path = getPathForRow(row);
2522 return isCollapsed(path);
2527 public boolean isExpanded(TreePath path)
2532 Object state = nodeStates.get(path);
2534 if ((state == null) || (state != EXPANDED))
2537 TreePath parent = path.getParentPath();
2540 return isExpanded(parent);
2545 public boolean isExpanded(int row)
2547 if (row < 0 || row >= getRowCount())
2550 TreePath path = getPathForRow(row);
2553 return isExpanded(path);
2561 public boolean getExpandsSelectedPaths()
2563 return expandsSelectedPaths;
2569 public void setExpandsSelectedPaths(boolean flag)
2571 if (expandsSelectedPaths == flag)
2574 boolean oldValue = expandsSelectedPaths;
2575 expandsSelectedPaths = flag;
2576 firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue, flag);
2579 public Rectangle getPathBounds(TreePath path)
2581 TreeUI ui = getUI();
2586 return ui.getPathBounds(this, path);
2589 public Rectangle getRowBounds(int row)
2591 TreePath path = getPathForRow(row);
2594 return getPathBounds(path);
2599 public boolean isEditing()
2601 TreeUI ui = getUI();
2604 return ui.isEditing(this);
2609 public boolean stopEditing()
2611 TreeUI ui = getUI();
2615 return ui.stopEditing(this);
2620 public void cancelEditing()
2622 TreeUI ui = getUI();
2626 ui.cancelEditing(this);
2629 public void startEditingAtPath(TreePath path)
2631 TreeUI ui = getUI();
2634 ui.startEditingAtPath(this, path);
2637 public TreePath getEditingPath()
2639 TreeUI ui = getUI();
2642 return ui.getEditingPath(this);
2647 public TreePath getPathForLocation(int x, int y)
2649 TreePath path = getClosestPathForLocation(x, y);
2653 Rectangle rect = getPathBounds(path);
2655 if ((rect != null) && rect.contains(x, y))
2662 public int getRowForLocation(int x, int y)
2664 TreePath path = getPathForLocation(x, y);
2667 return getRowForPath(path);
2672 public TreePath getClosestPathForLocation(int x, int y)
2674 TreeUI ui = getUI();
2677 return ui.getClosestPathForLocation(this, x, y);
2682 public int getClosestRowForLocation(int x, int y)
2684 TreePath path = getClosestPathForLocation(x, y);
2687 return getRowForPath(path);
2692 public Object getLastSelectedPathComponent()
2694 TreePath path = getSelectionPath();
2697 return path.getLastPathComponent();
2702 private void doExpandParents(TreePath path, boolean state)
2704 TreePath parent = path.getParentPath();
2706 if (!isExpanded(parent) && parent != null)
2707 doExpandParents(parent, false);
2709 nodeStates.put(path, state ? EXPANDED : COLLAPSED);
2712 protected void setExpandedState(TreePath path, boolean state)
2717 doExpandParents(path, state);
2720 protected void clearToggledPaths()
2725 protected Enumeration getDescendantToggledPaths(TreePath parent)
2730 Enumeration nodes = nodeStates.keys();
2731 Vector result = new Vector();
2733 while (nodes.hasMoreElements())
2735 TreePath path = (TreePath) nodes.nextElement();
2737 if (path.isDescendant(parent))
2738 result.addElement(path);
2741 return result.elements();
2744 public boolean hasBeenExpanded(TreePath path)
2749 return nodeStates.get(path) != null;
2752 public boolean isVisible(TreePath path)
2757 TreePath parent = path.getParentPath();
2760 return true; // Is root node.
2762 return isExpanded(parent);
2765 public void makeVisible(TreePath path)
2770 expandPath(path.getParentPath());
2773 public boolean isPathEditable(TreePath path)
2775 return isEditable();
2779 * Creates and returns an instance of {@link TreeModelHandler}.
2781 * @return an instance of {@link TreeModelHandler}
2783 protected TreeModelListener createTreeModelListener()
2785 return new TreeModelHandler();
2789 * Returns a sample TreeModel that can be used in a JTree. This can be used
2790 * in Bean- or GUI-Builders to show something interesting.
2792 * @return a sample TreeModel that can be used in a JTree
2794 protected static TreeModel getDefaultTreeModel()
2796 DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root node");
2797 DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("Child node 1");
2798 DefaultMutableTreeNode child11 =
2799 new DefaultMutableTreeNode("Child node 1.1");
2800 DefaultMutableTreeNode child12 =
2801 new DefaultMutableTreeNode("Child node 1.2");
2802 DefaultMutableTreeNode child13 =
2803 new DefaultMutableTreeNode("Child node 1.3");
2804 DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("Child node 2");
2805 DefaultMutableTreeNode child21 =
2806 new DefaultMutableTreeNode("Child node 2.1");
2807 DefaultMutableTreeNode child22 =
2808 new DefaultMutableTreeNode("Child node 2.2");
2809 DefaultMutableTreeNode child23 =
2810 new DefaultMutableTreeNode("Child node 2.3");
2811 DefaultMutableTreeNode child24 =
2812 new DefaultMutableTreeNode("Child node 2.4");
2814 DefaultMutableTreeNode child3 = new DefaultMutableTreeNode("Child node 3");
2818 child1.add(child11);
2819 child1.add(child12);
2820 child1.add(child13);
2821 child2.add(child21);
2822 child2.add(child22);
2823 child2.add(child23);
2824 child2.add(child24);
2825 return new DefaultTreeModel(root);
2829 * Converts the specified value to a String. This is used by the renderers
2830 * of this JTree and its nodes.
2832 * This implementation simply returns <code>value.toString()</code> and
2833 * ignores all other parameters. Subclass this method to control the
2836 * @param value the value that is converted to a String
2837 * @param selected indicates if that value is selected or not
2838 * @param expanded indicates if that value is expanded or not
2839 * @param leaf indicates if that value is a leaf node or not
2840 * @param row the row of the node
2841 * @param hasFocus indicates if that node has focus or not
2843 public String convertValueToText(Object value, boolean selected,
2844 boolean expanded, boolean leaf, int row, boolean hasFocus)
2846 return value.toString();
2850 * A String representation of this JTree. This is intended to be used for
2851 * debugging. The returned string may be empty but may not be
2852 * <code>null</code>.
2854 * @return a String representation of this JTree
2856 protected String paramString()
2858 // TODO: this is completely legal, but it would possibly be nice
2859 // to return some more content, like the tree structure, some properties
2865 * Returns all TreePath objects which are a descendants of the given path
2866 * and are exapanded at the moment of the execution of this method. If the
2867 * state of any node is beeing toggled while this method is executing this
2868 * change may be left unaccounted.
2870 * @param path The parent of this request
2872 * @return An Enumeration containing TreePath objects
2874 public Enumeration getExpandedDescendants(TreePath path)
2876 Enumeration paths = nodeStates.keys();
2877 Vector relevantPaths = new Vector();
2878 while (paths.hasMoreElements())
2880 TreePath nextPath = (TreePath) paths.nextElement();
2881 if (nodeStates.get(nextPath) == EXPANDED
2882 && path.isDescendant(nextPath))
2884 relevantPaths.add(nextPath);
2887 return relevantPaths.elements();
2891 * Returns the next table element (beginning from the row
2892 * <code>startingRow</code> that starts with <code>prefix</code>.
2893 * Searching is done in the direction specified by <code>bias</code>.
2895 * @param prefix the prefix to search for in the cell values
2896 * @param startingRow the index of the row where to start searching from
2897 * @param bias the search direction, either {@link Position.Bias#Forward} or
2898 * {@link Position.Bias#Backward}
2900 * @return the path to the found element or -1 if no such element has been
2903 * @throws IllegalArgumentException if prefix is <code>null</code> or
2904 * startingRow is not valid
2908 public TreePath getNextMatch(String prefix, int startingRow,
2912 throw new IllegalArgumentException("The argument 'prefix' must not be"
2914 if (startingRow < 0)
2915 throw new IllegalArgumentException("The argument 'startingRow' must not"
2916 + " be less than zero.");
2918 int size = getRowCount();
2919 if (startingRow > size)
2920 throw new IllegalArgumentException("The argument 'startingRow' must not"
2921 + " be greater than the number of"
2922 + " elements in the TreeModel.");
2924 TreePath foundPath = null;
2925 if (bias == Position.Bias.Forward)
2927 for (int i = startingRow; i < size; i++)
2929 TreePath path = getPathForRow(i);
2930 Object o = path.getLastPathComponent();
2931 // FIXME: in the following call to convertValueToText the
2932 // last argument (hasFocus) should be done right.
2933 String item = convertValueToText(o, isRowSelected(i),
2934 isExpanded(i), treeModel.isLeaf(o),
2936 if (item.startsWith(prefix))
2945 for (int i = startingRow; i >= 0; i--)
2947 TreePath path = getPathForRow(i);
2948 Object o = path.getLastPathComponent();
2949 // FIXME: in the following call to convertValueToText the
2950 // last argument (hasFocus) should be done right.
2951 String item = convertValueToText(o, isRowSelected(i),
2952 isExpanded(i), treeModel.isLeaf(o), i, false);
2953 if (item.startsWith(prefix))
2964 * Removes any paths in the current set of selected paths that are
2965 * descendants of <code>path</code>. If <code>includePath</code> is set
2966 * to <code>true</code> and <code>path</code> itself is selected, then
2967 * it will be removed too.
2969 * @param path the path from which selected descendants are to be removed
2970 * @param includeSelected if <code>true</code> then <code>path</code> itself
2971 * will also be remove if it's selected
2973 * @return <code>true</code> if something has been removed,
2974 * <code>false</code> otherwise
2978 protected boolean removeDescendantSelectedPaths(TreePath path,
2979 boolean includeSelected)
2981 boolean removedSomething = false;
2982 TreePath[] selected = getSelectionPaths();
2983 for (int index = 0; index < selected.length; index++)
2985 if ((selected[index] == path && includeSelected)
2986 || (selected[index].isDescendant(path)))
2988 removeSelectionPath(selected[index]);
2989 removedSomething = true;
2992 return removedSomething;
2996 * Removes any descendants of the TreePaths in toRemove that have been
2999 * @param toRemove - Enumeration of TreePaths that need to be removed from
3000 * cache of toggled tree paths.
3002 protected void removeDescendantToggledPaths(Enumeration toRemove)
3004 while (toRemove.hasMoreElements())
3006 TreePath current = (TreePath) toRemove.nextElement();
3007 Enumeration descendants = getDescendantToggledPaths(current);
3009 while (descendants.hasMoreElements())
3011 TreePath currentDes = (TreePath) descendants.nextElement();
3012 if (isExpanded(currentDes))
3013 nodeStates.remove(currentDes);
3020 * Sent when the tree has changed enough that we need to resize the bounds,
3021 * but not enough that we need to remove the expanded node set (e.g nodes were
3022 * expanded or collapsed, or nodes were inserted into the tree). You should
3023 * never have to invoke this, the UI will invoke this as it needs to.
3026 * If the tree uses {@link DefaultTreeModel}, you must call
3027 * {@link DefaultTreeModel#reload(TreeNode)} or
3028 * {@link DefaultTreeModel#reload()} after adding or removing nodes. Following
3029 * the official Java 1.5 API standard, just calling treeDidChange, repaint()
3030 * or revalidate() does <i>not</i> update the tree appearance properly.
3032 * @see DefaultTreeModel#reload()
3034 public void treeDidChange()
3041 * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
3043 * @param propertyName the name of the property
3044 * @param value the value of the property
3046 * @throws IllegalArgumentException if the specified property cannot be set
3048 * @throws ClassCastException if the property value does not match the
3050 * @throws NullPointerException if <code>c</code> or
3051 * <code>propertyValue</code> is <code>null</code>
3053 void setUIProperty(String propertyName, Object value)
3055 if (propertyName.equals("rowHeight"))
3057 if (! clientRowHeightSet)
3059 setRowHeight(((Integer) value).intValue());
3060 clientRowHeightSet = false;
3063 else if (propertyName.equals("scrollsOnExpand"))
3065 if (! clientScrollsOnExpandSet)
3067 setScrollsOnExpand(((Boolean) value).booleanValue());
3068 clientScrollsOnExpandSet = false;
3071 else if (propertyName.equals("showsRootHandles"))
3073 if (! clientShowsRootHandlesSet)
3075 setShowsRootHandles(((Boolean) value).booleanValue());
3076 clientShowsRootHandlesSet = false;
3081 super.setUIProperty(propertyName, value);