1 /* JTree.java
2    Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
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)
9 any later version.
10 
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.
15 
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
19 02110-1301 USA.
20 
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
24 combination.
25 
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. */
37 
38 package javax.swing;
39 
40 import java.awt.Color;
41 import java.awt.Cursor;
42 import java.awt.Dimension;
43 import java.awt.Font;
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;
55 
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;
85 
86 public class JTree extends JComponent implements Scrollable, Accessible
87 {
88 
89   /**
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.
93    */
94   protected class AccessibleJTree extends JComponent.AccessibleJComponent
95       implements AccessibleSelection, TreeSelectionListener, TreeModelListener,
96       TreeExpansionListener
97   {
98 
99     /**
100      * This class implements accessibility support for the JTree child. It provides
101      * an implementation of the Java Accessibility API appropriate to tree nodes.
102      */
103     protected class AccessibleJTreeNode extends AccessibleContext
104        implements Accessible, AccessibleComponent, AccessibleSelection,
105        AccessibleAction
106     {
107 
108       private JTree tree;
109       private TreePath tp;
110       private Accessible acc;
111       private AccessibleStateSet states;
112       private Vector selectionList;
113       private Vector actionList;
114       private TreeModel mod;
115       private Cursor cursor;
116 
117       /**
118        * Constructs an AccessibleJTreeNode
119        *
120        * @param t - the current tree
121        * @param p - the current path to be dealt with
122        * @param ap - the accessible object to use
123        */
AccessibleJTreeNode(JTree t, TreePath p, Accessible ap)124       public AccessibleJTreeNode(JTree t, TreePath p, Accessible ap)
125       {
126         states = new AccessibleStateSet();
127         selectionList = new Vector();
128         actionList = new Vector();
129         mod = tree.getModel();
130         cursor = JTree.this.getCursor();
131 
132         tree = t;
133         tp = p;
134         acc = ap;
135 
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++)
140           {
141             TreePath sel = selected[i];
142             if ((sel.getParentPath()).equals(tp))
143               selectionList.add(sel);
144           }
145 
146         // Add all the actions available for a node to
147         // the action list.
148         actionList.add("EXPAND");
149         actionList.add("COLLAPSE");
150         actionList.add("EDIT");
151         actionList.add("SELECT");
152         actionList.add("DESELECT");
153       }
154 
155       /**
156        * Adds the specified selected item in the object to the object's
157        * selection.
158        *
159        * @param i - the i-th child of this node.
160        */
addAccessibleSelection(int i)161       public void addAccessibleSelection(int i)
162       {
163         if (mod != null)
164           {
165             Object child = mod.getChild(tp.getLastPathComponent(), i);
166             if (child != null)
167               {
168                 if (!states.contains(AccessibleState.MULTISELECTABLE))
169                   clearAccessibleSelection();
170                 selectionList.add(child);
171                 tree.addSelectionPath(tp.pathByAddingChild(child));
172               }
173           }
174       }
175 
176       /**
177        * Adds the specified focus listener to receive focus events
178        * from this component.
179        *
180        * @param l - the new focus listener
181        */
addFocusListener(FocusListener l)182       public void addFocusListener(FocusListener l)
183       {
184         tree.addFocusListener(l);
185       }
186 
187       /**
188        * Add a PropertyChangeListener to the listener list.
189        *
190        * @param l - the new property change listener
191        */
addPropertyChangeListener(PropertyChangeListener l)192       public void addPropertyChangeListener(PropertyChangeListener l)
193       {
194         // Nothing to do here.
195       }
196 
197       /**
198        * Clears the selection in the object, so that nothing in the
199        * object is selected.
200        */
clearAccessibleSelection()201       public void clearAccessibleSelection()
202       {
203         selectionList.clear();
204       }
205 
206       /**
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.
210        *
211        * @param p - the point to check
212        * @return true if p is in the bounds
213        */
contains(Point p)214       public boolean contains(Point p)
215       {
216         return getBounds().contains(p);
217       }
218 
219       /**
220        * Perform the specified Action on the tree node.
221        *
222        * @param i - the i-th action to perform
223        * @return true if the the action was performed; else false.
224        */
doAccessibleAction(int i)225       public boolean doAccessibleAction(int i)
226       {
227         if (i >= actionList.size() || i < 0)
228           return false;
229 
230         if (actionList.get(i).equals("EXPAND"))
231           tree.expandPath(tp);
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);
240         else
241           return false;
242         return true;
243       }
244 
245       /**
246        * Get the AccessibleAction associated with this object.
247        *
248        * @return the action
249        */
getAccessibleAction()250       public AccessibleAction getAccessibleAction()
251       {
252         return this;
253       }
254 
255       /**
256        * Returns the number of accessible actions available in this tree node.
257        *
258        * @return the number of actions
259        */
getAccessibleActionCount()260       public int getAccessibleActionCount()
261       {
262         return actionList.size();
263       }
264 
265       /**
266        * Return a description of the specified action of the tree node.
267        *
268        * @param i - the i-th action's description
269        * @return a description of the action
270        */
getAccessibleActionDescription(int i)271       public String getAccessibleActionDescription(int i)
272       {
273         if (i < 0 || i >= actionList.size())
274           return (actionList.get(i)).toString();
275         return super.getAccessibleDescription();
276       }
277 
278       /**
279        * Returns the Accessible child, if one exists, contained at the
280        * local coordinate Point.
281        *
282        * @param p - the point of the accessible
283        * @return the accessible at point p if it exists
284        */
getAccessibleAt(Point p)285       public Accessible getAccessibleAt(Point p)
286       {
287         TreePath acc = tree.getClosestPathForLocation(p.x, p.y);
288         if (acc != null)
289           return new AccessibleJTreeNode(tree, acc, this);
290         return null;
291       }
292 
293       /**
294        * Return the specified Accessible child of the object.
295        *
296        * @param i - the i-th child of the current path
297        * @return the child if it exists
298        */
getAccessibleChild(int i)299       public Accessible getAccessibleChild(int i)
300       {
301         if (mod != null)
302           {
303             Object child = mod.getChild(tp.getLastPathComponent(), i);
304             if (child != null)
305               return new AccessibleJTreeNode(tree, tp.pathByAddingChild(child),
306                                              acc);
307           }
308         return null;
309       }
310 
311       /**
312        * Returns the number of accessible children in the object.
313        *
314        * @return the number of children the current node has
315        */
getAccessibleChildrenCount()316       public int getAccessibleChildrenCount()
317       {
318         TreeModel mod = getModel();
319         if (mod != null)
320           return mod.getChildCount(tp.getLastPathComponent());
321         return 0;
322       }
323 
324       /**
325        * Get the AccessibleComponent associated with this object.
326        *
327        * @return the accessible component if it is supported.
328        */
getAccessibleComponent()329       public AccessibleComponent getAccessibleComponent()
330       {
331         return this;
332       }
333 
334       /**
335        * Get the AccessibleContext associated with this tree node.
336        *
337        * @return an instance of this class
338        */
getAccessibleContext()339       public AccessibleContext getAccessibleContext()
340       {
341         return this;
342       }
343 
344       /**
345        * Get the accessible description of this object.
346        *
347        * @return the accessible description
348        */
getAccessibleDescription()349       public String getAccessibleDescription()
350       {
351         return super.getAccessibleDescription();
352       }
353 
354       /**
355        * Get the index of this object in its accessible parent.
356        *
357        * @return the index of this in the parent.
358        */
getAccessibleIndexInParent()359       public int getAccessibleIndexInParent()
360       {
361         AccessibleContext parent = getAccessibleParent().getAccessibleContext();
362         if (parent != null)
363           for (int i = 0; i < parent.getAccessibleChildrenCount(); i++)
364             {
365               if ((parent.getAccessibleChild(i)).equals(this))
366                 return i;
367             }
368         return -1;
369       }
370 
371       /**
372        * Get the accessible name of this object.
373        *
374        * @return the accessible name
375        */
getAccessibleName()376       public String getAccessibleName()
377       {
378         return super.getAccessibleName();
379       }
380 
381       /**
382        * Get the Accessible parent of this object.
383        *
384        * @return the accessible parent if it exists.
385        */
getAccessibleParent()386       public Accessible getAccessibleParent()
387       {
388         return super.getAccessibleParent();
389       }
390 
391       /**
392        * Get the role of this object.
393        *
394        * @return the accessible role
395        */
getAccessibleRole()396       public AccessibleRole getAccessibleRole()
397       {
398         return AccessibleJTree.this.getAccessibleRole();
399       }
400 
401       /**
402        * Get the AccessibleSelection associated with this object if one exists.
403        *
404        * @return the accessible selection for this.
405        */
getAccessibleSelection()406       public AccessibleSelection getAccessibleSelection()
407       {
408         return this;
409       }
410 
411       /**
412        * Returns an Accessible representing the specified selected item
413        * in the object.
414        *
415        * @return the accessible representing a certain selected item.
416        */
getAccessibleSelection(int i)417       public Accessible getAccessibleSelection(int i)
418       {
419         if (i > 0 && i < getAccessibleSelectionCount())
420             return new AccessibleJTreeNode(tree,
421                   tp.pathByAddingChild(selectionList.get(i)), acc);
422         return null;
423       }
424 
425       /**
426        * Returns the number of items currently selected.
427        *
428        * @return the number of items selected.
429        */
getAccessibleSelectionCount()430       public int getAccessibleSelectionCount()
431       {
432         return selectionList.size();
433       }
434 
435       /**
436        * Get the state set of this object.
437        *
438        * @return the state set for this object
439        */
getAccessibleStateSet()440       public AccessibleStateSet getAccessibleStateSet()
441       {
442         if (isVisible())
443           states.add(AccessibleState.VISIBLE);
444         if (tree.isCollapsed(tp))
445           states.add(AccessibleState.COLLAPSED);
446         if (tree.isEditable())
447           states.add(AccessibleState.EDITABLE);
448         if (mod != null &&
449             !mod.isLeaf(tp.getLastPathComponent()))
450           states.add(AccessibleState.EXPANDABLE);
451         if (tree.isExpanded(tp))
452           states.add(AccessibleState.EXPANDED);
453         if (isFocusable())
454           states.add(AccessibleState.FOCUSABLE);
455         if (hasFocus())
456           states.add(AccessibleState.FOCUSED);
457         if (tree.getSelectionModel().getSelectionMode() !=
458           TreeSelectionModel.SINGLE_TREE_SELECTION)
459           states.add(AccessibleState.MULTISELECTABLE);
460         if (tree.isOpaque())
461           states.add(AccessibleState.OPAQUE);
462         if (tree.isPathSelected(tp))
463           states.add(AccessibleState.SELECTED);
464         if (isShowing())
465           states.add(AccessibleState.SHOWING);
466 
467         states.add(AccessibleState.SELECTABLE);
468         return states;
469       }
470 
471       /**
472        * Get the AccessibleText associated with this object if one exists.
473        *
474        * @return the accessible text
475        */
getAccessibleText()476       public AccessibleText getAccessibleText()
477       {
478         return super.getAccessibleText();
479       }
480 
481       /**
482        * Get the AccessibleValue associated with this object if one exists.
483        *
484        * @return the accessible value if it exists
485        */
getAccessibleValue()486       public AccessibleValue getAccessibleValue()
487       {
488         return super.getAccessibleValue();
489       }
490 
491       /**
492        * Get the background color of this object.
493        *
494        * @return the color of the background.
495        */
getBackground()496       public Color getBackground()
497       {
498         return tree.getBackground();
499       }
500 
501       /**
502        * Gets the bounds of this object in the form of a Rectangle object.
503        *
504        * @return the bounds of the current node.
505        */
getBounds()506       public Rectangle getBounds()
507       {
508         return tree.getPathBounds(tp);
509       }
510 
511       /**
512        * Gets the Cursor of this object.
513        *
514        * @return the cursor for the current node
515        */
getCursor()516       public Cursor getCursor()
517       {
518         return cursor;
519       }
520 
521       /**
522        * Gets the Font of this object.
523        *
524        * @return the font for the current node
525        */
getFont()526       public Font getFont()
527       {
528         return tree.getFont();
529       }
530 
531       /**
532        * Gets the FontMetrics of this object.
533        *
534        * @param f - the current font.
535        * @return the font metrics for the given font.
536        */
getFontMetrics(Font f)537       public FontMetrics getFontMetrics(Font f)
538       {
539         return tree.getFontMetrics(f);
540       }
541 
542       /**
543        * Get the foreground color of this object.
544        *
545        * @return the foreground for this object.
546        */
getForeground()547       public Color getForeground()
548       {
549         return tree.getForeground();
550       }
551 
552       /**
553        * Gets the locale of the component.
554        *
555        * @return the locale of the component.
556        */
getLocale()557       public Locale getLocale()
558       {
559         return tree.getLocale();
560       }
561 
562       /**
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.
566        *
567        * @return the location of the current node.
568        */
getLocation()569       public Point getLocation()
570       {
571         return getLocationInJTree();
572       }
573 
574       /**
575        * Returns the location in the tree.
576        *
577        * @return the location in the JTree.
578        */
getLocationInJTree()579       protected Point getLocationInJTree()
580       {
581         Rectangle bounds = tree.getPathBounds(tp);
582         return new Point(bounds.x, bounds.y);
583       }
584 
585       /**
586        * Returns the location of the object on the screen.
587        *
588        * @return the location of the object on the screen.
589        */
getLocationOnScreen()590       public Point getLocationOnScreen()
591       {
592         Point loc = getLocation();
593         SwingUtilities.convertPointToScreen(loc, tree);
594         return loc;
595       }
596 
597       /**
598        * Returns the size of this object in the form of a Dimension object.
599        *
600        * @return the size of the object
601        */
getSize()602       public Dimension getSize()
603       {
604         Rectangle b = getBounds();
605         return b.getSize();
606       }
607 
608       /**
609        * Returns true if the current child of this object is selected.
610        *
611        * @param i - the child of the current node
612        * @return true if the child is selected.
613        */
isAccessibleChildSelected(int i)614       public boolean isAccessibleChildSelected(int i)
615       {
616         Object child = mod.getChild(tp.getLastPathComponent(), i);
617         if (child != null)
618           return tree.isPathSelected(tp.pathByAddingChild(child));
619         return false;
620       }
621 
622       /**
623        * Determines if the object is enabled.
624        *
625        * @return true if the tree is enabled
626        */
isEnabled()627       public boolean isEnabled()
628       {
629         return tree.isEnabled();
630       }
631 
632       /**
633        * Returns whether this object can accept focus or not.
634        *
635        * @return true, it is always focus traversable
636        */
isFocusTraversable()637       public boolean isFocusTraversable()
638       {
639         return true;
640       }
641 
642       /**
643        * Determines if the object is showing.
644        *
645        * @return true if the object is visible and the
646        * parent is visible.
647        */
isShowing()648       public boolean isShowing()
649       {
650         return isVisible() && tree.isShowing();
651       }
652 
653       /**
654        * Determines if the object is visible.
655        *
656        * @return true if the object is visible.
657        */
isVisible()658       public boolean isVisible()
659       {
660         return tree.isVisible(tp);
661       }
662 
663       /**
664        * Removes the specified selected item in the object from the
665        * object's selection.
666        *
667        * @param i - the specified item to remove
668        */
removeAccessibleSelection(int i)669       public void removeAccessibleSelection(int i)
670       {
671         if (mod != null)
672           {
673             Object child = mod.getChild(tp.getLastPathComponent(), i);
674             if (child != null)
675               {
676                 if (!states.contains(AccessibleState.MULTISELECTABLE))
677                   clearAccessibleSelection();
678                 if (selectionList.contains(child))
679                   {
680                     selectionList.remove(child);
681                     tree.removeSelectionPath(tp.pathByAddingChild(child));
682                   }
683               }
684           }
685       }
686 
687       /**
688        * Removes the specified focus listener so it no longer receives focus
689        * events from this component.
690        *
691        * @param l - the focus listener to remove
692        */
removeFocusListener(FocusListener l)693       public void removeFocusListener(FocusListener l)
694       {
695         tree.removeFocusListener(l);
696       }
697 
698       /**
699        * Remove a PropertyChangeListener from the listener list.
700        *
701        * @param l - the property change listener to remove.
702        */
removePropertyChangeListener(PropertyChangeListener l)703       public void removePropertyChangeListener(PropertyChangeListener l)
704       {
705         // Nothing to do here.
706       }
707 
708       /**
709        * Requests focus for this object.
710        */
requestFocus()711       public void requestFocus()
712       {
713         tree.requestFocus();
714       }
715 
716       /**
717        * Causes every selected item in the object to be selected if the object
718        * supports multiple selections.
719        */
selectAllAccessibleSelection()720       public void selectAllAccessibleSelection()
721       {
722         Object parent = tp.getLastPathComponent();
723         if (mod != null)
724           {
725             for (int i = 0; i < mod.getChildCount(parent); i++)
726               {
727                 Object child = mod.getChild(parent, i);
728                 if (child != null)
729                   {
730                     if (!states.contains(AccessibleState.MULTISELECTABLE))
731                       clearAccessibleSelection();
732                     if (selectionList.contains(child))
733                       {
734                         selectionList.add(child);
735                         tree.addSelectionPath(tp.pathByAddingChild(child));
736                       }
737                   }
738               }
739           }
740       }
741 
742       /**
743        * Set the accessible description of this object.
744        *
745        * @param s - the string to set the accessible description to.
746        */
setAccessibleDescription(String s)747       public void setAccessibleDescription(String s)
748       {
749         super.setAccessibleDescription(s);
750       }
751 
752       /**
753        * Set the localized accessible name of this object.
754        *
755        * @param s - the string to set the accessible name to.
756        */
setAccessibleName(String s)757       public void setAccessibleName(String s)
758       {
759         super.setAccessibleName(s);
760       }
761 
762       /**
763        * Set the background color of this object.
764        *
765        * @param c - the color to set the background to.
766        */
setBackground(Color c)767       public void setBackground(Color c)
768       {
769         // Nothing to do here.
770       }
771 
772       /**
773        * Sets the bounds of this object in the form of a Rectangle object.
774        *
775        * @param r - the bounds to set the object o
776        */
setBounds(Rectangle r)777       public void setBounds(Rectangle r)
778       {
779         // Nothing to do here.
780       }
781 
782       /**
783        * Sets the Cursor of this object.
784        *
785        * @param c - the new cursor
786        */
setCursor(Cursor c)787       public void setCursor(Cursor c)
788       {
789         cursor = c;
790       }
791 
792       /**
793        * Sets the enabled state of the object.
794        *
795        * @param b - boolean to enable or disable object
796        */
setEnabled(boolean b)797       public void setEnabled(boolean b)
798       {
799          // Nothing to do here.
800       }
801 
802       /**
803        * Sets the Font of this object.
804        *
805        * @param f - the new font.
806        */
setFont(Font f)807       public void setFont(Font f)
808       {
809          // Nothing to do here.
810       }
811 
812       /**
813        * Sets the foreground color of this object.
814        *
815        * @param c - the new foreground color.
816        */
setForeground(Color c)817       public void setForeground(Color c)
818       {
819         // Nothing to do here.
820       }
821 
822       /**
823        * Sets the location of the object relative to the parent.
824        *
825        * @param p - the new location for the object.
826        */
setLocation(Point p)827       public void setLocation(Point p)
828       {
829         // Nothing to do here.
830       }
831 
832       /**
833        * Resizes this object so that it has width and height.
834        *
835        * @param d - the new size for the object.
836        */
setSize(Dimension d)837       public void setSize(Dimension d)
838       {
839         // Nothing to do here.
840       }
841 
842       /**
843        * Sets the visible state of the object.
844        *
845        * @param b - sets the objects visibility.
846        */
setVisible(boolean b)847       public void setVisible(boolean b)
848       {
849         // Nothing to do here.
850       }
851     }
852 
853     /**
854      * Constructor
855      */
AccessibleJTree()856     public AccessibleJTree()
857     {
858       // Nothing to do here.
859     }
860 
861     /**
862      * Adds the specified selected item in the object to the object's selection.
863      *
864      * @param i - the row to add to the tree's selection
865      */
addAccessibleSelection(int i)866     public void addAccessibleSelection(int i)
867     {
868       addSelectionInterval(i, i);
869     }
870 
871     /**
872      * Clears the selection in the object, so that nothing in the object is selected.
873      */
clearAccessibleSelection()874     public void clearAccessibleSelection()
875     {
876       clearSelection();
877     }
878 
879     /**
880      * Fire a visible data property change notification.
881      */
fireVisibleDataPropertyChange()882     public void fireVisibleDataPropertyChange()
883     {
884       treeDidChange();
885     }
886 
887     /**
888      * Returns the Accessible child, if one exists, contained at the local
889      * coordinate Point.
890      *
891      * @param p - the point of the accessible to get.
892      * @return the accessible at point p.
893      */
getAccessibleAt(Point p)894     public Accessible getAccessibleAt(Point p)
895     {
896       TreePath tp = getClosestPathForLocation(p.x, p.y);
897       if (tp != null)
898         return new AccessibleJTreeNode(JTree.this, tp, null);
899       return null;
900     }
901 
902     /**
903      * Return the nth Accessible child of the object.
904      *
905      * @param i - the accessible child to get
906      * @return the i-th child
907      */
getAccessibleChild(int i)908     public Accessible getAccessibleChild(int i)
909     {
910       return null;
911     }
912 
913     /**
914      * Returns the number of top-level children nodes of this JTree.
915      *
916      * @return the number of top-level children
917      */
getAccessibleChildrenCount()918     public int getAccessibleChildrenCount()
919     {
920       TreeModel model = getModel();
921       if (model != null)
922         return model.getChildCount(model.getRoot());
923       return 0;
924     }
925 
926     /**
927      * Get the index of this object in its accessible parent.
928      *
929      * @return the index of this object.
930      */
getAccessibleIndexInParent()931     public int getAccessibleIndexInParent()
932     {
933       return 0;
934     }
935 
936     /**
937      * Get the role of this object.
938      *
939      * @return the role of this object
940      */
getAccessibleRole()941     public AccessibleRole getAccessibleRole()
942     {
943       return AccessibleRole.TREE;
944     }
945 
946     /**
947      * Get the AccessibleSelection associated with this object.
948      *
949      * @return the accessible selection of the tree
950      */
getAccessibleSelection()951     public AccessibleSelection getAccessibleSelection()
952     {
953       TreeModel mod = getModel();
954       if (mod != null)
955         return (new AccessibleJTreeNode(JTree.this,
956                   new TreePath(mod.getRoot()), null)).getAccessibleSelection();
957       return null;
958     }
959 
960     /**
961      * Returns an Accessible representing the specified selected item in the object.
962      *
963      * @return the i-th accessible in the selection
964      */
getAccessibleSelection(int i)965     public Accessible getAccessibleSelection(int i)
966     {
967       TreeModel mod = getModel();
968       if (mod != null)
969         return (new AccessibleJTreeNode(JTree.this,
970                   new TreePath(mod.getRoot()), null)).getAccessibleSelection(i);
971       return null;
972     }
973 
974     /**
975      * Returns the number of items currently selected.
976      *
977      * @return the number of selected accessibles.
978      */
getAccessibleSelectionCount()979     public int getAccessibleSelectionCount()
980     {
981       return getSelectionCount();
982     }
983 
984     /**
985      * Returns true if the current child of this object is selected.
986      *
987      * @param i - the child of this object
988      * @return true if the i-th child is selected.
989      */
isAccessibleChildSelected(int i)990     public boolean isAccessibleChildSelected(int i)
991     {
992       // Nothing to do here.
993       return false;
994     }
995 
996     /**
997      * Removes the specified selected item in the object from the object's
998      * selection.
999      *
1000      * @param i - the i-th selected item to remove
1001      */
removeAccessibleSelection(int i)1002     public void removeAccessibleSelection(int i)
1003     {
1004       removeSelectionInterval(i, i);
1005     }
1006 
1007     /**
1008      * Causes every selected item in the object to be selected if the object
1009      * supports multiple selections.
1010      */
selectAllAccessibleSelection()1011     public void selectAllAccessibleSelection()
1012     {
1013       if (getSelectionModel().getSelectionMode() !=
1014         TreeSelectionModel.SINGLE_TREE_SELECTION)
1015       addSelectionInterval(0, getVisibleRowCount());
1016     }
1017 
1018     /**
1019      * Tree Collapsed notification
1020      *
1021      * @param e - the event
1022      */
treeCollapsed(TreeExpansionEvent e)1023     public void treeCollapsed(TreeExpansionEvent e)
1024     {
1025       fireTreeCollapsed(e.getPath());
1026     }
1027 
1028     /**
1029      * Tree Model Expansion notification.
1030      *
1031      * @param e - the event
1032      */
treeExpanded(TreeExpansionEvent e)1033     public void treeExpanded(TreeExpansionEvent e)
1034     {
1035       fireTreeExpanded(e.getPath());
1036     }
1037 
1038     /**
1039      * Tree Model Node change notification.
1040      *
1041      * @param e - the event
1042      */
treeNodesChanged(TreeModelEvent e)1043     public void treeNodesChanged(TreeModelEvent e)
1044     {
1045       // Nothing to do here.
1046     }
1047 
1048     /**
1049      * Tree Model Node change notification.
1050      *
1051      * @param e - the event
1052      */
treeNodesInserted(TreeModelEvent e)1053     public void treeNodesInserted(TreeModelEvent e)
1054     {
1055       // Nothing to do here.
1056     }
1057 
1058     /**
1059      * Tree Model Node change notification.
1060      *
1061      * @param e - the event
1062      */
treeNodesRemoved(TreeModelEvent e)1063     public void treeNodesRemoved(TreeModelEvent e)
1064     {
1065       // Nothing to do here.
1066     }
1067 
1068     /**
1069      * Tree Model structure change change notification.
1070      *
1071      * @param e - the event
1072      */
treeStructureChanged(TreeModelEvent e)1073     public void treeStructureChanged(TreeModelEvent e)
1074     {
1075       // Nothing to do here.
1076     }
1077 
1078     /**
1079      * Tree Selection Listener value change method.
1080      *
1081      * @param e - the event
1082      */
valueChanged(TreeSelectionEvent e)1083     public void valueChanged(TreeSelectionEvent e)
1084     {
1085       fireValueChanged(e);
1086     }
1087   }
1088 
1089   public static class DynamicUtilTreeNode extends DefaultMutableTreeNode
1090   {
1091     protected Object childValue;
1092 
1093     protected boolean loadedChildren;
1094 
1095     /**
1096      * Currently not set or used by this class. It might be set and used in
1097      * later versions of this class.
1098      */
1099     protected boolean hasChildren;
1100 
DynamicUtilTreeNode(Object value, Object children)1101     public DynamicUtilTreeNode(Object value, Object children)
1102     {
1103       super(value);
1104       childValue = children;
1105       loadedChildren = false;
1106     }
1107 
getChildCount()1108     public int getChildCount()
1109     {
1110       loadChildren();
1111       return super.getChildCount();
1112     }
1113 
loadChildren()1114     protected void loadChildren()
1115     {
1116       if (!loadedChildren)
1117         {
1118           createChildren(this, childValue);
1119           loadedChildren = true;
1120         }
1121     }
1122 
children()1123     public Enumeration children()
1124     {
1125       loadChildren();
1126       return super.children();
1127     }
1128 
1129     /**
1130      * Returns the child node at position <code>pos</code>. Subclassed
1131      * here to load the children if necessary.
1132      *
1133      * @param pos the position of the child node to fetch
1134      *
1135      * @return the childnode at the specified position
1136      */
getChildAt(int pos)1137     public TreeNode getChildAt(int pos)
1138     {
1139       loadChildren();
1140       return super.getChildAt(pos);
1141     }
1142 
isLeaf()1143     public boolean isLeaf()
1144     {
1145       return childValue == null || !(childValue instanceof Hashtable
1146           || childValue instanceof Vector
1147           || childValue.getClass().isArray());
1148     }
1149 
createChildren(DefaultMutableTreeNode parent, Object children)1150     public static void createChildren(DefaultMutableTreeNode parent,
1151                                       Object children)
1152     {
1153       if (children instanceof Hashtable)
1154         {
1155           Hashtable tab = (Hashtable) children;
1156           Enumeration e = tab.keys();
1157           while (e.hasMoreElements())
1158             {
1159               Object key = e.nextElement();
1160               Object val = tab.get(key);
1161               parent.add(new DynamicUtilTreeNode(key, val));
1162             }
1163         }
1164       else if (children instanceof Vector)
1165         {
1166           Iterator i = ((Vector) children).iterator();
1167           while (i.hasNext())
1168             {
1169               Object n = i.next();
1170               parent.add(new DynamicUtilTreeNode(n, n));
1171             }
1172         }
1173       else if (children != null && children.getClass().isArray())
1174         {
1175           Object[] arr = (Object[]) children;
1176           for (int i = 0; i < arr.length; ++i)
1177             parent.add(new DynamicUtilTreeNode(arr[i], arr[i]));
1178         }
1179     }
1180   }
1181 
1182   /**
1183    * Listens to the model of the JTree and updates the property
1184    * <code>expandedState</code> if nodes are removed or changed.
1185    */
1186   protected class TreeModelHandler implements TreeModelListener
1187   {
1188 
1189     /**
1190      * Creates a new instance of TreeModelHandler.
1191      */
TreeModelHandler()1192     protected TreeModelHandler()
1193     {
1194       // Nothing to do here.
1195     }
1196 
1197     /**
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.
1202      *
1203      * This method is called after the actual change occured.
1204      *
1205      * @param ev the TreeModelEvent describing the change
1206      */
treeNodesChanged(TreeModelEvent ev)1207     public void treeNodesChanged(TreeModelEvent ev)
1208     {
1209       // Nothing to do here.
1210     }
1211 
1212     /**
1213      * Notifies when a node is inserted into the tree.
1214      *
1215      * This method is called after the actual change occured.
1216      *
1217      * @param ev the TreeModelEvent describing the change
1218      */
treeNodesInserted(TreeModelEvent ev)1219     public void treeNodesInserted(TreeModelEvent ev)
1220     {
1221       // nothing to do here
1222     }
1223 
1224     /**
1225      * Notifies when a node is removed from the tree.
1226      *
1227      * This method is called after the actual change occured.
1228      *
1229      * @param ev the TreeModelEvent describing the change
1230          */
treeNodesRemoved(TreeModelEvent ev)1231     public void treeNodesRemoved(TreeModelEvent ev)
1232     {
1233       if (ev != null)
1234         {
1235           TreePath parent = ev.getTreePath();
1236           Object[] children = ev.getChildren();
1237           TreeSelectionModel sm = getSelectionModel();
1238           if (children != null)
1239             {
1240               TreePath path;
1241               Vector toRemove = new Vector();
1242               // Collect items that we must remove.
1243               for (int i = children.length - 1; i >= 0; i--)
1244                 {
1245                   path = parent.pathByAddingChild(children[i]);
1246                   if (nodeStates.containsKey(path))
1247                     toRemove.add(path);
1248                   // Clear selection while we are at it.
1249                   if (sm != null)
1250                     removeDescendantSelectedPaths(path, true);
1251                 }
1252               if (toRemove.size() > 0)
1253                 removeDescendantToggledPaths(toRemove.elements());
1254               TreeModel model = getModel();
1255               if (model == null || model.isLeaf(parent.getLastPathComponent()))
1256                 nodeStates.remove(parent);
1257             }
1258         }
1259     }
1260 
1261     /**
1262      * Notifies when the structure of the tree is changed.
1263      *
1264      * This method is called after the actual change occured.
1265      *
1266      * @param ev the TreeModelEvent describing the change
1267      */
treeStructureChanged(TreeModelEvent ev)1268     public void treeStructureChanged(TreeModelEvent ev)
1269     {
1270       if (ev != null)
1271         {
1272           TreePath parent = ev.getTreePath();
1273           if (parent != null)
1274             {
1275               if (parent.getPathCount() == 1)
1276                 {
1277                   // We have a new root, clear everything.
1278                   clearToggledPaths();
1279                   Object root = treeModel.getRoot();
1280                   if (root != null && treeModel.isLeaf(root))
1281                     nodeStates.put(parent, Boolean.TRUE);
1282                 }
1283               else if (nodeStates.containsKey(parent))
1284                 {
1285                   Vector toRemove = new Vector();
1286                   boolean expanded = isExpanded(parent);
1287                   toRemove.add(parent);
1288                   removeDescendantToggledPaths(toRemove.elements());
1289                   if (expanded)
1290                     {
1291                       TreeModel model = getModel();
1292                       if (model != null
1293                           || model.isLeaf(parent.getLastPathComponent()))
1294                         collapsePath(parent);
1295                       else
1296                         nodeStates.put(parent, Boolean.TRUE);
1297                     }
1298                 }
1299               removeDescendantSelectedPaths(parent, false);
1300             }
1301         }
1302     }
1303   }
1304 
1305   /**
1306    * This redirects TreeSelectionEvents and rewrites the source of it to be
1307    * this JTree. This is typically done when the tree model generates an
1308    * event, but the JTree object associated with that model should be listed
1309    * as the actual source of the event.
1310    */
1311   protected class TreeSelectionRedirector implements TreeSelectionListener,
1312                                                      Serializable
1313   {
1314     /** The serial version UID. */
1315     private static final long serialVersionUID = -3505069663646241664L;
1316 
1317     /**
1318      * Creates a new instance of TreeSelectionRedirector
1319      */
TreeSelectionRedirector()1320     protected TreeSelectionRedirector()
1321     {
1322       // Nothing to do here.
1323     }
1324 
1325     /**
1326      * Notifies when the tree selection changes.
1327      *
1328      * @param ev the TreeSelectionEvent that describes the change
1329      */
valueChanged(TreeSelectionEvent ev)1330     public void valueChanged(TreeSelectionEvent ev)
1331     {
1332       TreeSelectionEvent rewritten =
1333         (TreeSelectionEvent) ev.cloneWithSource(JTree.this);
1334       fireValueChanged(rewritten);
1335     }
1336   }
1337 
1338   /**
1339    * A TreeModel that does not allow anything to be selected.
1340    */
1341   protected static class EmptySelectionModel extends DefaultTreeSelectionModel
1342   {
1343     /** The serial version UID. */
1344     private static final long serialVersionUID = -5815023306225701477L;
1345 
1346     /**
1347      * The shared instance of this model.
1348      */
1349     protected static final EmptySelectionModel sharedInstance =
1350       new EmptySelectionModel();
1351 
1352     /**
1353      * Creates a new instance of EmptySelectionModel.
1354      */
EmptySelectionModel()1355     protected EmptySelectionModel()
1356     {
1357       // Nothing to do here.
1358     }
1359 
1360     /**
1361      * Returns the shared instance of EmptySelectionModel.
1362      *
1363      * @return the shared instance of EmptySelectionModel
1364      */
sharedInstance()1365     public static EmptySelectionModel sharedInstance()
1366     {
1367       return sharedInstance;
1368     }
1369 
1370     /**
1371      * This catches attempts to set a selection and sets nothing instead.
1372      *
1373      * @param paths not used here
1374      */
setSelectionPaths(TreePath[] paths)1375     public void setSelectionPaths(TreePath[] paths)
1376     {
1377       // We don't allow selections in this class.
1378     }
1379 
1380     /**
1381      * This catches attempts to add something to the selection.
1382      *
1383      * @param paths not used here
1384      */
addSelectionPaths(TreePath[] paths)1385     public void addSelectionPaths(TreePath[] paths)
1386     {
1387       // We don't allow selections in this class.
1388     }
1389 
1390     /**
1391      * This catches attempts to remove something from the selection.
1392      *
1393      * @param paths not used here
1394      */
removeSelectionPaths(TreePath[] paths)1395     public void removeSelectionPaths(TreePath[] paths)
1396     {
1397       // We don't allow selections in this class.
1398     }
1399   }
1400 
1401   private static final long serialVersionUID = 7559816092864483649L;
1402 
1403   public static final String CELL_EDITOR_PROPERTY = "cellEditor";
1404 
1405   public static final String CELL_RENDERER_PROPERTY = "cellRenderer";
1406 
1407   public static final String EDITABLE_PROPERTY = "editable";
1408 
1409   public static final String INVOKES_STOP_CELL_EDITING_PROPERTY =
1410     "invokesStopCellEditing";
1411 
1412   public static final String LARGE_MODEL_PROPERTY = "largeModel";
1413 
1414   public static final String ROOT_VISIBLE_PROPERTY = "rootVisible";
1415 
1416   public static final String ROW_HEIGHT_PROPERTY = "rowHeight";
1417 
1418   public static final String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
1419 
1420   public static final String SELECTION_MODEL_PROPERTY = "selectionModel";
1421 
1422   public static final String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
1423 
1424   public static final String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
1425 
1426   public static final String TREE_MODEL_PROPERTY = "model";
1427 
1428   public static final String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
1429 
1430   /** @since 1.3 */
1431   public static final String ANCHOR_SELECTION_PATH_PROPERTY =
1432     "anchorSelectionPath";
1433 
1434         /** @since 1.3 */
1435   public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
1436 
1437   /** @since 1.3 */
1438   public static final String EXPANDS_SELECTED_PATHS_PROPERTY =
1439     "expandsSelectedPaths";
1440 
1441   private static final Object EXPANDED = Boolean.TRUE;
1442 
1443   private static final Object COLLAPSED = Boolean.FALSE;
1444 
1445   private boolean dragEnabled;
1446 
1447   private boolean expandsSelectedPaths;
1448 
1449   private TreePath anchorSelectionPath;
1450 
1451   /**
1452    * This contains the state of all nodes in the tree. Al/ entries map the
1453    * TreePath of a note to to its state. Valid states are EXPANDED and
1454    * COLLAPSED. Nodes not in this Hashtable are assumed state COLLAPSED.
1455    *
1456    * This is package private to avoid accessor methods.
1457    */
1458   Hashtable nodeStates = new Hashtable();
1459 
1460   protected transient TreeCellEditor cellEditor;
1461 
1462   protected transient TreeCellRenderer cellRenderer;
1463 
1464   protected boolean editable;
1465 
1466   protected boolean invokesStopCellEditing;
1467 
1468   protected boolean largeModel;
1469 
1470   protected boolean rootVisible;
1471 
1472   protected int rowHeight;
1473 
1474   protected boolean scrollsOnExpand;
1475 
1476   protected transient TreeSelectionModel selectionModel;
1477 
1478   protected boolean showsRootHandles;
1479 
1480   protected int toggleClickCount;
1481 
1482   protected transient TreeModel treeModel;
1483 
1484   protected int visibleRowCount;
1485 
1486   /**
1487    * Handles TreeModelEvents to update the expandedState.
1488    */
1489   protected transient TreeModelListener treeModelListener;
1490 
1491   /**
1492    * Redirects TreeSelectionEvents so that the source is this JTree.
1493    */
1494   protected TreeSelectionRedirector selectionRedirector =
1495     new TreeSelectionRedirector();
1496 
1497   /**
1498    * Indicates if the rowHeight property has been set by a client
1499    * program or by the UI.
1500    *
1501    * @see #setUIProperty(String, Object)
1502    * @see LookAndFeel#installProperty(JComponent, String, Object)
1503    */
1504   private boolean clientRowHeightSet = false;
1505 
1506   /**
1507    * Indicates if the scrollsOnExpand property has been set by a client
1508    * program or by the UI.
1509    *
1510    * @see #setUIProperty(String, Object)
1511    * @see LookAndFeel#installProperty(JComponent, String, Object)
1512    */
1513   private boolean clientScrollsOnExpandSet = false;
1514 
1515   /**
1516    * Indicates if the showsRootHandles property has been set by a client
1517    * program or by the UI.
1518    *
1519    * @see #setUIProperty(String, Object)
1520    * @see LookAndFeel#installProperty(JComponent, String, Object)
1521    */
1522   private boolean clientShowsRootHandlesSet = false;
1523 
1524   /**
1525    * Creates a new <code>JTree</code> object.
1526    */
JTree()1527   public JTree()
1528   {
1529     this(getDefaultTreeModel());
1530   }
1531 
1532   /**
1533    * Creates a new <code>JTree</code> object.
1534    *
1535    * @param value the initial nodes in the tree
1536    */
JTree(Hashtable<?, ?> value)1537   public JTree(Hashtable<?, ?> value)
1538   {
1539     this(createTreeModel(value));
1540   }
1541 
1542   /**
1543    * Creates a new <code>JTree</code> object.
1544    *
1545    * @param value the initial nodes in the tree
1546    */
JTree(Object[] value)1547   public JTree(Object[] value)
1548   {
1549     this(createTreeModel(value));
1550   }
1551 
1552   /**
1553    * Creates a new <code>JTree</code> object.
1554    *
1555    * @param model the model to use
1556    */
JTree(TreeModel model)1557   public JTree(TreeModel model)
1558   {
1559     setRootVisible(true);
1560     setSelectionModel( new DefaultTreeSelectionModel() );
1561 
1562     // The root node appears expanded by default.
1563     nodeStates = new Hashtable();
1564 
1565     // The cell renderer gets set by the UI.
1566     cellRenderer = null;
1567 
1568     // Install the UI before installing the model. This way we avoid double
1569     // initialization of lots of UI and model stuff inside the UI and related
1570     // classes. The necessary UI updates are performed via property change
1571     // events to the UI.
1572     updateUI();
1573     setModel(model);
1574   }
1575 
1576   /**
1577    * Creates a new <code>JTree</code> object.
1578    *
1579    * @param root the root node
1580    */
JTree(TreeNode root)1581   public JTree(TreeNode root)
1582   {
1583     this(root, false);
1584   }
1585 
1586   /**
1587    * Creates a new <code>JTree</code> object.
1588    *
1589    * @param root the root node
1590    * @param asksAllowChildren if false, all nodes without children are leaf
1591    *        nodes. If true, only nodes that do not allow children are leaf
1592    *        nodes.
1593    */
JTree(TreeNode root, boolean asksAllowChildren)1594   public JTree(TreeNode root, boolean asksAllowChildren)
1595   {
1596     this(new DefaultTreeModel(root, asksAllowChildren));
1597   }
1598 
1599   /**
1600    * Creates a new <code>JTree</code> object.
1601    *
1602    * @param value the initial nodes in the tree
1603    */
JTree(Vector<?> value)1604   public JTree(Vector<?> value)
1605   {
1606     this(createTreeModel(value));
1607   }
1608 
getRowForPath(TreePath path)1609   public int getRowForPath(TreePath path)
1610   {
1611     TreeUI ui = getUI();
1612 
1613     if (ui != null)
1614       return ui.getRowForPath(this, path);
1615 
1616     return -1;
1617   }
1618 
getPathForRow(int row)1619   public TreePath getPathForRow(int row)
1620   {
1621     TreeUI ui = getUI();
1622     return ui != null ? ui.getPathForRow(this, row) : null;
1623   }
1624 
1625   /**
1626    * Get the pathes that are displayes between the two given rows.
1627    *
1628    * @param index0 the starting row, inclusive
1629    * @param index1 the ending row, inclusive
1630    *
1631    * @return the array of the tree pathes
1632    */
getPathBetweenRows(int index0, int index1)1633   protected TreePath[] getPathBetweenRows(int index0, int index1)
1634   {
1635     TreeUI ui = getUI();
1636 
1637     if (ui == null)
1638       return null;
1639 
1640     int minIndex = Math.min(index0, index1);
1641     int maxIndex = Math.max(index0, index1);
1642     TreePath[] paths = new TreePath[maxIndex - minIndex + 1];
1643 
1644     for (int i = minIndex; i <= maxIndex; ++i)
1645       paths[i - minIndex] = ui.getPathForRow(this, i);
1646 
1647     return paths;
1648   }
1649 
1650   /**
1651    * Creates a new <code>TreeModel</code> object.
1652    *
1653    * @param value the values stored in the model
1654    */
createTreeModel(Object value)1655   protected static TreeModel createTreeModel(Object value)
1656   {
1657     return new DefaultTreeModel(new DynamicUtilTreeNode(value, value));
1658   }
1659 
1660   /**
1661    * Return the UI associated with this <code>JTree</code> object.
1662    *
1663    * @return the associated <code>TreeUI</code> object
1664    */
getUI()1665   public TreeUI getUI()
1666   {
1667     return (TreeUI) ui;
1668   }
1669 
1670   /**
1671    * Sets the UI associated with this <code>JTree</code> object.
1672    *
1673    * @param ui the <code>TreeUI</code> to associate
1674    */
setUI(TreeUI ui)1675   public void setUI(TreeUI ui)
1676   {
1677     super.setUI(ui);
1678   }
1679 
1680   /**
1681    * This method resets the UI used to the Look and Feel defaults..
1682    */
updateUI()1683   public void updateUI()
1684   {
1685     setUI((TreeUI) UIManager.getUI(this));
1686   }
1687 
1688   /**
1689    * This method returns the String ID of the UI class of Separator.
1690    *
1691    * @return The UI class' String ID.
1692    */
getUIClassID()1693   public String getUIClassID()
1694   {
1695     return "TreeUI";
1696   }
1697 
1698   /**
1699    * Gets the AccessibleContext associated with this
1700    * <code>JTree</code>.
1701    *
1702    * @return the associated context
1703    */
getAccessibleContext()1704   public AccessibleContext getAccessibleContext()
1705   {
1706     return new AccessibleJTree();
1707   }
1708 
1709   /**
1710    * Returns the preferred viewport size.
1711    *
1712    * @return the preferred size
1713    */
getPreferredScrollableViewportSize()1714   public Dimension getPreferredScrollableViewportSize()
1715   {
1716     return getPreferredSize();
1717   }
1718 
1719   /**
1720    * Return the preferred scrolling amount (in pixels) for the given scrolling
1721    * direction and orientation. This method handles a partially exposed row by
1722    * returning the distance required to completely expose the item.
1723    *
1724    * @param visibleRect the currently visible part of the component.
1725    * @param orientation the scrolling orientation
1726    * @param direction the scrolling direction (negative - up, positive -down).
1727    *          The values greater than one means that more mouse wheel or similar
1728    *          events were generated, and hence it is better to scroll the longer
1729    *          distance.
1730    * @author Audrius Meskauskas (audriusa@bioinformatics.org)
1731    */
getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)1732   public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
1733                                         int direction)
1734   {
1735     int delta = 0;
1736 
1737     // Round so that the top would start from the row boundary
1738     if (orientation == SwingConstants.VERTICAL)
1739       {
1740         int row = getClosestRowForLocation(0, visibleRect.y);
1741         if (row != -1)
1742           {
1743             Rectangle b = getRowBounds(row);
1744             if (b.y != visibleRect.y)
1745               {
1746                 if (direction < 0)
1747                   delta = Math.max(0, visibleRect.y - b.y);
1748                 else
1749                   delta = b.y + b.height - visibleRect.y;
1750               }
1751             else
1752               {
1753                 if (direction < 0)
1754                   {
1755                     if (row != 0)
1756                       {
1757                         b = getRowBounds(row - 1);
1758                         delta = b.height;
1759                       }
1760                   }
1761                 else
1762                   delta = b.height;
1763               }
1764           }
1765       }
1766     else
1767       // The RI always  returns 4 for HORIZONTAL scrolling.
1768       delta = 4;
1769     return delta;
1770   }
1771 
getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)1772   public int getScrollableBlockIncrement(Rectangle visibleRect,
1773                                          int orientation, int direction)
1774   {
1775     int block;
1776     if (orientation == SwingConstants.VERTICAL)
1777       block = visibleRect.height;
1778     else
1779       block = visibleRect.width;
1780     return block;
1781   }
1782 
getScrollableTracksViewportHeight()1783   public boolean getScrollableTracksViewportHeight()
1784   {
1785     if (getParent() instanceof JViewport)
1786       return ((JViewport) getParent()).getHeight() > getPreferredSize().height;
1787     return false;
1788   }
1789 
getScrollableTracksViewportWidth()1790   public boolean getScrollableTracksViewportWidth()
1791   {
1792     if (getParent() instanceof JViewport)
1793       return ((JViewport) getParent()).getWidth() > getPreferredSize().width;
1794     return false;
1795   }
1796 
1797   /**
1798    * Adds a <code>TreeExpansionListener</code> object to the tree.
1799    *
1800    * @param listener the listener to add
1801    */
addTreeExpansionListener(TreeExpansionListener listener)1802   public void addTreeExpansionListener(TreeExpansionListener listener)
1803   {
1804     listenerList.add(TreeExpansionListener.class, listener);
1805   }
1806 
1807   /**
1808    * Removes a <code>TreeExpansionListener</code> object from the tree.
1809    *
1810    * @param listener the listener to remove
1811    */
removeTreeExpansionListener(TreeExpansionListener listener)1812   public void removeTreeExpansionListener(TreeExpansionListener listener)
1813   {
1814     listenerList.remove(TreeExpansionListener.class, listener);
1815   }
1816 
1817   /**
1818    * Returns all added <code>TreeExpansionListener</code> objects.
1819    *
1820    * @return an array of listeners
1821    */
getTreeExpansionListeners()1822   public TreeExpansionListener[] getTreeExpansionListeners()
1823   {
1824     return (TreeExpansionListener[]) getListeners(TreeExpansionListener.class);
1825   }
1826 
1827   /**
1828    * Notifies all listeners that the tree was collapsed.
1829    *
1830    * @param path the path to the node that was collapsed
1831    */
fireTreeCollapsed(TreePath path)1832   public void fireTreeCollapsed(TreePath path)
1833   {
1834     TreeExpansionEvent event = new TreeExpansionEvent(this, path);
1835     TreeExpansionListener[] listeners = getTreeExpansionListeners();
1836 
1837     for (int index = 0; index < listeners.length; ++index)
1838       listeners[index].treeCollapsed(event);
1839   }
1840 
1841   /**
1842    * Notifies all listeners that the tree was expanded.
1843    *
1844    * @param path the path to the node that was expanded
1845    */
fireTreeExpanded(TreePath path)1846   public void fireTreeExpanded(TreePath path)
1847   {
1848     TreeExpansionEvent event = new TreeExpansionEvent(this, path);
1849     TreeExpansionListener[] listeners = getTreeExpansionListeners();
1850 
1851     for (int index = 0; index < listeners.length; ++index)
1852       listeners[index].treeExpanded(event);
1853   }
1854 
1855   /**
1856    * Adds a <code>TreeSelctionListener</code> object to the tree.
1857    *
1858    * @param listener the listener to add
1859    */
addTreeSelectionListener(TreeSelectionListener listener)1860   public void addTreeSelectionListener(TreeSelectionListener listener)
1861   {
1862     listenerList.add(TreeSelectionListener.class, listener);
1863   }
1864 
1865   /**
1866    * Removes a <code>TreeSelectionListener</code> object from the tree.
1867    *
1868    * @param listener the listener to remove
1869    */
removeTreeSelectionListener(TreeSelectionListener listener)1870   public void removeTreeSelectionListener(TreeSelectionListener listener)
1871   {
1872     listenerList.remove(TreeSelectionListener.class, listener);
1873   }
1874 
1875   /**
1876    * Returns all added <code>TreeSelectionListener</code> objects.
1877    *
1878    * @return an array of listeners
1879    */
getTreeSelectionListeners()1880   public TreeSelectionListener[] getTreeSelectionListeners()
1881   {
1882     return (TreeSelectionListener[])
1883     getListeners(TreeSelectionListener.class);
1884   }
1885 
1886   /**
1887    * Notifies all listeners when the selection of the tree changed.
1888    *
1889    * @param event the event to send
1890    */
fireValueChanged(TreeSelectionEvent event)1891   protected void fireValueChanged(TreeSelectionEvent event)
1892   {
1893     TreeSelectionListener[] listeners = getTreeSelectionListeners();
1894 
1895     for (int index = 0; index < listeners.length; ++index)
1896       listeners[index].valueChanged(event);
1897   }
1898 
1899   /**
1900    * Adds a <code>TreeWillExpandListener</code> object to the tree.
1901    *
1902    * @param listener the listener to add
1903    */
addTreeWillExpandListener(TreeWillExpandListener listener)1904   public void addTreeWillExpandListener(TreeWillExpandListener listener)
1905   {
1906     listenerList.add(TreeWillExpandListener.class, listener);
1907   }
1908 
1909   /**
1910    * Removes a <code>TreeWillExpandListener</code> object from the tree.
1911    *
1912    * @param listener the listener to remove
1913    */
removeTreeWillExpandListener(TreeWillExpandListener listener)1914   public void removeTreeWillExpandListener(TreeWillExpandListener listener)
1915   {
1916     listenerList.remove(TreeWillExpandListener.class, listener);
1917   }
1918 
1919   /**
1920    * Returns all added <code>TreeWillExpandListener</code> objects.
1921    *
1922    * @return an array of listeners
1923    */
getTreeWillExpandListeners()1924   public TreeWillExpandListener[] getTreeWillExpandListeners()
1925   {
1926     return (TreeWillExpandListener[])
1927     getListeners(TreeWillExpandListener.class);
1928   }
1929 
1930   /**
1931    * Notifies all listeners that the tree will collapse.
1932    *
1933    * @param path the path to the node that will collapse
1934    */
fireTreeWillCollapse(TreePath path)1935   public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException
1936   {
1937     TreeExpansionEvent event = new TreeExpansionEvent(this, path);
1938     TreeWillExpandListener[] listeners = getTreeWillExpandListeners();
1939 
1940     for (int index = 0; index < listeners.length; ++index)
1941       listeners[index].treeWillCollapse(event);
1942   }
1943 
1944   /**
1945    * Notifies all listeners that the tree will expand.
1946    *
1947    * @param path the path to the node that will expand
1948    */
fireTreeWillExpand(TreePath path)1949   public void fireTreeWillExpand(TreePath path) throws ExpandVetoException
1950   {
1951     TreeExpansionEvent event = new TreeExpansionEvent(this, path);
1952     TreeWillExpandListener[] listeners = getTreeWillExpandListeners();
1953 
1954     for (int index = 0; index < listeners.length; ++index)
1955       listeners[index].treeWillExpand(event);
1956   }
1957 
1958   /**
1959    * Returns the model of this <code>JTree</code> object.
1960    *
1961    * @return the associated <code>TreeModel</code>
1962    */
getModel()1963   public TreeModel getModel()
1964   {
1965     return treeModel;
1966   }
1967 
1968   /**
1969    * Sets the model to use in <code>JTree</code>.
1970    *
1971    * @param model the <code>TreeModel</code> to use
1972    */
setModel(TreeModel model)1973   public void setModel(TreeModel model)
1974   {
1975     if (treeModel == model)
1976       return;
1977 
1978     // Remove listeners from old model.
1979     if (treeModel != null && treeModelListener != null)
1980       treeModel.removeTreeModelListener(treeModelListener);
1981 
1982     // add treeModelListener to the new model
1983     if (treeModelListener == null)
1984       treeModelListener = createTreeModelListener();
1985     if (model != null) // as setModel(null) is allowed
1986       model.addTreeModelListener(treeModelListener);
1987 
1988     TreeModel oldValue = treeModel;
1989     treeModel = model;
1990     clearToggledPaths();
1991 
1992     if (treeModel != null)
1993       {
1994         if (treeModelListener == null)
1995           treeModelListener = createTreeModelListener();
1996         if (treeModelListener != null)
1997           treeModel.addTreeModelListener(treeModelListener);
1998         Object root = treeModel.getRoot();
1999         if (root != null && !treeModel.isLeaf(root))
2000           {
2001             nodeStates.put(new TreePath(root), Boolean.TRUE);
2002           }
2003       }
2004 
2005     firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model);
2006   }
2007 
2008   /**
2009    * Checks if this <code>JTree</code> object is editable.
2010    *
2011    * @return <code>true</code> if this tree object is editable,
2012    *         <code>false</code> otherwise
2013    */
isEditable()2014   public boolean isEditable()
2015   {
2016     return editable;
2017   }
2018 
2019   /**
2020    * Sets the <code>editable</code> property.
2021    *
2022    * @param flag <code>true</code> to make this tree object editable,
2023    *        <code>false</code> otherwise
2024    */
setEditable(boolean flag)2025   public void setEditable(boolean flag)
2026   {
2027     if (editable == flag)
2028       return;
2029 
2030     boolean oldValue = editable;
2031     editable = flag;
2032     firePropertyChange(EDITABLE_PROPERTY, oldValue, editable);
2033   }
2034 
2035   /**
2036    * Checks if the root element is visible.
2037    *
2038    * @return <code>true</code> if the root element is visible,
2039    *         <code>false</code> otherwise
2040    */
isRootVisible()2041   public boolean isRootVisible()
2042   {
2043     return rootVisible;
2044   }
2045 
setRootVisible(boolean flag)2046   public void setRootVisible(boolean flag)
2047   {
2048     if (rootVisible == flag)
2049       return;
2050 
2051     // If the root is currently selected, unselect it
2052     if (rootVisible && !flag)
2053       {
2054         TreeSelectionModel model = getSelectionModel();
2055         // The root is always shown in the first row
2056         TreePath rootPath = getPathForRow(0);
2057         model.removeSelectionPath(rootPath);
2058       }
2059 
2060     boolean oldValue = rootVisible;
2061     rootVisible = flag;
2062     firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag);
2063 
2064   }
2065 
getShowsRootHandles()2066   public boolean getShowsRootHandles()
2067   {
2068     return showsRootHandles;
2069   }
2070 
setShowsRootHandles(boolean flag)2071   public void setShowsRootHandles(boolean flag)
2072   {
2073     clientShowsRootHandlesSet = true;
2074 
2075     if (showsRootHandles == flag)
2076       return;
2077 
2078     boolean oldValue = showsRootHandles;
2079     showsRootHandles = flag;
2080     firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue, flag);
2081   }
2082 
getCellEditor()2083   public TreeCellEditor getCellEditor()
2084   {
2085     return cellEditor;
2086   }
2087 
setCellEditor(TreeCellEditor editor)2088   public void setCellEditor(TreeCellEditor editor)
2089   {
2090     if (cellEditor == editor)
2091       return;
2092 
2093     TreeCellEditor oldValue = cellEditor;
2094     cellEditor = editor;
2095     firePropertyChange(CELL_EDITOR_PROPERTY, oldValue, editor);
2096   }
2097 
getCellRenderer()2098   public TreeCellRenderer getCellRenderer()
2099   {
2100     return cellRenderer;
2101   }
2102 
setCellRenderer(TreeCellRenderer newRenderer)2103   public void setCellRenderer(TreeCellRenderer newRenderer)
2104   {
2105     if (cellRenderer == newRenderer)
2106       return;
2107 
2108     TreeCellRenderer oldValue = cellRenderer;
2109     cellRenderer = newRenderer;
2110     firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, newRenderer);
2111   }
2112 
getSelectionModel()2113   public TreeSelectionModel getSelectionModel()
2114   {
2115     return selectionModel;
2116   }
2117 
setSelectionModel(TreeSelectionModel model)2118   public void setSelectionModel(TreeSelectionModel model)
2119   {
2120     if (selectionModel == model)
2121       return;
2122 
2123     if( model == null )
2124       model = EmptySelectionModel.sharedInstance();
2125 
2126     if (selectionModel != null)
2127       selectionModel.removeTreeSelectionListener(selectionRedirector);
2128 
2129     TreeSelectionModel oldValue = selectionModel;
2130     selectionModel = model;
2131 
2132     selectionModel.addTreeSelectionListener(selectionRedirector);
2133 
2134     firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, model);
2135     revalidate();
2136     repaint();
2137   }
2138 
getVisibleRowCount()2139   public int getVisibleRowCount()
2140   {
2141     return visibleRowCount;
2142   }
2143 
setVisibleRowCount(int rows)2144   public void setVisibleRowCount(int rows)
2145   {
2146     if (visibleRowCount == rows)
2147       return;
2148 
2149     int oldValue = visibleRowCount;
2150     visibleRowCount = rows;
2151     firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldValue, rows);
2152   }
2153 
isLargeModel()2154   public boolean isLargeModel()
2155   {
2156     return largeModel;
2157   }
2158 
setLargeModel(boolean large)2159   public void setLargeModel(boolean large)
2160   {
2161     if (largeModel == large)
2162       return;
2163 
2164     boolean oldValue = largeModel;
2165     largeModel = large;
2166     firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, large);
2167   }
2168 
getRowHeight()2169   public int getRowHeight()
2170   {
2171     return rowHeight;
2172   }
2173 
setRowHeight(int height)2174   public void setRowHeight(int height)
2175   {
2176     clientRowHeightSet = true;
2177 
2178     if (rowHeight == height)
2179       return;
2180 
2181     int oldValue = rowHeight;
2182     rowHeight = height;
2183     firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, height);
2184   }
2185 
isFixedRowHeight()2186   public boolean isFixedRowHeight()
2187   {
2188     return rowHeight > 0;
2189   }
2190 
getInvokesStopCellEditing()2191   public boolean getInvokesStopCellEditing()
2192   {
2193     return invokesStopCellEditing;
2194   }
2195 
setInvokesStopCellEditing(boolean invoke)2196   public void setInvokesStopCellEditing(boolean invoke)
2197   {
2198     if (invokesStopCellEditing == invoke)
2199       return;
2200 
2201     boolean oldValue = invokesStopCellEditing;
2202     invokesStopCellEditing = invoke;
2203     firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY,
2204                        oldValue, invoke);
2205   }
2206 
2207   /**
2208    * @since 1.3
2209    */
getToggleClickCount()2210   public int getToggleClickCount()
2211   {
2212     return toggleClickCount;
2213   }
2214 
2215   /**
2216    * @since 1.3
2217    */
setToggleClickCount(int count)2218   public void setToggleClickCount(int count)
2219   {
2220     if (toggleClickCount == count)
2221       return;
2222 
2223     int oldValue = toggleClickCount;
2224     toggleClickCount = count;
2225     firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldValue, count);
2226   }
2227 
scrollPathToVisible(TreePath path)2228   public void scrollPathToVisible(TreePath path)
2229   {
2230     if (path == null)
2231       return;
2232     Rectangle rect = getPathBounds(path);
2233     scrollRectToVisible(rect);
2234   }
2235 
scrollRowToVisible(int row)2236   public void scrollRowToVisible(int row)
2237   {
2238     scrollPathToVisible(getPathForRow(row));
2239   }
2240 
getScrollsOnExpand()2241   public boolean getScrollsOnExpand()
2242   {
2243     return scrollsOnExpand;
2244   }
2245 
setScrollsOnExpand(boolean scroll)2246   public void setScrollsOnExpand(boolean scroll)
2247   {
2248     clientScrollsOnExpandSet = true;
2249     if (scrollsOnExpand == scroll)
2250       return;
2251 
2252     boolean oldValue = scrollsOnExpand;
2253     scrollsOnExpand = scroll;
2254     firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll);
2255   }
2256 
setSelectionPath(TreePath path)2257   public void setSelectionPath(TreePath path)
2258   {
2259     clearSelectionPathStates();
2260     selectionModel.setSelectionPath(path);
2261   }
2262 
setSelectionPaths(TreePath[] paths)2263   public void setSelectionPaths(TreePath[] paths)
2264   {
2265     clearSelectionPathStates();
2266     selectionModel.setSelectionPaths(paths);
2267   }
2268 
2269   /**
2270    * This method, and all calls to it, should be removed once the
2271    * DefaultTreeModel fires events properly.  Maintenance of the nodeStates
2272    * table should really be done in the TreeModelHandler.
2273    */
clearSelectionPathStates()2274   private void clearSelectionPathStates()
2275   {
2276     TreePath[] oldPaths = selectionModel.getSelectionPaths();
2277     if (oldPaths != null)
2278       for (int i = 0; i < oldPaths.length; i++)
2279         nodeStates.remove(oldPaths[i]);
2280   }
2281 
setSelectionRow(int row)2282   public void setSelectionRow(int row)
2283   {
2284     TreePath path = getPathForRow(row);
2285 
2286     if (path != null)
2287       setSelectionPath(path);
2288   }
2289 
setSelectionRows(int[] rows)2290   public void setSelectionRows(int[] rows)
2291   {
2292     // Make sure we have an UI so getPathForRow() does not return null.
2293     if (rows == null || getUI() == null)
2294       return;
2295 
2296     TreePath[] paths = new TreePath[rows.length];
2297 
2298     for (int i = rows.length - 1; i >= 0; --i)
2299       paths[i] = getPathForRow(rows[i]);
2300 
2301     setSelectionPaths(paths);
2302   }
2303 
setSelectionInterval(int index0, int index1)2304   public void setSelectionInterval(int index0, int index1)
2305   {
2306     TreePath[] paths = getPathBetweenRows(index0, index1);
2307 
2308     if (paths != null)
2309       setSelectionPaths(paths);
2310   }
2311 
addSelectionPath(TreePath path)2312   public void addSelectionPath(TreePath path)
2313   {
2314     selectionModel.addSelectionPath(path);
2315   }
2316 
addSelectionPaths(TreePath[] paths)2317   public void addSelectionPaths(TreePath[] paths)
2318   {
2319     selectionModel.addSelectionPaths(paths);
2320   }
2321 
addSelectionRow(int row)2322   public void addSelectionRow(int row)
2323   {
2324     TreePath path = getPathForRow(row);
2325 
2326     if (path != null)
2327       selectionModel.addSelectionPath(path);
2328   }
2329 
addSelectionRows(int[] rows)2330   public void addSelectionRows(int[] rows)
2331   {
2332     // Make sure we have an UI so getPathForRow() does not return null.
2333     if (rows == null || getUI() == null)
2334       return;
2335 
2336     TreePath[] paths = new TreePath[rows.length];
2337 
2338     for (int i = rows.length - 1; i >= 0; --i)
2339       paths[i] = getPathForRow(rows[i]);
2340 
2341     addSelectionPaths(paths);
2342   }
2343 
2344   /**
2345    * Select all rows between the two given indexes, inclusive. The method
2346    * will not select the inner leaves and braches of the currently collapsed
2347    * nodes in this interval.
2348    *
2349    * @param index0 the starting row, inclusive
2350    * @param index1 the ending row, inclusive
2351    */
addSelectionInterval(int index0, int index1)2352   public void addSelectionInterval(int index0, int index1)
2353   {
2354     TreePath[] paths = getPathBetweenRows(index0, index1);
2355 
2356     if (paths != null)
2357       addSelectionPaths(paths);
2358   }
2359 
removeSelectionPath(TreePath path)2360   public void removeSelectionPath(TreePath path)
2361   {
2362     clearSelectionPathStates();
2363     selectionModel.removeSelectionPath(path);
2364   }
2365 
removeSelectionPaths(TreePath[] paths)2366   public void removeSelectionPaths(TreePath[] paths)
2367   {
2368     clearSelectionPathStates();
2369     selectionModel.removeSelectionPaths(paths);
2370   }
2371 
removeSelectionRow(int row)2372   public void removeSelectionRow(int row)
2373   {
2374     TreePath path = getPathForRow(row);
2375 
2376     if (path != null)
2377       removeSelectionPath(path);
2378   }
2379 
removeSelectionRows(int[] rows)2380   public void removeSelectionRows(int[] rows)
2381   {
2382     if (rows == null || getUI() == null)
2383       return;
2384 
2385     TreePath[] paths = new TreePath[rows.length];
2386 
2387     for (int i = rows.length - 1; i >= 0; --i)
2388       paths[i] = getPathForRow(rows[i]);
2389 
2390     removeSelectionPaths(paths);
2391   }
2392 
removeSelectionInterval(int index0, int index1)2393   public void removeSelectionInterval(int index0, int index1)
2394   {
2395     TreePath[] paths = getPathBetweenRows(index0, index1);
2396 
2397     if (paths != null)
2398       removeSelectionPaths(paths);
2399   }
2400 
clearSelection()2401   public void clearSelection()
2402   {
2403     selectionModel.clearSelection();
2404     setLeadSelectionPath(null);
2405   }
2406 
getLeadSelectionPath()2407   public TreePath getLeadSelectionPath()
2408   {
2409     if (selectionModel == null)
2410       return null;
2411     else
2412       return selectionModel.getLeadSelectionPath();
2413   }
2414 
2415   /**
2416    * @since 1.3
2417    */
setLeadSelectionPath(TreePath path)2418   public void setLeadSelectionPath(TreePath path)
2419   {
2420     if (selectionModel != null)
2421       {
2422         TreePath oldValue = selectionModel.getLeadSelectionPath();
2423         if (path == oldValue || path != null && path.equals(oldValue))
2424           return;
2425 
2426         // Repaint the previous and current rows with the lead selection path.
2427         if (path != null)
2428           {
2429             repaint(getPathBounds(path));
2430             selectionModel.addSelectionPath(path);
2431           }
2432 
2433         if (oldValue != null)
2434           repaint(getPathBounds(oldValue));
2435 
2436         firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path);
2437       }
2438   }
2439 
2440   /**
2441    * @since 1.3
2442    */
getAnchorSelectionPath()2443   public TreePath getAnchorSelectionPath()
2444   {
2445     return anchorSelectionPath;
2446   }
2447 
2448   /**
2449    * @since 1.3
2450    */
setAnchorSelectionPath(TreePath path)2451   public void setAnchorSelectionPath(TreePath path)
2452   {
2453     if (anchorSelectionPath == path)
2454       return;
2455 
2456     TreePath oldValue = anchorSelectionPath;
2457     anchorSelectionPath = path;
2458     firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, path);
2459   }
2460 
getLeadSelectionRow()2461   public int getLeadSelectionRow()
2462   {
2463     return selectionModel.getLeadSelectionRow();
2464   }
2465 
getMaxSelectionRow()2466   public int getMaxSelectionRow()
2467   {
2468     return selectionModel.getMaxSelectionRow();
2469   }
2470 
getMinSelectionRow()2471   public int getMinSelectionRow()
2472   {
2473     return selectionModel.getMinSelectionRow();
2474   }
2475 
getSelectionCount()2476   public int getSelectionCount()
2477   {
2478     return selectionModel.getSelectionCount();
2479   }
2480 
getSelectionPath()2481   public TreePath getSelectionPath()
2482   {
2483     return selectionModel.getSelectionPath();
2484   }
2485 
getSelectionPaths()2486   public TreePath[] getSelectionPaths()
2487   {
2488     return selectionModel.getSelectionPaths();
2489   }
2490 
getSelectionRows()2491   public int[] getSelectionRows()
2492   {
2493     return selectionModel.getSelectionRows();
2494   }
2495 
isPathSelected(TreePath path)2496   public boolean isPathSelected(TreePath path)
2497   {
2498     return selectionModel.isPathSelected(path);
2499   }
2500 
2501   /**
2502    * Returns <code>true</code> when the specified row is selected,
2503    * <code>false</code> otherwise. This call is delegated to the
2504    * {@link TreeSelectionModel#isRowSelected(int)} method.
2505    *
2506    * @param row the row to check
2507    *
2508    * @return <code>true</code> when the specified row is selected,
2509    *         <code>false</code> otherwise
2510    */
isRowSelected(int row)2511   public boolean isRowSelected(int row)
2512   {
2513     return selectionModel.isRowSelected(row);
2514   }
2515 
isSelectionEmpty()2516   public boolean isSelectionEmpty()
2517   {
2518     return selectionModel.isSelectionEmpty();
2519   }
2520 
2521   /**
2522    * Return the value of the <code>dragEnabled</code> property.
2523    *
2524    * @return the value
2525    *
2526    * @since 1.4
2527    */
getDragEnabled()2528   public boolean getDragEnabled()
2529   {
2530     return dragEnabled;
2531   }
2532 
2533   /**
2534    * Set the <code>dragEnabled</code> property.
2535    *
2536    * @param enabled new value
2537    *
2538    * @since 1.4
2539    */
setDragEnabled(boolean enabled)2540   public void setDragEnabled(boolean enabled)
2541   {
2542     dragEnabled = enabled;
2543   }
2544 
getRowCount()2545   public int getRowCount()
2546   {
2547     TreeUI ui = getUI();
2548 
2549     if (ui != null)
2550       return ui.getRowCount(this);
2551 
2552     return 0;
2553   }
2554 
collapsePath(TreePath path)2555   public void collapsePath(TreePath path)
2556   {
2557     try
2558       {
2559         fireTreeWillCollapse(path);
2560       }
2561     catch (ExpandVetoException ev)
2562       {
2563         // We do nothing if attempt has been vetoed.
2564       }
2565     setExpandedState(path, false);
2566     fireTreeCollapsed(path);
2567   }
2568 
collapseRow(int row)2569   public void collapseRow(int row)
2570   {
2571     if (row < 0 || row >= getRowCount())
2572       return;
2573 
2574     TreePath path = getPathForRow(row);
2575 
2576     if (path != null)
2577       collapsePath(path);
2578   }
2579 
expandPath(TreePath path)2580   public void expandPath(TreePath path)
2581   {
2582     // Don't expand if path is null
2583     // or is already expanded.
2584     if (path == null || isExpanded(path))
2585       return;
2586 
2587     try
2588       {
2589         fireTreeWillExpand(path);
2590       }
2591     catch (ExpandVetoException ev)
2592       {
2593         // We do nothing if attempt has been vetoed.
2594       }
2595 
2596     setExpandedState(path, true);
2597     fireTreeExpanded(path);
2598   }
2599 
expandRow(int row)2600   public void expandRow(int row)
2601   {
2602     if (row < 0 || row >= getRowCount())
2603       return;
2604 
2605     TreePath path = getPathForRow(row);
2606 
2607     if (path != null)
2608       expandPath(path);
2609   }
2610 
isCollapsed(TreePath path)2611   public boolean isCollapsed(TreePath path)
2612   {
2613     return !isExpanded(path);
2614   }
2615 
isCollapsed(int row)2616   public boolean isCollapsed(int row)
2617   {
2618     if (row < 0 || row >= getRowCount())
2619       return false;
2620 
2621     TreePath path = getPathForRow(row);
2622 
2623     if (path != null)
2624       return isCollapsed(path);
2625 
2626     return false;
2627   }
2628 
isExpanded(TreePath path)2629   public boolean isExpanded(TreePath path)
2630   {
2631     if (path == null)
2632       return false;
2633 
2634     Object state = nodeStates.get(path);
2635 
2636     if ((state == null) || (state != EXPANDED))
2637       return false;
2638 
2639     TreePath parent = path.getParentPath();
2640 
2641     if (parent != null)
2642       return isExpanded(parent);
2643 
2644     return true;
2645   }
2646 
isExpanded(int row)2647   public boolean isExpanded(int row)
2648   {
2649     if (row < 0 || row >= getRowCount())
2650       return false;
2651 
2652     TreePath path = getPathForRow(row);
2653 
2654     if (path != null)
2655       return isExpanded(path);
2656 
2657     return false;
2658   }
2659 
2660   /**
2661    * @since 1.3
2662    */
getExpandsSelectedPaths()2663   public boolean getExpandsSelectedPaths()
2664   {
2665     return expandsSelectedPaths;
2666   }
2667 
2668   /**
2669    * @since 1.3
2670    */
setExpandsSelectedPaths(boolean flag)2671   public void setExpandsSelectedPaths(boolean flag)
2672   {
2673     if (expandsSelectedPaths == flag)
2674       return;
2675 
2676     boolean oldValue = expandsSelectedPaths;
2677     expandsSelectedPaths = flag;
2678     firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue, flag);
2679   }
2680 
getPathBounds(TreePath path)2681   public Rectangle getPathBounds(TreePath path)
2682   {
2683     TreeUI ui = getUI();
2684 
2685     if (ui == null)
2686       return null;
2687 
2688     return ui.getPathBounds(this, path);
2689   }
2690 
getRowBounds(int row)2691   public Rectangle getRowBounds(int row)
2692   {
2693     TreePath path = getPathForRow(row);
2694 
2695     if (path != null)
2696       return getPathBounds(path);
2697 
2698     return null;
2699   }
2700 
isEditing()2701   public boolean isEditing()
2702   {
2703     TreeUI ui = getUI();
2704 
2705     if (ui != null)
2706       return ui.isEditing(this);
2707 
2708     return false;
2709   }
2710 
stopEditing()2711   public boolean stopEditing()
2712   {
2713     TreeUI ui = getUI();
2714 
2715     if (isEditing())
2716       if (ui != null)
2717         return ui.stopEditing(this);
2718 
2719     return false;
2720   }
2721 
cancelEditing()2722   public void cancelEditing()
2723   {
2724     TreeUI ui = getUI();
2725 
2726     if (isEditing())
2727       if (ui != null)
2728         ui.cancelEditing(this);
2729   }
2730 
startEditingAtPath(TreePath path)2731   public void startEditingAtPath(TreePath path)
2732   {
2733     TreeUI ui = getUI();
2734 
2735     if (ui != null)
2736       ui.startEditingAtPath(this, path);
2737   }
2738 
getEditingPath()2739   public TreePath getEditingPath()
2740   {
2741     TreeUI ui = getUI();
2742 
2743     if (ui != null)
2744       return ui.getEditingPath(this);
2745 
2746     return null;
2747   }
2748 
getPathForLocation(int x, int y)2749   public TreePath getPathForLocation(int x, int y)
2750   {
2751     TreePath path = getClosestPathForLocation(x, y);
2752 
2753     if (path != null)
2754       {
2755         Rectangle rect = getPathBounds(path);
2756 
2757         if ((rect != null) && rect.contains(x, y))
2758           return path;
2759       }
2760 
2761     return null;
2762   }
2763 
getRowForLocation(int x, int y)2764   public int getRowForLocation(int x, int y)
2765   {
2766     TreePath path = getPathForLocation(x, y);
2767 
2768     if (path != null)
2769       return getRowForPath(path);
2770 
2771     return -1;
2772   }
2773 
getClosestPathForLocation(int x, int y)2774   public TreePath getClosestPathForLocation(int x, int y)
2775   {
2776     TreeUI ui = getUI();
2777 
2778     if (ui != null)
2779       return ui.getClosestPathForLocation(this, x, y);
2780 
2781     return null;
2782   }
2783 
getClosestRowForLocation(int x, int y)2784   public int getClosestRowForLocation(int x, int y)
2785   {
2786     TreePath path = getClosestPathForLocation(x, y);
2787 
2788     if (path != null)
2789       return getRowForPath(path);
2790 
2791     return -1;
2792   }
2793 
getLastSelectedPathComponent()2794   public Object getLastSelectedPathComponent()
2795   {
2796     TreePath path = getSelectionPath();
2797 
2798     if (path != null)
2799       return path.getLastPathComponent();
2800 
2801     return null;
2802   }
2803 
doExpandParents(TreePath path, boolean state)2804   private void doExpandParents(TreePath path, boolean state)
2805   {
2806     TreePath parent = path.getParentPath();
2807 
2808     if (!isExpanded(parent) && parent != null)
2809       doExpandParents(parent, false);
2810 
2811     nodeStates.put(path, state ? EXPANDED : COLLAPSED);
2812   }
2813 
setExpandedState(TreePath path, boolean state)2814   protected void setExpandedState(TreePath path, boolean state)
2815   {
2816     if (path == null)
2817       return;
2818 
2819     doExpandParents(path, state);
2820   }
2821 
clearToggledPaths()2822   protected void clearToggledPaths()
2823   {
2824     nodeStates.clear();
2825   }
2826 
getDescendantToggledPaths(TreePath parent)2827   protected Enumeration<TreePath> getDescendantToggledPaths(TreePath parent)
2828   {
2829     if (parent == null)
2830       return null;
2831 
2832     Enumeration nodes = nodeStates.keys();
2833     Vector result = new Vector();
2834 
2835     while (nodes.hasMoreElements())
2836       {
2837         TreePath path = (TreePath) nodes.nextElement();
2838 
2839         if (path.isDescendant(parent))
2840           result.addElement(path);
2841       }
2842 
2843     return result.elements();
2844   }
2845 
hasBeenExpanded(TreePath path)2846   public boolean hasBeenExpanded(TreePath path)
2847   {
2848     if (path == null)
2849       return false;
2850 
2851     return nodeStates.get(path) != null;
2852   }
2853 
isVisible(TreePath path)2854   public boolean isVisible(TreePath path)
2855   {
2856     if (path == null)
2857       return false;
2858 
2859     TreePath parent = path.getParentPath();
2860 
2861     if (parent == null)
2862       return true; // Is root node.
2863 
2864     return isExpanded(parent);
2865   }
2866 
makeVisible(TreePath path)2867   public void makeVisible(TreePath path)
2868   {
2869     if (path == null)
2870       return;
2871 
2872     expandPath(path.getParentPath());
2873   }
2874 
isPathEditable(TreePath path)2875   public boolean isPathEditable(TreePath path)
2876   {
2877     return isEditable();
2878   }
2879 
2880   /**
2881    * Creates and returns an instance of {@link TreeModelHandler}.
2882    *
2883    * @return an instance of {@link TreeModelHandler}
2884    */
createTreeModelListener()2885   protected TreeModelListener createTreeModelListener()
2886   {
2887     return new TreeModelHandler();
2888   }
2889 
2890   /**
2891    * Returns a sample TreeModel that can be used in a JTree. This can be used
2892    * in Bean- or GUI-Builders to show something interesting.
2893    *
2894    * @return a sample TreeModel that can be used in a JTree
2895    */
getDefaultTreeModel()2896   protected static TreeModel getDefaultTreeModel()
2897   {
2898     DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root node");
2899     DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("Child node 1");
2900     DefaultMutableTreeNode child11 =
2901       new DefaultMutableTreeNode("Child node 1.1");
2902     DefaultMutableTreeNode child12 =
2903       new DefaultMutableTreeNode("Child node 1.2");
2904     DefaultMutableTreeNode child13 =
2905       new DefaultMutableTreeNode("Child node 1.3");
2906     DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("Child node 2");
2907     DefaultMutableTreeNode child21 =
2908       new DefaultMutableTreeNode("Child node 2.1");
2909     DefaultMutableTreeNode child22 =
2910       new DefaultMutableTreeNode("Child node 2.2");
2911     DefaultMutableTreeNode child23 =
2912       new DefaultMutableTreeNode("Child node 2.3");
2913     DefaultMutableTreeNode child24 =
2914       new DefaultMutableTreeNode("Child node 2.4");
2915 
2916     DefaultMutableTreeNode child3 = new DefaultMutableTreeNode("Child node 3");
2917     root.add(child1);
2918     root.add(child2);
2919     root.add(child3);
2920     child1.add(child11);
2921     child1.add(child12);
2922     child1.add(child13);
2923     child2.add(child21);
2924     child2.add(child22);
2925     child2.add(child23);
2926     child2.add(child24);
2927     return new DefaultTreeModel(root);
2928   }
2929 
2930   /**
2931    * Converts the specified value to a String. This is used by the renderers
2932    * of this JTree and its nodes.
2933    *
2934    * This implementation simply returns <code>value.toString()</code> and
2935    * ignores all other parameters. Subclass this method to control the
2936    * conversion.
2937    *
2938    * @param value the value that is converted to a String
2939    * @param selected indicates if that value is selected or not
2940    * @param expanded indicates if that value is expanded or not
2941    * @param leaf indicates if that value is a leaf node or not
2942    * @param row the row of the node
2943    * @param hasFocus indicates if that node has focus or not
2944    */
convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus)2945   public String convertValueToText(Object value, boolean selected,
2946                                    boolean expanded, boolean leaf, int row, boolean hasFocus)
2947   {
2948     return value.toString();
2949   }
2950 
2951   /**
2952    * A String representation of this JTree. This is intended to be used for
2953    * debugging. The returned string may be empty but may not be
2954    * <code>null</code>.
2955    *
2956    * @return a String representation of this JTree
2957    */
paramString()2958   protected String paramString()
2959   {
2960     // TODO: this is completely legal, but it would possibly be nice
2961     // to return some more content, like the tree structure, some properties
2962     // etc ...
2963     return "";
2964   }
2965 
2966   /**
2967    * Returns all TreePath objects which are a descendants of the given path
2968    * and are exapanded at the moment of the execution of this method. If the
2969    * state of any node is beeing toggled while this method is executing this
2970    * change may be left unaccounted.
2971    *
2972    * @param path The parent of this request
2973    *
2974    * @return An Enumeration containing TreePath objects
2975    */
getExpandedDescendants(TreePath path)2976   public Enumeration<TreePath> getExpandedDescendants(TreePath path)
2977   {
2978     Enumeration paths = nodeStates.keys();
2979     Vector relevantPaths = new Vector();
2980     while (paths.hasMoreElements())
2981       {
2982         TreePath nextPath = (TreePath) paths.nextElement();
2983         if (nodeStates.get(nextPath) == EXPANDED
2984             && path.isDescendant(nextPath))
2985           {
2986             relevantPaths.add(nextPath);
2987           }
2988       }
2989     return relevantPaths.elements();
2990   }
2991 
2992   /**
2993    * Returns the next table element (beginning from the row
2994    * <code>startingRow</code> that starts with <code>prefix</code>.
2995    * Searching is done in the direction specified by <code>bias</code>.
2996    *
2997    * @param prefix the prefix to search for in the cell values
2998    * @param startingRow the index of the row where to start searching from
2999    * @param bias the search direction, either {@link Position.Bias#Forward} or
3000    *        {@link Position.Bias#Backward}
3001    *
3002    * @return the path to the found element or -1 if no such element has been
3003    *         found
3004    *
3005    * @throws IllegalArgumentException if prefix is <code>null</code> or
3006    *         startingRow is not valid
3007    *
3008    * @since 1.4
3009    */
getNextMatch(String prefix, int startingRow, Position.Bias bias)3010   public TreePath getNextMatch(String prefix, int startingRow,
3011                                Position.Bias bias)
3012   {
3013     if (prefix == null)
3014       throw new IllegalArgumentException("The argument 'prefix' must not be"
3015                                          + " null.");
3016     if (startingRow < 0)
3017       throw new IllegalArgumentException("The argument 'startingRow' must not"
3018                                          + " be less than zero.");
3019 
3020     int size = getRowCount();
3021     if (startingRow > size)
3022       throw new IllegalArgumentException("The argument 'startingRow' must not"
3023                                          + " be greater than the number of"
3024                                          + " elements in the TreeModel.");
3025 
3026     TreePath foundPath = null;
3027     if (bias == Position.Bias.Forward)
3028       {
3029         for (int i = startingRow; i < size; i++)
3030           {
3031             TreePath path = getPathForRow(i);
3032             Object o = path.getLastPathComponent();
3033             // FIXME: in the following call to convertValueToText the
3034             // last argument (hasFocus) should be done right.
3035             String item = convertValueToText(o, isRowSelected(i),
3036                                              isExpanded(i), treeModel.isLeaf(o),
3037                                              i, false);
3038             if (item.startsWith(prefix))
3039               {
3040                 foundPath = path;
3041                 break;
3042               }
3043           }
3044       }
3045     else
3046       {
3047         for (int i = startingRow; i >= 0; i--)
3048           {
3049             TreePath path = getPathForRow(i);
3050             Object o = path.getLastPathComponent();
3051             // FIXME: in the following call to convertValueToText the
3052             // last argument (hasFocus) should be done right.
3053             String item = convertValueToText(o, isRowSelected(i),
3054                                              isExpanded(i), treeModel.isLeaf(o), i, false);
3055             if (item.startsWith(prefix))
3056               {
3057                 foundPath = path;
3058                 break;
3059               }
3060           }
3061       }
3062     return foundPath;
3063   }
3064 
3065   /**
3066    * Removes any paths in the current set of selected paths that are
3067    * descendants of <code>path</code>. If <code>includePath</code> is set
3068    * to <code>true</code> and <code>path</code> itself is selected, then
3069    * it will be removed too.
3070    *
3071    * @param path the path from which selected descendants are to be removed
3072    * @param includeSelected if <code>true</code> then <code>path</code> itself
3073    *        will also be remove if it's selected
3074    *
3075    * @return <code>true</code> if something has been removed,
3076    *         <code>false</code> otherwise
3077    *
3078    * @since 1.3
3079    */
removeDescendantSelectedPaths(TreePath path, boolean includeSelected)3080   protected boolean removeDescendantSelectedPaths(TreePath path,
3081                                                   boolean includeSelected)
3082   {
3083     boolean removedSomething = false;
3084     TreePath[] selected = getSelectionPaths();
3085     for (int index = 0; index < selected.length; index++)
3086       {
3087         if ((selected[index] == path && includeSelected)
3088             || (selected[index].isDescendant(path)))
3089           {
3090             removeSelectionPath(selected[index]);
3091             removedSomething = true;
3092           }
3093       }
3094     return removedSomething;
3095   }
3096 
3097   /**
3098    * Removes any descendants of the TreePaths in toRemove that have been
3099    * expanded.
3100    *
3101    * @param toRemove - Enumeration of TreePaths that need to be removed from
3102    * cache of toggled tree paths.
3103    */
removeDescendantToggledPaths(Enumeration<TreePath> toRemove)3104   protected void removeDescendantToggledPaths(Enumeration<TreePath> toRemove)
3105   {
3106     while (toRemove.hasMoreElements())
3107       {
3108         TreePath current = toRemove.nextElement();
3109         Enumeration descendants = getDescendantToggledPaths(current);
3110 
3111         while (descendants.hasMoreElements())
3112           {
3113             TreePath currentDes = (TreePath) descendants.nextElement();
3114             if (isExpanded(currentDes))
3115                 nodeStates.remove(currentDes);
3116           }
3117       }
3118   }
3119 
3120   /**
3121    * <p>
3122    * Sent when the tree has changed enough that we need to resize the bounds,
3123    * but not enough that we need to remove the expanded node set (e.g nodes were
3124    * expanded or collapsed, or nodes were inserted into the tree). You should
3125    * never have to invoke this, the UI will invoke this as it needs to.
3126    * </p>
3127    * <p>
3128    * If the tree uses {@link DefaultTreeModel}, you must call
3129    * {@link DefaultTreeModel#reload(TreeNode)} or
3130    * {@link DefaultTreeModel#reload()} after adding or removing nodes. Following
3131    * the official Java 1.5 API standard, just calling treeDidChange, repaint()
3132    * or revalidate() does <i>not</i> update the tree appearance properly.
3133    *
3134    * @see DefaultTreeModel#reload()
3135    */
treeDidChange()3136   public void treeDidChange()
3137   {
3138     repaint();
3139   }
3140 
3141   /**
3142    * Helper method for
3143    * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
3144    *
3145    * @param propertyName the name of the property
3146    * @param value the value of the property
3147    *
3148    * @throws IllegalArgumentException if the specified property cannot be set
3149    *         by this method
3150    * @throws ClassCastException if the property value does not match the
3151    *         property type
3152    * @throws NullPointerException if <code>c</code> or
3153    *         <code>propertyValue</code> is <code>null</code>
3154    */
setUIProperty(String propertyName, Object value)3155   void setUIProperty(String propertyName, Object value)
3156   {
3157     if (propertyName.equals("rowHeight"))
3158       {
3159         if (! clientRowHeightSet)
3160           {
3161             setRowHeight(((Integer) value).intValue());
3162             clientRowHeightSet = false;
3163           }
3164       }
3165     else if (propertyName.equals("scrollsOnExpand"))
3166       {
3167         if (! clientScrollsOnExpandSet)
3168           {
3169             setScrollsOnExpand(((Boolean) value).booleanValue());
3170             clientScrollsOnExpandSet = false;
3171           }
3172       }
3173     else if (propertyName.equals("showsRootHandles"))
3174       {
3175         if (! clientShowsRootHandlesSet)
3176           {
3177             setShowsRootHandles(((Boolean) value).booleanValue());
3178             clientShowsRootHandlesSet = false;
3179           }
3180       }
3181     else
3182       {
3183         super.setUIProperty(propertyName, value);
3184       }
3185   }
3186 }
3187