170 * The cell used to draw nodes. If <code>null</code>, the UI uses a default
171 * <code>cellRenderer</code>.
172 */
173 transient protected TreeCellRenderer cellRenderer;
174
175 /**
176 * Height to use for each display row. If this is <= 0 the renderer
177 * determines the height for each row.
178 */
179 protected int rowHeight;
180 private boolean rowHeightSet = false;
181
182 /**
183 * Maps from <code>TreePath</code> to <code>Boolean</code>
184 * indicating whether or not the
185 * particular path is expanded. This ONLY indicates whether a
186 * given path is expanded, and NOT if it is visible or not. That
187 * information must be determined by visiting all the parent
188 * paths and seeing if they are visible.
189 */
190 transient private Hashtable expandedState;
191
192
193 /**
194 * True if handles are displayed at the topmost level of the tree.
195 * <p>
196 * A handle is a small icon that displays adjacent to the node which
197 * allows the user to click once to expand or collapse the node. A
198 * common interface shows a plus sign (+) for a node which can be
199 * expanded and a minus sign (-) for a node which can be collapsed.
200 * Handles are always shown for nodes below the topmost level.
201 * <p>
202 * If the <code>rootVisible</code> setting specifies that the root
203 * node is to be displayed, then that is the only node at the topmost
204 * level. If the root node is not displayed, then all of its
205 * children are at the topmost level of the tree. Handles are
206 * always displayed for nodes other than the topmost.
207 * <p>
208 * If the root node isn't visible, it is generally a good to make
209 * this value true. Otherwise, the tree looks exactly like a list,
210 * and users may not know that the "list entries" are actually
264 * If true, when a node is expanded, as many of the descendants are
265 * scrolled to be visible.
266 */
267 protected boolean scrollsOnExpand;
268 private boolean scrollsOnExpandSet = false;
269
270 /**
271 * Number of mouse clicks before a node is expanded.
272 */
273 protected int toggleClickCount;
274
275 /**
276 * Updates the <code>expandedState</code>.
277 */
278 transient protected TreeModelListener treeModelListener;
279
280 /**
281 * Used when <code>setExpandedState</code> is invoked,
282 * will be a <code>Stack</code> of <code>Stack</code>s.
283 */
284 transient private Stack expandedStack;
285
286 /**
287 * Lead selection path, may not be <code>null</code>.
288 */
289 private TreePath leadPath;
290
291 /**
292 * Anchor path.
293 */
294 private TreePath anchorPath;
295
296 /**
297 * True if paths in the selection should be expanded.
298 */
299 private boolean expandsSelectedPaths;
300
301 /**
302 * This is set to true for the life of the <code>setUI</code> call.
303 */
304 private boolean settingUI;
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 public JTree(TreeModel newModel) {
653 super();
654 expandedStack = new Stack();
655 toggleClickCount = 2;
656 expandedState = new Hashtable();
657 setLayout(null);
658 rowHeight = 16;
659 visibleRowCount = 20;
660 rootVisible = true;
661 selectionModel = new DefaultTreeSelectionModel();
662 cellRenderer = null;
663 scrollsOnExpand = true;
664 setOpaque(true);
665 expandsSelectedPaths = true;
666 updateUI();
667 setModel(newModel);
668 }
669
670 /**
671 * Returns the L&F object that renders this component.
672 *
673 * @return the <code>TreeUI</code> object that renders this component
674 */
675 public TreeUI getUI() {
676 return (TreeUI)ui;
677 }
678
679 /**
680 * Sets the L&F object that renders this component.
681 *
682 * @param ui the <code>TreeUI</code> L&F object
683 * @see UIDefaults#getUI
684 * @beaninfo
685 * bound: true
686 * hidden: true
687 * attribute: visualUpdate true
688 * description: The UI object that implements the Component's LookAndFeel.
689 */
690 public void setUI(TreeUI ui) {
691 if ((TreeUI)this.ui != ui) {
692 settingUI = true;
693 uiTreeExpansionListener = null;
694 try {
695 super.setUI(ui);
696 }
697 finally {
698 settingUI = false;
699 }
700 }
701 }
702
703 /**
704 * Notification from the <code>UIManager</code> that the L&F has changed.
705 * Replaces the current UI object with the latest version from the
706 * <code>UIManager</code>.
707 *
708 * @see JComponent#updateUI
709 */
710 public void updateUI() {
711 setUI((TreeUI)UIManager.getUI(this));
1256 public final DropMode getDropMode() {
1257 return dropMode;
1258 }
1259
1260 /**
1261 * Calculates a drop location in this component, representing where a
1262 * drop at the given point should insert data.
1263 *
1264 * @param p the point to calculate a drop location for
1265 * @return the drop location, or <code>null</code>
1266 */
1267 DropLocation dropLocationForPoint(Point p) {
1268 DropLocation location = null;
1269
1270 int row = getClosestRowForLocation(p.x, p.y);
1271 Rectangle bounds = getRowBounds(row);
1272 TreeModel model = getModel();
1273 Object root = (model == null) ? null : model.getRoot();
1274 TreePath rootPath = (root == null) ? null : new TreePath(root);
1275
1276 TreePath child = null;
1277 TreePath parent = null;
1278 boolean outside = row == -1
1279 || p.y < bounds.y
1280 || p.y >= bounds.y + bounds.height;
1281
1282 switch(dropMode) {
1283 case USE_SELECTION:
1284 case ON:
1285 if (outside) {
1286 location = new DropLocation(p, null, -1);
1287 } else {
1288 location = new DropLocation(p, getPathForRow(row), -1);
1289 }
1290
1291 break;
1292 case INSERT:
1293 case ON_OR_INSERT:
1294 if (row == -1) {
1295 if (root != null && !model.isLeaf(root) && isExpanded(rootPath)) {
1296 location = new DropLocation(p, rootPath, 0);
1297 } else {
1890
1891 /**
1892 * Returns an <code>Enumeration</code> of the descendants of the
1893 * path <code>parent</code> that
1894 * are currently expanded. If <code>parent</code> is not currently
1895 * expanded, this will return <code>null</code>.
1896 * If you expand/collapse nodes while
1897 * iterating over the returned <code>Enumeration</code>
1898 * this may not return all
1899 * the expanded paths, or may return paths that are no longer expanded.
1900 *
1901 * @param parent the path which is to be examined
1902 * @return an <code>Enumeration</code> of the descendents of
1903 * <code>parent</code>, or <code>null</code> if
1904 * <code>parent</code> is not currently expanded
1905 */
1906 public Enumeration<TreePath> getExpandedDescendants(TreePath parent) {
1907 if(!isExpanded(parent))
1908 return null;
1909
1910 Enumeration toggledPaths = expandedState.keys();
1911 Vector elements = null;
1912 TreePath path;
1913 Object value;
1914
1915 if(toggledPaths != null) {
1916 while(toggledPaths.hasMoreElements()) {
1917 path = (TreePath)toggledPaths.nextElement();
1918 value = expandedState.get(path);
1919 // Add the path if it is expanded, a descendant of parent,
1920 // and it is visible (all parents expanded). This is rather
1921 // expensive!
1922 if(path != parent && value != null &&
1923 ((Boolean)value).booleanValue() &&
1924 parent.isDescendant(path) && isVisible(path)) {
1925 if (elements == null) {
1926 elements = new Vector();
1927 }
1928 elements.addElement(path);
1929 }
1930 }
1931 }
1932 if (elements == null) {
1933 Set<TreePath> empty = Collections.emptySet();
1934 return Collections.enumeration(empty);
1935 }
1936 return elements.elements();
1937 }
1938
1939 /**
1940 * Returns true if the node identified by the path has ever been
1941 * expanded.
1942 * @return true if the <code>path</code> has ever been expanded
1943 */
1944 public boolean hasBeenExpanded(TreePath path) {
1945 return (path != null && expandedState.get(path) != null);
1946 }
1947
1948 /**
1949 * Returns true if the node identified by the path is currently expanded,
1950 *
1951 * @param path the <code>TreePath</code> specifying the node to check
1952 * @return false if any of the nodes in the node's path are collapsed,
1953 * true if all nodes in the path are expanded
1954 */
1955 public boolean isExpanded(TreePath path) {
1956 if(path == null)
1957 return false;
1958
1959 // Is this node expanded?
1960 Object value = expandedState.get(path);
1961
1962 if(value == null || !((Boolean)value).booleanValue())
1963 return false;
1964
1965 // It is, make sure its parent is also expanded.
1966 TreePath parentPath = path.getParentPath();
1967
1968 if(parentPath != null)
1969 return isExpanded(parentPath);
1970 return true;
1971 }
1972
1973 /**
1974 * Returns true if the node at the specified display row is currently
1975 * expanded.
1976 *
1977 * @param row the row to check, where 0 is the first row in the
1978 * display
1979 * @return true if the node is currently expanded, otherwise false
1980 */
1981 public boolean isExpanded(int row) {
1982 TreeUI tree = getUI();
1983
1984 if(tree != null) {
1985 TreePath path = tree.getPathForRow(this, row);
1986
1987 if(path != null) {
1988 Boolean value = (Boolean)expandedState.get(path);
1989
1990 return (value != null && value.booleanValue());
1991 }
1992 }
1993 return false;
1994 }
1995
1996 /**
1997 * Returns true if the value identified by path is currently collapsed,
1998 * this will return false if any of the values in path are currently
1999 * not being displayed.
2000 *
2001 * @param path the <code>TreePath</code> to check
2002 * @return true if any of the nodes in the node's path are collapsed,
2003 * false if all nodes in the path are expanded
2004 */
2005 public boolean isCollapsed(TreePath path) {
2006 return !isExpanded(path);
2007 }
2008
2590 * Removes a listener for <code>TreeExpansion</code> events.
2591 *
2592 * @param tel the <code>TreeExpansionListener</code> to remove
2593 */
2594 public void removeTreeExpansionListener(TreeExpansionListener tel) {
2595 listenerList.remove(TreeExpansionListener.class, tel);
2596 if (uiTreeExpansionListener == tel) {
2597 uiTreeExpansionListener = null;
2598 }
2599 }
2600
2601 /**
2602 * Returns an array of all the <code>TreeExpansionListener</code>s added
2603 * to this JTree with addTreeExpansionListener().
2604 *
2605 * @return all of the <code>TreeExpansionListener</code>s added or an empty
2606 * array if no listeners have been added
2607 * @since 1.4
2608 */
2609 public TreeExpansionListener[] getTreeExpansionListeners() {
2610 return (TreeExpansionListener[])listenerList.getListeners(
2611 TreeExpansionListener.class);
2612 }
2613
2614 /**
2615 * Adds a listener for <code>TreeWillExpand</code> events.
2616 *
2617 * @param tel a <code>TreeWillExpandListener</code> that will be notified
2618 * when a tree node will be expanded or collapsed (a "negative
2619 * expansion")
2620 */
2621 public void addTreeWillExpandListener(TreeWillExpandListener tel) {
2622 listenerList.add(TreeWillExpandListener.class, tel);
2623 }
2624
2625 /**
2626 * Removes a listener for <code>TreeWillExpand</code> events.
2627 *
2628 * @param tel the <code>TreeWillExpandListener</code> to remove
2629 */
2630 public void removeTreeWillExpandListener(TreeWillExpandListener tel) {
2631 listenerList.remove(TreeWillExpandListener.class, tel);
2632 }
2633
2634 /**
2635 * Returns an array of all the <code>TreeWillExpandListener</code>s added
2636 * to this JTree with addTreeWillExpandListener().
2637 *
2638 * @return all of the <code>TreeWillExpandListener</code>s added or an empty
2639 * array if no listeners have been added
2640 * @since 1.4
2641 */
2642 public TreeWillExpandListener[] getTreeWillExpandListeners() {
2643 return (TreeWillExpandListener[])listenerList.getListeners(
2644 TreeWillExpandListener.class);
2645 }
2646
2647 /**
2648 * Notifies all listeners that have registered interest for
2649 * notification on this event type. The event instance
2650 * is lazily created using the <code>path</code> parameter.
2651 *
2652 * @param path the <code>TreePath</code> indicating the node that was
2653 * expanded
2654 * @see EventListenerList
2655 */
2656 public void fireTreeExpanded(TreePath path) {
2657 // Guaranteed to return a non-null array
2658 Object[] listeners = listenerList.getListenerList();
2659 TreeExpansionEvent e = null;
2660 if (uiTreeExpansionListener != null) {
2661 e = new TreeExpansionEvent(this, path);
2662 uiTreeExpansionListener.treeExpanded(e);
2663 }
2664 // Process the listeners last to first, notifying
2781 */
2782 public void removeTreeSelectionListener(TreeSelectionListener tsl) {
2783 listenerList.remove(TreeSelectionListener.class,tsl);
2784 if(listenerList.getListenerCount(TreeSelectionListener.class) == 0
2785 && selectionRedirector != null) {
2786 selectionModel.removeTreeSelectionListener
2787 (selectionRedirector);
2788 selectionRedirector = null;
2789 }
2790 }
2791
2792 /**
2793 * Returns an array of all the <code>TreeSelectionListener</code>s added
2794 * to this JTree with addTreeSelectionListener().
2795 *
2796 * @return all of the <code>TreeSelectionListener</code>s added or an empty
2797 * array if no listeners have been added
2798 * @since 1.4
2799 */
2800 public TreeSelectionListener[] getTreeSelectionListeners() {
2801 return (TreeSelectionListener[])listenerList.getListeners(
2802 TreeSelectionListener.class);
2803 }
2804
2805 /**
2806 * Notifies all listeners that have registered interest for
2807 * notification on this event type.
2808 *
2809 * @param e the <code>TreeSelectionEvent</code> to be fired;
2810 * generated by the
2811 * <code>TreeSelectionModel</code>
2812 * when a node is selected or deselected
2813 * @see EventListenerList
2814 */
2815 protected void fireValueChanged(TreeSelectionEvent e) {
2816 // Guaranteed to return a non-null array
2817 Object[] listeners = listenerList.getListenerList();
2818 // Process the listeners last to first, notifying
2819 // those that are interested in this event
2820 for (int i = listeners.length-2; i>=0; i-=2) {
2821 // TreeSelectionEvent e = null;
2822 if (listeners[i]==TreeSelectionListener.class) {
2914 // start search from the next/previous element froom the
2915 // selected element
2916 int increment = (bias == Position.Bias.Forward) ? 1 : -1;
2917 int row = startingRow;
2918 do {
2919 TreePath path = getPathForRow(row);
2920 String text = convertValueToText(
2921 path.getLastPathComponent(), isRowSelected(row),
2922 isExpanded(row), true, row, false);
2923
2924 if (text.toUpperCase().startsWith(prefix)) {
2925 return path;
2926 }
2927 row = (row + increment + max) % max;
2928 } while (row != startingRow);
2929 return null;
2930 }
2931
2932 // Serialization support.
2933 private void writeObject(ObjectOutputStream s) throws IOException {
2934 Vector values = new Vector();
2935
2936 s.defaultWriteObject();
2937 // Save the cellRenderer, if its Serializable.
2938 if(cellRenderer != null && cellRenderer instanceof Serializable) {
2939 values.addElement("cellRenderer");
2940 values.addElement(cellRenderer);
2941 }
2942 // Save the cellEditor, if its Serializable.
2943 if(cellEditor != null && cellEditor instanceof Serializable) {
2944 values.addElement("cellEditor");
2945 values.addElement(cellEditor);
2946 }
2947 // Save the treeModel, if its Serializable.
2948 if(treeModel != null && treeModel instanceof Serializable) {
2949 values.addElement("treeModel");
2950 values.addElement(treeModel);
2951 }
2952 // Save the selectionModel, if its Serializable.
2953 if(selectionModel != null && selectionModel instanceof Serializable) {
2954 values.addElement("selectionModel");
2961 values.addElement("expandedState");
2962 values.addElement(expandedData);
2963 }
2964
2965 s.writeObject(values);
2966 if (getUIClassID().equals(uiClassID)) {
2967 byte count = JComponent.getWriteObjCounter(this);
2968 JComponent.setWriteObjCounter(this, --count);
2969 if (count == 0 && ui != null) {
2970 ui.installUI(this);
2971 }
2972 }
2973 }
2974
2975 private void readObject(ObjectInputStream s)
2976 throws IOException, ClassNotFoundException {
2977 s.defaultReadObject();
2978
2979 // Create an instance of expanded state.
2980
2981 expandedState = new Hashtable();
2982
2983 expandedStack = new Stack();
2984
2985 Vector values = (Vector)s.readObject();
2986 int indexCounter = 0;
2987 int maxCounter = values.size();
2988
2989 if(indexCounter < maxCounter && values.elementAt(indexCounter).
2990 equals("cellRenderer")) {
2991 cellRenderer = (TreeCellRenderer)values.elementAt(++indexCounter);
2992 indexCounter++;
2993 }
2994 if(indexCounter < maxCounter && values.elementAt(indexCounter).
2995 equals("cellEditor")) {
2996 cellEditor = (TreeCellEditor)values.elementAt(++indexCounter);
2997 indexCounter++;
2998 }
2999 if(indexCounter < maxCounter && values.elementAt(indexCounter).
3000 equals("treeModel")) {
3001 treeModel = (TreeModel)values.elementAt(++indexCounter);
3002 indexCounter++;
3003 }
3016 selectionRedirector = new TreeSelectionRedirector();
3017 selectionModel.addTreeSelectionListener(selectionRedirector);
3018 }
3019 // Listener to TreeModel.
3020 if(treeModel != null) {
3021 treeModelListener = createTreeModelListener();
3022 if(treeModelListener != null)
3023 treeModel.addTreeModelListener(treeModelListener);
3024 }
3025 }
3026
3027 /**
3028 * Returns an object that can be archived indicating what nodes are
3029 * expanded and what aren't. The objects from the model are NOT
3030 * written out.
3031 */
3032 private Object getArchivableExpandedState() {
3033 TreeModel model = getModel();
3034
3035 if(model != null) {
3036 Enumeration paths = expandedState.keys();
3037
3038 if(paths != null) {
3039 Vector state = new Vector();
3040
3041 while(paths.hasMoreElements()) {
3042 TreePath path = (TreePath)paths.nextElement();
3043 Object archivePath;
3044
3045 try {
3046 archivePath = getModelIndexsForPath(path);
3047 } catch (Error error) {
3048 archivePath = null;
3049 }
3050 if(archivePath != null) {
3051 state.addElement(archivePath);
3052 state.addElement(expandedState.get(path));
3053 }
3054 }
3055 return state;
3056 }
3057 }
3058 return null;
3059 }
3060
3061 /**
3062 * Updates the expanded state of nodes in the tree based on the
3300 * @return the "block" increment for scrolling in the specified direction
3301 * @see JScrollBar#setBlockIncrement(int)
3302 */
3303 public int getScrollableBlockIncrement(Rectangle visibleRect,
3304 int orientation, int direction) {
3305 return (orientation == SwingConstants.VERTICAL) ? visibleRect.height :
3306 visibleRect.width;
3307 }
3308
3309 /**
3310 * Returns false to indicate that the width of the viewport does not
3311 * determine the width of the table, unless the preferred width of
3312 * the tree is smaller than the viewports width. In other words:
3313 * ensure that the tree is never smaller than its viewport.
3314 *
3315 * @return false
3316 * @see Scrollable#getScrollableTracksViewportWidth
3317 */
3318 public boolean getScrollableTracksViewportWidth() {
3319 if (getParent() instanceof JViewport) {
3320 return (((JViewport)getParent()).getWidth() > getPreferredSize().width);
3321 }
3322 return false;
3323 }
3324
3325 /**
3326 * Returns false to indicate that the height of the viewport does not
3327 * determine the height of the table, unless the preferred height
3328 * of the tree is smaller than the viewports height. In other words:
3329 * ensure that the tree is never smaller than its viewport.
3330 *
3331 * @return false
3332 * @see Scrollable#getScrollableTracksViewportHeight
3333 */
3334 public boolean getScrollableTracksViewportHeight() {
3335 if (getParent() instanceof JViewport) {
3336 return (((JViewport)getParent()).getHeight() > getPreferredSize().height);
3337 }
3338 return false;
3339 }
3340
3341 /**
3342 * Sets the expanded state of this <code>JTree</code>.
3343 * If <code>state</code> is
3344 * true, all parents of <code>path</code> and path are marked as
3345 * expanded. If <code>state</code> is false, all parents of
3346 * <code>path</code> are marked EXPANDED, but <code>path</code> itself
3347 * is marked collapsed.<p>
3348 * This will fail if a <code>TreeWillExpandListener</code> vetos it.
3349 */
3350 protected void setExpandedState(TreePath path, boolean state) {
3351 if(path != null) {
3352 // Make sure all parents of path are expanded.
3353 Stack stack;
3354 TreePath parentPath = path.getParentPath();
3355
3356 if (expandedStack.size() == 0) {
3357 stack = new Stack();
3358 }
3359 else {
3360 stack = (Stack)expandedStack.pop();
3361 }
3362
3363 try {
3364 while(parentPath != null) {
3365 if(isExpanded(parentPath)) {
3366 parentPath = null;
3367 }
3368 else {
3369 stack.push(parentPath);
3370 parentPath = parentPath.getParentPath();
3371 }
3372 }
3373 for(int counter = stack.size() - 1; counter >= 0; counter--) {
3374 parentPath = (TreePath)stack.pop();
3375 if(!isExpanded(parentPath)) {
3376 try {
3377 fireTreeWillExpand(parentPath);
3378 } catch (ExpandVetoException eve) {
3379 // Expand vetoed!
3380 return;
3381 }
3382 expandedState.put(parentPath, Boolean.TRUE);
3383 fireTreeExpanded(parentPath);
3384 if (accessibleContext != null) {
3385 ((AccessibleJTree)accessibleContext).
3386 fireVisibleDataPropertyChange();
3387 }
3388 }
3389 }
3390 }
3391 finally {
3392 if (expandedStack.size() < TEMP_STACK_SIZE) {
3393 stack.removeAllElements();
3394 expandedStack.push(stack);
3434 if (accessibleContext != null) {
3435 ((AccessibleJTree)accessibleContext).
3436 fireVisibleDataPropertyChange();
3437 }
3438 }
3439 }
3440 }
3441 }
3442
3443 /**
3444 * Returns an <code>Enumeration</code> of <code>TreePaths</code>
3445 * that have been expanded that
3446 * are descendants of <code>parent</code>.
3447 */
3448 protected Enumeration<TreePath>
3449 getDescendantToggledPaths(TreePath parent)
3450 {
3451 if(parent == null)
3452 return null;
3453
3454 Vector descendants = new Vector();
3455 Enumeration nodes = expandedState.keys();
3456 TreePath path;
3457
3458 while(nodes.hasMoreElements()) {
3459 path = (TreePath)nodes.nextElement();
3460 if(parent.isDescendant(path))
3461 descendants.addElement(path);
3462 }
3463 return descendants.elements();
3464 }
3465
3466 /**
3467 * Removes any descendants of the <code>TreePaths</code> in
3468 * <code>toRemove</code>
3469 * that have been expanded.
3470 */
3471 protected void
3472 removeDescendantToggledPaths(Enumeration<TreePath> toRemove)
3473 {
3474 if(toRemove != null) {
3475 while(toRemove.hasMoreElements()) {
3476 Enumeration descendants = getDescendantToggledPaths
3477 ((TreePath)toRemove.nextElement());
3478
3479 if(descendants != null) {
3480 while(descendants.hasMoreElements()) {
3481 expandedState.remove(descendants.nextElement());
3482 }
3483 }
3484 }
3485 }
3486 }
3487
3488 /**
3489 * Clears the cache of toggled tree paths. This does NOT send out
3490 * any <code>TreeExpansionListener</code> events.
3491 */
3492 protected void clearToggledPaths() {
3493 expandedState.clear();
3494 }
3495
3496 /**
3497 * Creates and returns an instance of <code>TreeModelHandler</code>.
4042 if (path != null) {
4043 // TIGER - 4839971
4044 // Set parent to null so AccessibleJTreeNode computes
4045 // its parent.
4046 AccessibleJTreeNode node = new AccessibleJTreeNode(JTree.this,
4047 path,
4048 null);
4049 PropertyChangeEvent pce = new PropertyChangeEvent(node,
4050 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4051 AccessibleState.COLLAPSED,
4052 AccessibleState.EXPANDED);
4053 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4054 null, pce);
4055 }
4056 }
4057
4058
4059 private AccessibleContext getCurrentAccessibleContext() {
4060 Component c = getCurrentComponent();
4061 if (c instanceof Accessible) {
4062 return (((Accessible) c).getAccessibleContext());
4063 } else {
4064 return null;
4065 }
4066 }
4067
4068 private Component getCurrentComponent() {
4069 // is the object visible?
4070 // if so, get row, selected, focus & leaf state,
4071 // and then get the renderer component and return it
4072 TreeModel model = JTree.this.getModel();
4073 if (model == null) {
4074 return null;
4075 }
4076 TreePath path = new TreePath(model.getRoot());
4077 if (JTree.this.isVisible(path)) {
4078 TreeCellRenderer r = JTree.this.getCellRenderer();
4079 TreeUI ui = JTree.this.getUI();
4080 if (ui != null) {
4081 int row = ui.getRowForPath(JTree.this, path);
4082 int lsr = JTree.this.getLeadSelectionRow();
4365 objChildPath[objChildPath.length-1] = childObj;
4366 return new TreePath(objChildPath);
4367 }
4368 }
4369
4370 /**
4371 * Get the AccessibleContext associated with this tree node.
4372 * In the implementation of the Java Accessibility API for
4373 * this class, return this object, which is its own
4374 * AccessibleContext.
4375 *
4376 * @return this object
4377 */
4378 public AccessibleContext getAccessibleContext() {
4379 return this;
4380 }
4381
4382 private AccessibleContext getCurrentAccessibleContext() {
4383 Component c = getCurrentComponent();
4384 if (c instanceof Accessible) {
4385 return (((Accessible) c).getAccessibleContext());
4386 } else {
4387 return null;
4388 }
4389 }
4390
4391 private Component getCurrentComponent() {
4392 // is the object visible?
4393 // if so, get row, selected, focus & leaf state,
4394 // and then get the renderer component and return it
4395 if (tree.isVisible(path)) {
4396 TreeCellRenderer r = tree.getCellRenderer();
4397 if (r == null) {
4398 return null;
4399 }
4400 TreeUI ui = tree.getUI();
4401 if (ui != null) {
4402 int row = ui.getRowForPath(JTree.this, path);
4403 boolean selected = tree.isPathSelected(path);
4404 boolean expanded = tree.isExpanded(path);
4405 boolean hasFocus = false; // how to tell?? -PK
4909 return false;
4910 }
4911 }
4912 }
4913
4914 public void setEnabled(boolean b) {
4915 AccessibleContext ac = getCurrentAccessibleContext();
4916 if (ac instanceof AccessibleComponent) {
4917 ((AccessibleComponent) ac).setEnabled(b);
4918 } else {
4919 Component c = getCurrentComponent();
4920 if (c != null) {
4921 c.setEnabled(b);
4922 }
4923 }
4924 }
4925
4926 public boolean isVisible() {
4927 Rectangle pathBounds = tree.getPathBounds(path);
4928 Rectangle parentBounds = tree.getVisibleRect();
4929 if (pathBounds != null && parentBounds != null &&
4930 parentBounds.intersects(pathBounds)) {
4931 return true;
4932 } else {
4933 return false;
4934 }
4935 }
4936
4937 public void setVisible(boolean b) {
4938 }
4939
4940 public boolean isShowing() {
4941 return (tree.isShowing() && isVisible());
4942 }
4943
4944 public boolean contains(Point p) {
4945 AccessibleContext ac = getCurrentAccessibleContext();
4946 if (ac instanceof AccessibleComponent) {
4947 Rectangle r = ((AccessibleComponent) ac).getBounds();
4948 return r.contains(p);
4949 } else {
4950 Component c = getCurrentComponent();
4951 if (c != null) {
4952 Rectangle r = c.getBounds();
4953 return r.contains(p);
4954 } else {
|
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
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;
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 public JTree(TreeModel newModel) {
653 super();
654 expandedStack = new Stack<Stack<TreePath>>();
655 toggleClickCount = 2;
656 expandedState = new Hashtable<TreePath, Boolean>();
657 setLayout(null);
658 rowHeight = 16;
659 visibleRowCount = 20;
660 rootVisible = true;
661 selectionModel = new DefaultTreeSelectionModel();
662 cellRenderer = null;
663 scrollsOnExpand = true;
664 setOpaque(true);
665 expandsSelectedPaths = true;
666 updateUI();
667 setModel(newModel);
668 }
669
670 /**
671 * Returns the L&F object that renders this component.
672 *
673 * @return the <code>TreeUI</code> object that renders this component
674 */
675 public TreeUI getUI() {
676 return (TreeUI)ui;
677 }
678
679 /**
680 * Sets the L&F object that renders this component.
681 *
682 * @param ui the <code>TreeUI</code> L&F object
683 * @see UIDefaults#getUI
684 * @beaninfo
685 * bound: true
686 * hidden: true
687 * attribute: visualUpdate true
688 * description: The UI object that implements the Component's LookAndFeel.
689 */
690 public void setUI(TreeUI ui) {
691 if (this.ui != ui) {
692 settingUI = true;
693 uiTreeExpansionListener = null;
694 try {
695 super.setUI(ui);
696 }
697 finally {
698 settingUI = false;
699 }
700 }
701 }
702
703 /**
704 * Notification from the <code>UIManager</code> that the L&F has changed.
705 * Replaces the current UI object with the latest version from the
706 * <code>UIManager</code>.
707 *
708 * @see JComponent#updateUI
709 */
710 public void updateUI() {
711 setUI((TreeUI)UIManager.getUI(this));
1256 public final DropMode getDropMode() {
1257 return dropMode;
1258 }
1259
1260 /**
1261 * Calculates a drop location in this component, representing where a
1262 * drop at the given point should insert data.
1263 *
1264 * @param p the point to calculate a drop location for
1265 * @return the drop location, or <code>null</code>
1266 */
1267 DropLocation dropLocationForPoint(Point p) {
1268 DropLocation location = null;
1269
1270 int row = getClosestRowForLocation(p.x, p.y);
1271 Rectangle bounds = getRowBounds(row);
1272 TreeModel model = getModel();
1273 Object root = (model == null) ? null : model.getRoot();
1274 TreePath rootPath = (root == null) ? null : new TreePath(root);
1275
1276 TreePath child;
1277 TreePath parent;
1278 boolean outside = row == -1
1279 || p.y < bounds.y
1280 || p.y >= bounds.y + bounds.height;
1281
1282 switch(dropMode) {
1283 case USE_SELECTION:
1284 case ON:
1285 if (outside) {
1286 location = new DropLocation(p, null, -1);
1287 } else {
1288 location = new DropLocation(p, getPathForRow(row), -1);
1289 }
1290
1291 break;
1292 case INSERT:
1293 case ON_OR_INSERT:
1294 if (row == -1) {
1295 if (root != null && !model.isLeaf(root) && isExpanded(rootPath)) {
1296 location = new DropLocation(p, rootPath, 0);
1297 } else {
1890
1891 /**
1892 * Returns an <code>Enumeration</code> of the descendants of the
1893 * path <code>parent</code> that
1894 * are currently expanded. If <code>parent</code> is not currently
1895 * expanded, this will return <code>null</code>.
1896 * If you expand/collapse nodes while
1897 * iterating over the returned <code>Enumeration</code>
1898 * this may not return all
1899 * the expanded paths, or may return paths that are no longer expanded.
1900 *
1901 * @param parent the path which is to be examined
1902 * @return an <code>Enumeration</code> of the descendents of
1903 * <code>parent</code>, or <code>null</code> if
1904 * <code>parent</code> is not currently expanded
1905 */
1906 public Enumeration<TreePath> getExpandedDescendants(TreePath parent) {
1907 if(!isExpanded(parent))
1908 return null;
1909
1910 Enumeration<TreePath> toggledPaths = expandedState.keys();
1911 Vector<TreePath> elements = null;
1912 TreePath path;
1913 Object value;
1914
1915 if(toggledPaths != null) {
1916 while(toggledPaths.hasMoreElements()) {
1917 path = toggledPaths.nextElement();
1918 value = expandedState.get(path);
1919 // Add the path if it is expanded, a descendant of parent,
1920 // and it is visible (all parents expanded). This is rather
1921 // expensive!
1922 if(path != parent && value != null &&
1923 ((Boolean)value).booleanValue() &&
1924 parent.isDescendant(path) && isVisible(path)) {
1925 if (elements == null) {
1926 elements = new Vector<TreePath>();
1927 }
1928 elements.addElement(path);
1929 }
1930 }
1931 }
1932 if (elements == null) {
1933 Set<TreePath> empty = Collections.emptySet();
1934 return Collections.enumeration(empty);
1935 }
1936 return elements.elements();
1937 }
1938
1939 /**
1940 * Returns true if the node identified by the path has ever been
1941 * expanded.
1942 * @return true if the <code>path</code> has ever been expanded
1943 */
1944 public boolean hasBeenExpanded(TreePath path) {
1945 return (path != null && expandedState.get(path) != null);
1946 }
1947
1948 /**
1949 * Returns true if the node identified by the path is currently expanded,
1950 *
1951 * @param path the <code>TreePath</code> specifying the node to check
1952 * @return false if any of the nodes in the node's path are collapsed,
1953 * true if all nodes in the path are expanded
1954 */
1955 public boolean isExpanded(TreePath path) {
1956 if(path == null)
1957 return false;
1958
1959 // Is this node expanded?
1960 Boolean value = expandedState.get(path);
1961
1962 if(value == null || !value.booleanValue())
1963 return false;
1964
1965 // It is, make sure its parent is also expanded.
1966 TreePath parentPath = path.getParentPath();
1967
1968 if(parentPath != null)
1969 return isExpanded(parentPath);
1970 return true;
1971 }
1972
1973 /**
1974 * Returns true if the node at the specified display row is currently
1975 * expanded.
1976 *
1977 * @param row the row to check, where 0 is the first row in the
1978 * display
1979 * @return true if the node is currently expanded, otherwise false
1980 */
1981 public boolean isExpanded(int row) {
1982 TreeUI tree = getUI();
1983
1984 if(tree != null) {
1985 TreePath path = tree.getPathForRow(this, row);
1986
1987 if(path != null) {
1988 Boolean value = expandedState.get(path);
1989
1990 return (value != null && value.booleanValue());
1991 }
1992 }
1993 return false;
1994 }
1995
1996 /**
1997 * Returns true if the value identified by path is currently collapsed,
1998 * this will return false if any of the values in path are currently
1999 * not being displayed.
2000 *
2001 * @param path the <code>TreePath</code> to check
2002 * @return true if any of the nodes in the node's path are collapsed,
2003 * false if all nodes in the path are expanded
2004 */
2005 public boolean isCollapsed(TreePath path) {
2006 return !isExpanded(path);
2007 }
2008
2590 * Removes a listener for <code>TreeExpansion</code> events.
2591 *
2592 * @param tel the <code>TreeExpansionListener</code> to remove
2593 */
2594 public void removeTreeExpansionListener(TreeExpansionListener tel) {
2595 listenerList.remove(TreeExpansionListener.class, tel);
2596 if (uiTreeExpansionListener == tel) {
2597 uiTreeExpansionListener = null;
2598 }
2599 }
2600
2601 /**
2602 * Returns an array of all the <code>TreeExpansionListener</code>s added
2603 * to this JTree with addTreeExpansionListener().
2604 *
2605 * @return all of the <code>TreeExpansionListener</code>s added or an empty
2606 * array if no listeners have been added
2607 * @since 1.4
2608 */
2609 public TreeExpansionListener[] getTreeExpansionListeners() {
2610 return listenerList.getListeners(TreeExpansionListener.class);
2611 }
2612
2613 /**
2614 * Adds a listener for <code>TreeWillExpand</code> events.
2615 *
2616 * @param tel a <code>TreeWillExpandListener</code> that will be notified
2617 * when a tree node will be expanded or collapsed (a "negative
2618 * expansion")
2619 */
2620 public void addTreeWillExpandListener(TreeWillExpandListener tel) {
2621 listenerList.add(TreeWillExpandListener.class, tel);
2622 }
2623
2624 /**
2625 * Removes a listener for <code>TreeWillExpand</code> events.
2626 *
2627 * @param tel the <code>TreeWillExpandListener</code> to remove
2628 */
2629 public void removeTreeWillExpandListener(TreeWillExpandListener tel) {
2630 listenerList.remove(TreeWillExpandListener.class, tel);
2631 }
2632
2633 /**
2634 * Returns an array of all the <code>TreeWillExpandListener</code>s added
2635 * to this JTree with addTreeWillExpandListener().
2636 *
2637 * @return all of the <code>TreeWillExpandListener</code>s added or an empty
2638 * array if no listeners have been added
2639 * @since 1.4
2640 */
2641 public TreeWillExpandListener[] getTreeWillExpandListeners() {
2642 return listenerList.getListeners(TreeWillExpandListener.class);
2643 }
2644
2645 /**
2646 * Notifies all listeners that have registered interest for
2647 * notification on this event type. The event instance
2648 * is lazily created using the <code>path</code> parameter.
2649 *
2650 * @param path the <code>TreePath</code> indicating the node that was
2651 * expanded
2652 * @see EventListenerList
2653 */
2654 public void fireTreeExpanded(TreePath path) {
2655 // Guaranteed to return a non-null array
2656 Object[] listeners = listenerList.getListenerList();
2657 TreeExpansionEvent e = null;
2658 if (uiTreeExpansionListener != null) {
2659 e = new TreeExpansionEvent(this, path);
2660 uiTreeExpansionListener.treeExpanded(e);
2661 }
2662 // Process the listeners last to first, notifying
2779 */
2780 public void removeTreeSelectionListener(TreeSelectionListener tsl) {
2781 listenerList.remove(TreeSelectionListener.class,tsl);
2782 if(listenerList.getListenerCount(TreeSelectionListener.class) == 0
2783 && selectionRedirector != null) {
2784 selectionModel.removeTreeSelectionListener
2785 (selectionRedirector);
2786 selectionRedirector = null;
2787 }
2788 }
2789
2790 /**
2791 * Returns an array of all the <code>TreeSelectionListener</code>s added
2792 * to this JTree with addTreeSelectionListener().
2793 *
2794 * @return all of the <code>TreeSelectionListener</code>s added or an empty
2795 * array if no listeners have been added
2796 * @since 1.4
2797 */
2798 public TreeSelectionListener[] getTreeSelectionListeners() {
2799 return listenerList.getListeners(TreeSelectionListener.class);
2800 }
2801
2802 /**
2803 * Notifies all listeners that have registered interest for
2804 * notification on this event type.
2805 *
2806 * @param e the <code>TreeSelectionEvent</code> to be fired;
2807 * generated by the
2808 * <code>TreeSelectionModel</code>
2809 * when a node is selected or deselected
2810 * @see EventListenerList
2811 */
2812 protected void fireValueChanged(TreeSelectionEvent e) {
2813 // Guaranteed to return a non-null array
2814 Object[] listeners = listenerList.getListenerList();
2815 // Process the listeners last to first, notifying
2816 // those that are interested in this event
2817 for (int i = listeners.length-2; i>=0; i-=2) {
2818 // TreeSelectionEvent e = null;
2819 if (listeners[i]==TreeSelectionListener.class) {
2911 // start search from the next/previous element froom the
2912 // selected element
2913 int increment = (bias == Position.Bias.Forward) ? 1 : -1;
2914 int row = startingRow;
2915 do {
2916 TreePath path = getPathForRow(row);
2917 String text = convertValueToText(
2918 path.getLastPathComponent(), isRowSelected(row),
2919 isExpanded(row), true, row, false);
2920
2921 if (text.toUpperCase().startsWith(prefix)) {
2922 return path;
2923 }
2924 row = (row + increment + max) % max;
2925 } while (row != startingRow);
2926 return null;
2927 }
2928
2929 // Serialization support.
2930 private void writeObject(ObjectOutputStream s) throws IOException {
2931 Vector<Object> values = new Vector<Object>();
2932
2933 s.defaultWriteObject();
2934 // Save the cellRenderer, if its Serializable.
2935 if(cellRenderer != null && cellRenderer instanceof Serializable) {
2936 values.addElement("cellRenderer");
2937 values.addElement(cellRenderer);
2938 }
2939 // Save the cellEditor, if its Serializable.
2940 if(cellEditor != null && cellEditor instanceof Serializable) {
2941 values.addElement("cellEditor");
2942 values.addElement(cellEditor);
2943 }
2944 // Save the treeModel, if its Serializable.
2945 if(treeModel != null && treeModel instanceof Serializable) {
2946 values.addElement("treeModel");
2947 values.addElement(treeModel);
2948 }
2949 // Save the selectionModel, if its Serializable.
2950 if(selectionModel != null && selectionModel instanceof Serializable) {
2951 values.addElement("selectionModel");
2958 values.addElement("expandedState");
2959 values.addElement(expandedData);
2960 }
2961
2962 s.writeObject(values);
2963 if (getUIClassID().equals(uiClassID)) {
2964 byte count = JComponent.getWriteObjCounter(this);
2965 JComponent.setWriteObjCounter(this, --count);
2966 if (count == 0 && ui != null) {
2967 ui.installUI(this);
2968 }
2969 }
2970 }
2971
2972 private void readObject(ObjectInputStream s)
2973 throws IOException, ClassNotFoundException {
2974 s.defaultReadObject();
2975
2976 // Create an instance of expanded state.
2977
2978 expandedState = new Hashtable<TreePath, Boolean>();
2979
2980 expandedStack = new Stack<Stack<TreePath>>();
2981
2982 Vector values = (Vector)s.readObject();
2983 int indexCounter = 0;
2984 int maxCounter = values.size();
2985
2986 if(indexCounter < maxCounter && values.elementAt(indexCounter).
2987 equals("cellRenderer")) {
2988 cellRenderer = (TreeCellRenderer)values.elementAt(++indexCounter);
2989 indexCounter++;
2990 }
2991 if(indexCounter < maxCounter && values.elementAt(indexCounter).
2992 equals("cellEditor")) {
2993 cellEditor = (TreeCellEditor)values.elementAt(++indexCounter);
2994 indexCounter++;
2995 }
2996 if(indexCounter < maxCounter && values.elementAt(indexCounter).
2997 equals("treeModel")) {
2998 treeModel = (TreeModel)values.elementAt(++indexCounter);
2999 indexCounter++;
3000 }
3013 selectionRedirector = new TreeSelectionRedirector();
3014 selectionModel.addTreeSelectionListener(selectionRedirector);
3015 }
3016 // Listener to TreeModel.
3017 if(treeModel != null) {
3018 treeModelListener = createTreeModelListener();
3019 if(treeModelListener != null)
3020 treeModel.addTreeModelListener(treeModelListener);
3021 }
3022 }
3023
3024 /**
3025 * Returns an object that can be archived indicating what nodes are
3026 * expanded and what aren't. The objects from the model are NOT
3027 * written out.
3028 */
3029 private Object getArchivableExpandedState() {
3030 TreeModel model = getModel();
3031
3032 if(model != null) {
3033 Enumeration<TreePath> paths = expandedState.keys();
3034
3035 if(paths != null) {
3036 Vector<Object> state = new Vector<Object>();
3037
3038 while(paths.hasMoreElements()) {
3039 TreePath path = paths.nextElement();
3040 Object archivePath;
3041
3042 try {
3043 archivePath = getModelIndexsForPath(path);
3044 } catch (Error error) {
3045 archivePath = null;
3046 }
3047 if(archivePath != null) {
3048 state.addElement(archivePath);
3049 state.addElement(expandedState.get(path));
3050 }
3051 }
3052 return state;
3053 }
3054 }
3055 return null;
3056 }
3057
3058 /**
3059 * Updates the expanded state of nodes in the tree based on the
3297 * @return the "block" increment for scrolling in the specified direction
3298 * @see JScrollBar#setBlockIncrement(int)
3299 */
3300 public int getScrollableBlockIncrement(Rectangle visibleRect,
3301 int orientation, int direction) {
3302 return (orientation == SwingConstants.VERTICAL) ? visibleRect.height :
3303 visibleRect.width;
3304 }
3305
3306 /**
3307 * Returns false to indicate that the width of the viewport does not
3308 * determine the width of the table, unless the preferred width of
3309 * the tree is smaller than the viewports width. In other words:
3310 * ensure that the tree is never smaller than its viewport.
3311 *
3312 * @return false
3313 * @see Scrollable#getScrollableTracksViewportWidth
3314 */
3315 public boolean getScrollableTracksViewportWidth() {
3316 if (getParent() instanceof JViewport) {
3317 return getParent().getWidth() > getPreferredSize().width;
3318 }
3319 return false;
3320 }
3321
3322 /**
3323 * Returns false to indicate that the height of the viewport does not
3324 * determine the height of the table, unless the preferred height
3325 * of the tree is smaller than the viewports height. In other words:
3326 * ensure that the tree is never smaller than its viewport.
3327 *
3328 * @return false
3329 * @see Scrollable#getScrollableTracksViewportHeight
3330 */
3331 public boolean getScrollableTracksViewportHeight() {
3332 if (getParent() instanceof JViewport) {
3333 return getParent().getHeight() > getPreferredSize().height;
3334 }
3335 return false;
3336 }
3337
3338 /**
3339 * Sets the expanded state of this <code>JTree</code>.
3340 * If <code>state</code> is
3341 * true, all parents of <code>path</code> and path are marked as
3342 * expanded. If <code>state</code> is false, all parents of
3343 * <code>path</code> are marked EXPANDED, but <code>path</code> itself
3344 * is marked collapsed.<p>
3345 * This will fail if a <code>TreeWillExpandListener</code> vetos it.
3346 */
3347 protected void setExpandedState(TreePath path, boolean state) {
3348 if(path != null) {
3349 // Make sure all parents of path are expanded.
3350 Stack<TreePath> stack;
3351 TreePath parentPath = path.getParentPath();
3352
3353 if (expandedStack.size() == 0) {
3354 stack = new Stack<TreePath>();
3355 }
3356 else {
3357 stack = expandedStack.pop();
3358 }
3359
3360 try {
3361 while(parentPath != null) {
3362 if(isExpanded(parentPath)) {
3363 parentPath = null;
3364 }
3365 else {
3366 stack.push(parentPath);
3367 parentPath = parentPath.getParentPath();
3368 }
3369 }
3370 for(int counter = stack.size() - 1; counter >= 0; counter--) {
3371 parentPath = stack.pop();
3372 if(!isExpanded(parentPath)) {
3373 try {
3374 fireTreeWillExpand(parentPath);
3375 } catch (ExpandVetoException eve) {
3376 // Expand vetoed!
3377 return;
3378 }
3379 expandedState.put(parentPath, Boolean.TRUE);
3380 fireTreeExpanded(parentPath);
3381 if (accessibleContext != null) {
3382 ((AccessibleJTree)accessibleContext).
3383 fireVisibleDataPropertyChange();
3384 }
3385 }
3386 }
3387 }
3388 finally {
3389 if (expandedStack.size() < TEMP_STACK_SIZE) {
3390 stack.removeAllElements();
3391 expandedStack.push(stack);
3431 if (accessibleContext != null) {
3432 ((AccessibleJTree)accessibleContext).
3433 fireVisibleDataPropertyChange();
3434 }
3435 }
3436 }
3437 }
3438 }
3439
3440 /**
3441 * Returns an <code>Enumeration</code> of <code>TreePaths</code>
3442 * that have been expanded that
3443 * are descendants of <code>parent</code>.
3444 */
3445 protected Enumeration<TreePath>
3446 getDescendantToggledPaths(TreePath parent)
3447 {
3448 if(parent == null)
3449 return null;
3450
3451 Vector<TreePath> descendants = new Vector<TreePath>();
3452 Enumeration<TreePath> nodes = expandedState.keys();
3453
3454 while(nodes.hasMoreElements()) {
3455 TreePath path = nodes.nextElement();
3456 if(parent.isDescendant(path))
3457 descendants.addElement(path);
3458 }
3459 return descendants.elements();
3460 }
3461
3462 /**
3463 * Removes any descendants of the <code>TreePaths</code> in
3464 * <code>toRemove</code>
3465 * that have been expanded.
3466 */
3467 protected void
3468 removeDescendantToggledPaths(Enumeration<TreePath> toRemove)
3469 {
3470 if(toRemove != null) {
3471 while(toRemove.hasMoreElements()) {
3472 Enumeration descendants = getDescendantToggledPaths
3473 (toRemove.nextElement());
3474
3475 if(descendants != null) {
3476 while(descendants.hasMoreElements()) {
3477 expandedState.remove(descendants.nextElement());
3478 }
3479 }
3480 }
3481 }
3482 }
3483
3484 /**
3485 * Clears the cache of toggled tree paths. This does NOT send out
3486 * any <code>TreeExpansionListener</code> events.
3487 */
3488 protected void clearToggledPaths() {
3489 expandedState.clear();
3490 }
3491
3492 /**
3493 * Creates and returns an instance of <code>TreeModelHandler</code>.
4038 if (path != null) {
4039 // TIGER - 4839971
4040 // Set parent to null so AccessibleJTreeNode computes
4041 // its parent.
4042 AccessibleJTreeNode node = new AccessibleJTreeNode(JTree.this,
4043 path,
4044 null);
4045 PropertyChangeEvent pce = new PropertyChangeEvent(node,
4046 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4047 AccessibleState.COLLAPSED,
4048 AccessibleState.EXPANDED);
4049 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
4050 null, pce);
4051 }
4052 }
4053
4054
4055 private AccessibleContext getCurrentAccessibleContext() {
4056 Component c = getCurrentComponent();
4057 if (c instanceof Accessible) {
4058 return c.getAccessibleContext();
4059 } else {
4060 return null;
4061 }
4062 }
4063
4064 private Component getCurrentComponent() {
4065 // is the object visible?
4066 // if so, get row, selected, focus & leaf state,
4067 // and then get the renderer component and return it
4068 TreeModel model = JTree.this.getModel();
4069 if (model == null) {
4070 return null;
4071 }
4072 TreePath path = new TreePath(model.getRoot());
4073 if (JTree.this.isVisible(path)) {
4074 TreeCellRenderer r = JTree.this.getCellRenderer();
4075 TreeUI ui = JTree.this.getUI();
4076 if (ui != null) {
4077 int row = ui.getRowForPath(JTree.this, path);
4078 int lsr = JTree.this.getLeadSelectionRow();
4361 objChildPath[objChildPath.length-1] = childObj;
4362 return new TreePath(objChildPath);
4363 }
4364 }
4365
4366 /**
4367 * Get the AccessibleContext associated with this tree node.
4368 * In the implementation of the Java Accessibility API for
4369 * this class, return this object, which is its own
4370 * AccessibleContext.
4371 *
4372 * @return this object
4373 */
4374 public AccessibleContext getAccessibleContext() {
4375 return this;
4376 }
4377
4378 private AccessibleContext getCurrentAccessibleContext() {
4379 Component c = getCurrentComponent();
4380 if (c instanceof Accessible) {
4381 return c.getAccessibleContext();
4382 } else {
4383 return null;
4384 }
4385 }
4386
4387 private Component getCurrentComponent() {
4388 // is the object visible?
4389 // if so, get row, selected, focus & leaf state,
4390 // and then get the renderer component and return it
4391 if (tree.isVisible(path)) {
4392 TreeCellRenderer r = tree.getCellRenderer();
4393 if (r == null) {
4394 return null;
4395 }
4396 TreeUI ui = tree.getUI();
4397 if (ui != null) {
4398 int row = ui.getRowForPath(JTree.this, path);
4399 boolean selected = tree.isPathSelected(path);
4400 boolean expanded = tree.isExpanded(path);
4401 boolean hasFocus = false; // how to tell?? -PK
4905 return false;
4906 }
4907 }
4908 }
4909
4910 public void setEnabled(boolean b) {
4911 AccessibleContext ac = getCurrentAccessibleContext();
4912 if (ac instanceof AccessibleComponent) {
4913 ((AccessibleComponent) ac).setEnabled(b);
4914 } else {
4915 Component c = getCurrentComponent();
4916 if (c != null) {
4917 c.setEnabled(b);
4918 }
4919 }
4920 }
4921
4922 public boolean isVisible() {
4923 Rectangle pathBounds = tree.getPathBounds(path);
4924 Rectangle parentBounds = tree.getVisibleRect();
4925 return pathBounds != null && parentBounds != null &&
4926 parentBounds.intersects(pathBounds);
4927 }
4928
4929 public void setVisible(boolean b) {
4930 }
4931
4932 public boolean isShowing() {
4933 return (tree.isShowing() && isVisible());
4934 }
4935
4936 public boolean contains(Point p) {
4937 AccessibleContext ac = getCurrentAccessibleContext();
4938 if (ac instanceof AccessibleComponent) {
4939 Rectangle r = ((AccessibleComponent) ac).getBounds();
4940 return r.contains(p);
4941 } else {
4942 Component c = getCurrentComponent();
4943 if (c != null) {
4944 Rectangle r = c.getBounds();
4945 return r.contains(p);
4946 } else {
|