OSDN Git Service

2006-06-09 Thomas Fitzsimmons <fitzsim@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / JTree.java
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
703       public void removePropertyChangeListener(PropertyChangeListener l)
704       {
705         // Nothing to do here.
706       }
707       
708       /**
709        * Requests focus for this object.
710        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
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        */
847       public void setVisible(boolean b)
848       {
849         // Nothing to do here.
850       }
851     }
852     
853     /**
854      * Constructor
855      */
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      */
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      */
874     public void clearAccessibleSelection()
875     {
876       clearSelection();
877     }
878     
879     /**
880      * Fire a visible data property change notification.
881      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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
1101     public DynamicUtilTreeNode(Object value, Object children)
1102     {
1103       super(value);
1104       childValue = children;
1105       loadedChildren = false;
1106     }
1107
1108     public int getChildCount()
1109     {
1110       loadChildren();
1111       return super.getChildCount();
1112     }
1113
1114     protected void loadChildren()
1115     {
1116       if (!loadedChildren)
1117         {
1118           createChildren(this, childValue);
1119           loadedChildren = true;
1120         }
1121     }
1122
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      */
1137     public TreeNode getChildAt(int pos)
1138     {
1139       loadChildren();
1140       return super.getChildAt(pos);
1141     }
1142
1143     public boolean isLeaf()
1144     {
1145       return childValue == null || !(childValue instanceof Hashtable
1146           || childValue instanceof Vector 
1147           || childValue.getClass().isArray());
1148     }
1149
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      */
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      */
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      */
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          */
1231     public void treeNodesRemoved(TreeModelEvent ev)
1232     {
1233       // TODO: The API docs suggest that this method should do something
1234       // but I cannot really see what has to be done here ...
1235     }
1236
1237     /**
1238      * Notifies when the structure of the tree is changed.
1239      * 
1240      * This method is called after the actual change occured.
1241      * 
1242      * @param ev the TreeModelEvent describing the change
1243      */
1244     public void treeStructureChanged(TreeModelEvent ev)
1245     {
1246       // Set state of new path.
1247       TreePath path = ev.getTreePath();
1248       setExpandedState(path, isExpanded(path));
1249     }
1250   }
1251
1252   /**
1253    * This redirects TreeSelectionEvents and rewrites the source of it to be
1254    * this JTree. This is typically done when the tree model generates an
1255    * event, but the JTree object associated with that model should be listed
1256    * as the actual source of the event.
1257    */
1258   protected class TreeSelectionRedirector implements TreeSelectionListener,
1259                                                      Serializable
1260   {
1261     /** The serial version UID. */
1262     private static final long serialVersionUID = -3505069663646241664L;
1263
1264     /**
1265      * Creates a new instance of TreeSelectionRedirector
1266      */
1267     protected TreeSelectionRedirector()
1268     {
1269       // Nothing to do here.
1270     }
1271
1272     /**
1273      * Notifies when the tree selection changes.
1274      * 
1275      * @param ev the TreeSelectionEvent that describes the change
1276      */
1277     public void valueChanged(TreeSelectionEvent ev)
1278     {
1279       TreeSelectionEvent rewritten = 
1280         (TreeSelectionEvent) ev.cloneWithSource(JTree.this);
1281       fireValueChanged(rewritten);
1282
1283       // Only repaint the changed nodes.
1284       TreePath[] changed = ev.getPaths();
1285       for (int i = 0; i < changed.length; i++)
1286         {
1287           repaint(getPathBounds(changed[i]));
1288         }
1289     }
1290   }
1291
1292   /**
1293    * A TreeModel that does not allow anything to be selected.
1294    */
1295   protected static class EmptySelectionModel extends DefaultTreeSelectionModel
1296   {
1297     /** The serial version UID. */
1298     private static final long serialVersionUID = -5815023306225701477L;
1299
1300     /**
1301      * The shared instance of this model.
1302      */
1303     protected static final EmptySelectionModel sharedInstance =
1304       new EmptySelectionModel();
1305
1306     /**
1307      * Creates a new instance of EmptySelectionModel.
1308      */
1309     protected EmptySelectionModel()
1310     {
1311       // Nothing to do here.
1312     }
1313
1314     /**
1315      * Returns the shared instance of EmptySelectionModel.
1316      * 
1317      * @return the shared instance of EmptySelectionModel
1318      */
1319     public static EmptySelectionModel sharedInstance()
1320     {
1321       return sharedInstance;
1322     }
1323
1324     /**
1325      * This catches attempts to set a selection and sets nothing instead.
1326      * 
1327      * @param paths not used here
1328      */
1329     public void setSelectionPaths(TreePath[] paths)
1330     {
1331       // We don't allow selections in this class.
1332     }
1333
1334     /**
1335      * This catches attempts to add something to the selection.
1336      * 
1337      * @param paths not used here
1338      */
1339     public void addSelectionPaths(TreePath[] paths)
1340     {
1341       // We don't allow selections in this class.
1342     }
1343
1344     /**
1345      * This catches attempts to remove something from the selection.
1346      * 
1347      * @param paths not used here
1348      */
1349     public void removeSelectionPaths(TreePath[] paths)
1350     {
1351       // We don't allow selections in this class.
1352     }
1353   }
1354
1355   private static final long serialVersionUID = 7559816092864483649L;
1356
1357   public static final String CELL_EDITOR_PROPERTY = "cellEditor";
1358
1359   public static final String CELL_RENDERER_PROPERTY = "cellRenderer";
1360
1361   public static final String EDITABLE_PROPERTY = "editable";
1362
1363   public static final String INVOKES_STOP_CELL_EDITING_PROPERTY =
1364     "invokesStopCellEditing";
1365
1366   public static final String LARGE_MODEL_PROPERTY = "largeModel";
1367
1368   public static final String ROOT_VISIBLE_PROPERTY = "rootVisible";
1369
1370   public static final String ROW_HEIGHT_PROPERTY = "rowHeight";
1371
1372   public static final String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
1373
1374   public static final String SELECTION_MODEL_PROPERTY = "selectionModel";
1375
1376   public static final String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
1377
1378   public static final String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
1379
1380   public static final String TREE_MODEL_PROPERTY = "model";
1381
1382   public static final String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
1383
1384   /** @since 1.3 */
1385   public static final String ANCHOR_SELECTION_PATH_PROPERTY =
1386     "anchorSelectionPath";
1387
1388         /** @since 1.3 */
1389   public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
1390
1391   /** @since 1.3 */
1392   public static final String EXPANDS_SELECTED_PATHS_PROPERTY =
1393     "expandsSelectedPaths";
1394
1395   private static final Object EXPANDED = new Object();
1396
1397   private static final Object COLLAPSED = new Object();
1398
1399   private boolean dragEnabled;
1400
1401   private boolean expandsSelectedPaths;
1402
1403   private TreePath anchorSelectionPath;
1404
1405   /**
1406    * This contains the state of all nodes in the tree. Al/ entries map the
1407    * TreePath of a note to to its state. Valid states are EXPANDED and
1408    * COLLAPSED. Nodes not in this Hashtable are assumed state COLLAPSED.
1409    */
1410   private Hashtable nodeStates = new Hashtable();
1411
1412   protected transient TreeCellEditor cellEditor;
1413
1414   protected transient TreeCellRenderer cellRenderer;
1415
1416   protected boolean editable;
1417
1418   protected boolean invokesStopCellEditing;
1419
1420   protected boolean largeModel;
1421
1422   protected boolean rootVisible;
1423
1424   protected int rowHeight;
1425
1426   protected boolean scrollsOnExpand;
1427
1428   protected transient TreeSelectionModel selectionModel;
1429
1430   protected boolean showsRootHandles;
1431
1432   protected int toggleClickCount;
1433
1434   protected transient TreeModel treeModel;
1435
1436   protected int visibleRowCount;
1437
1438   /**
1439    * Handles TreeModelEvents to update the expandedState.
1440    */
1441   protected transient TreeModelListener treeModelListener;
1442
1443   /**
1444    * Redirects TreeSelectionEvents so that the source is this JTree.
1445    */
1446   protected TreeSelectionRedirector selectionRedirector =
1447     new TreeSelectionRedirector();
1448
1449   /**
1450    * Indicates if the rowHeight property has been set by a client
1451    * program or by the UI.
1452    *
1453    * @see #setUIProperty(String, Object)
1454    * @see LookAndFeel#installProperty(JComponent, String, Object)
1455    */
1456   private boolean clientRowHeightSet = false;
1457
1458   /**
1459    * Indicates if the scrollsOnExpand property has been set by a client
1460    * program or by the UI.
1461    *
1462    * @see #setUIProperty(String, Object)
1463    * @see LookAndFeel#installProperty(JComponent, String, Object)
1464    */
1465   private boolean clientScrollsOnExpandSet = false;
1466
1467   /**
1468    * Indicates if the showsRootHandles property has been set by a client
1469    * program or by the UI.
1470    *
1471    * @see #setUIProperty(String, Object)
1472    * @see LookAndFeel#installProperty(JComponent, String, Object)
1473    */
1474   private boolean clientShowsRootHandlesSet = false;
1475
1476   /**
1477    * Creates a new <code>JTree</code> object.
1478    */
1479   public JTree()
1480   {
1481     this(getDefaultTreeModel());
1482   }
1483
1484   /**
1485    * Creates a new <code>JTree</code> object.
1486    * 
1487    * @param value the initial nodes in the tree
1488    */
1489   public JTree(Hashtable value)
1490   {
1491     this(createTreeModel(value));
1492   }
1493
1494   /**
1495    * Creates a new <code>JTree</code> object.
1496    * 
1497    * @param value the initial nodes in the tree
1498    */
1499   public JTree(Object[] value)
1500   {
1501     this(createTreeModel(value));
1502   }
1503
1504   /**
1505    * Creates a new <code>JTree</code> object.
1506    * 
1507    * @param model the model to use
1508    */
1509   public JTree(TreeModel model)
1510   {
1511     setRootVisible(true);
1512     setSelectionModel(new EmptySelectionModel());
1513     selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
1514     
1515     // The root node appears expanded by default.
1516     nodeStates = new Hashtable();
1517
1518     // Install the UI before installing the model. This way we avoid double
1519     // initialization of lots of UI and model stuff inside the UI and related
1520     // classes. The necessary UI updates are performed via property change
1521     // events to the UI.
1522     updateUI();
1523     setModel(model);
1524   }
1525
1526   /**
1527    * Creates a new <code>JTree</code> object.
1528    * 
1529    * @param root the root node
1530    */
1531   public JTree(TreeNode root)
1532   {
1533     this(root, false);
1534   }
1535
1536   /**
1537    * Creates a new <code>JTree</code> object.
1538    * 
1539    * @param root the root node
1540    * @param asksAllowChildren if false, all nodes without children are leaf
1541    *        nodes. If true, only nodes that do not allow children are leaf
1542    *        nodes.
1543    */
1544   public JTree(TreeNode root, boolean asksAllowChildren)
1545   {
1546     this(new DefaultTreeModel(root, asksAllowChildren));
1547   }
1548
1549   /**
1550    * Creates a new <code>JTree</code> object.
1551    * 
1552    * @param value the initial nodes in the tree
1553    */
1554   public JTree(Vector value)
1555   {
1556     this(createTreeModel(value));
1557   }
1558
1559   public int getRowForPath(TreePath path)
1560   {
1561     TreeUI ui = getUI();
1562
1563     if (ui != null)
1564       return ui.getRowForPath(this, path);
1565
1566     return -1;
1567   }
1568
1569   public TreePath getPathForRow(int row)
1570   {
1571     TreeUI ui = getUI();
1572     return ui != null ? ui.getPathForRow(this, row) : null;
1573   }
1574   
1575   /**
1576    * Get the pathes that are displayes between the two given rows.
1577    * 
1578    * @param index0 the starting row, inclusive
1579    * @param index1 the ending row, inclusive
1580    * 
1581    * @return the array of the tree pathes
1582    */
1583   protected TreePath[] getPathBetweenRows(int index0, int index1)
1584   {
1585     TreeUI ui = getUI();
1586
1587     if (ui == null)
1588       return null;
1589
1590     int minIndex = Math.min(index0, index1);
1591     int maxIndex = Math.max(index0, index1);
1592     TreePath[] paths = new TreePath[maxIndex - minIndex + 1];
1593
1594     for (int i = minIndex; i <= maxIndex; ++i)
1595       paths[i - minIndex] = ui.getPathForRow(this, i);
1596
1597     return paths;
1598   }
1599
1600   /**
1601    * Creates a new <code>TreeModel</code> object.
1602    * 
1603    * @param value the values stored in the model
1604    */
1605   protected static TreeModel createTreeModel(Object value)
1606   {
1607     return new DefaultTreeModel(new DynamicUtilTreeNode(value, value));
1608   }
1609
1610   /**
1611    * Return the UI associated with this <code>JTree</code> object.
1612    * 
1613    * @return the associated <code>TreeUI</code> object
1614    */
1615   public TreeUI getUI()
1616   {
1617     return (TreeUI) ui;
1618   }
1619
1620   /**
1621    * Sets the UI associated with this <code>JTree</code> object.
1622    * 
1623    * @param ui the <code>TreeUI</code> to associate
1624    */
1625   public void setUI(TreeUI ui)
1626   {
1627     super.setUI(ui);
1628   }
1629
1630   /**
1631    * This method resets the UI used to the Look and Feel defaults..
1632    */
1633   public void updateUI()
1634   {
1635     setUI((TreeUI) UIManager.getUI(this));
1636   }
1637
1638   /**
1639    * This method returns the String ID of the UI class of Separator.
1640    * 
1641    * @return The UI class' String ID.
1642    */
1643   public String getUIClassID()
1644   {
1645     return "TreeUI";
1646   }
1647
1648   /**
1649    * Gets the AccessibleContext associated with this
1650    * <code>JTree</code>.
1651    * 
1652    * @return the associated context
1653    */
1654   public AccessibleContext getAccessibleContext()
1655   {
1656     return new AccessibleJTree();
1657   }
1658
1659   /**
1660    * Returns the preferred viewport size.
1661    * 
1662    * @return the preferred size
1663    */
1664   public Dimension getPreferredScrollableViewportSize()
1665   {
1666     return getPreferredSize();
1667   }
1668   
1669   /**
1670    * Return the preferred scrolling amount (in pixels) for the given scrolling
1671    * direction and orientation. This method handles a partially exposed row by
1672    * returning the distance required to completely expose the item.
1673    * 
1674    * @param visibleRect the currently visible part of the component.
1675    * @param orientation the scrolling orientation
1676    * @param direction the scrolling direction (negative - up, positive -down).
1677    *          The values greater than one means that more mouse wheel or similar
1678    *          events were generated, and hence it is better to scroll the longer
1679    *          distance.
1680    * @author Audrius Meskauskas (audriusa@bioinformatics.org)
1681    */
1682   public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
1683                                         int direction)
1684   {
1685     int delta;
1686
1687     // Round so that the top would start from the row boundary
1688     if (orientation == SwingConstants.VERTICAL)
1689       {
1690         // One pixel down, otherwise picks another row too high.
1691         int row = getClosestRowForLocation(visibleRect.x, visibleRect.y + 1);
1692         row = row + direction;
1693         if (row < 0)
1694           row = 0;
1695
1696         Rectangle newTop = getRowBounds(row);
1697         delta = newTop.y - visibleRect.y;
1698       }
1699     else
1700       delta = direction * rowHeight == 0 ? 20 : rowHeight;
1701     return delta;
1702   }
1703
1704   public int getScrollableBlockIncrement(Rectangle visibleRect,
1705                                          int orientation, int direction)
1706   {
1707     return getScrollableUnitIncrement(visibleRect, orientation, direction);
1708   }
1709
1710   public boolean getScrollableTracksViewportHeight()
1711   {
1712     if (getParent() instanceof JViewport)
1713       return ((JViewport) getParent()).getHeight() > getPreferredSize().height;
1714     return false;
1715   }
1716
1717   public boolean getScrollableTracksViewportWidth()
1718   {
1719     if (getParent() instanceof JViewport)
1720       return ((JViewport) getParent()).getWidth() > getPreferredSize().width;
1721     return false;
1722   }
1723
1724   /**
1725    * Adds a <code>TreeExpansionListener</code> object to the tree.
1726    * 
1727    * @param listener the listener to add
1728    */
1729   public void addTreeExpansionListener(TreeExpansionListener listener)
1730   {
1731     listenerList.add(TreeExpansionListener.class, listener);
1732   }
1733
1734   /**
1735    * Removes a <code>TreeExpansionListener</code> object from the tree.
1736    * 
1737    * @param listener the listener to remove
1738    */
1739   public void removeTreeExpansionListener(TreeExpansionListener listener)
1740   {
1741     listenerList.remove(TreeExpansionListener.class, listener);
1742   }
1743
1744   /**
1745    * Returns all added <code>TreeExpansionListener</code> objects.
1746    * 
1747    * @return an array of listeners
1748    */
1749   public TreeExpansionListener[] getTreeExpansionListeners()
1750   {
1751     return (TreeExpansionListener[]) getListeners(TreeExpansionListener.class);
1752   }
1753
1754   /**
1755    * Notifies all listeners that the tree was collapsed.
1756    * 
1757    * @param path the path to the node that was collapsed
1758    */
1759   public void fireTreeCollapsed(TreePath path)
1760   {
1761     TreeExpansionEvent event = new TreeExpansionEvent(this, path);
1762     TreeExpansionListener[] listeners = getTreeExpansionListeners();
1763
1764     for (int index = 0; index < listeners.length; ++index)
1765       listeners[index].treeCollapsed(event);
1766   }
1767
1768   /**
1769    * Notifies all listeners that the tree was expanded.
1770    * 
1771    * @param path the path to the node that was expanded
1772    */
1773   public void fireTreeExpanded(TreePath path)
1774   {
1775     TreeExpansionEvent event = new TreeExpansionEvent(this, path);
1776     TreeExpansionListener[] listeners = getTreeExpansionListeners();
1777
1778     for (int index = 0; index < listeners.length; ++index)
1779       listeners[index].treeExpanded(event);
1780   }
1781
1782   /**
1783    * Adds a <code>TreeSelctionListener</code> object to the tree.
1784    * 
1785    * @param listener the listener to add
1786    */
1787   public void addTreeSelectionListener(TreeSelectionListener listener)
1788   {
1789     listenerList.add(TreeSelectionListener.class, listener);
1790   }
1791
1792   /**
1793    * Removes a <code>TreeSelectionListener</code> object from the tree.
1794    * 
1795    * @param listener the listener to remove
1796    */
1797   public void removeTreeSelectionListener(TreeSelectionListener listener)
1798   {
1799     listenerList.remove(TreeSelectionListener.class, listener);
1800   }
1801
1802   /**
1803    * Returns all added <code>TreeSelectionListener</code> objects.
1804    * 
1805    * @return an array of listeners
1806    */
1807   public TreeSelectionListener[] getTreeSelectionListeners()
1808   {
1809     return (TreeSelectionListener[]) 
1810     getListeners(TreeSelectionListener.class);
1811   }
1812
1813   /**
1814    * Notifies all listeners when the selection of the tree changed.
1815    * 
1816    * @param event the event to send
1817    */
1818   protected void fireValueChanged(TreeSelectionEvent event)
1819   {
1820     TreeSelectionListener[] listeners = getTreeSelectionListeners();
1821
1822     for (int index = 0; index < listeners.length; ++index)
1823       listeners[index].valueChanged(event);
1824   }
1825
1826   /**
1827    * Adds a <code>TreeWillExpandListener</code> object to the tree.
1828    * 
1829    * @param listener the listener to add
1830    */
1831   public void addTreeWillExpandListener(TreeWillExpandListener listener)
1832   {
1833     listenerList.add(TreeWillExpandListener.class, listener);
1834   }
1835
1836   /**
1837    * Removes a <code>TreeWillExpandListener</code> object from the tree.
1838    * 
1839    * @param listener the listener to remove
1840    */
1841   public void removeTreeWillExpandListener(TreeWillExpandListener listener)
1842   {
1843     listenerList.remove(TreeWillExpandListener.class, listener);
1844   }
1845
1846   /**
1847    * Returns all added <code>TreeWillExpandListener</code> objects.
1848    * 
1849    * @return an array of listeners
1850    */
1851   public TreeWillExpandListener[] getTreeWillExpandListeners()
1852   {
1853     return (TreeWillExpandListener[]) 
1854     getListeners(TreeWillExpandListener.class);
1855   }
1856
1857   /**
1858    * Notifies all listeners that the tree will collapse.
1859    * 
1860    * @param path the path to the node that will collapse
1861    */
1862   public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException
1863   {
1864     TreeExpansionEvent event = new TreeExpansionEvent(this, path);
1865     TreeWillExpandListener[] listeners = getTreeWillExpandListeners();
1866
1867     for (int index = 0; index < listeners.length; ++index)
1868       listeners[index].treeWillCollapse(event);
1869   }
1870
1871   /**
1872    * Notifies all listeners that the tree will expand.
1873    * 
1874    * @param path the path to the node that will expand
1875    */
1876   public void fireTreeWillExpand(TreePath path) throws ExpandVetoException
1877   {
1878     TreeExpansionEvent event = new TreeExpansionEvent(this, path);
1879     TreeWillExpandListener[] listeners = getTreeWillExpandListeners();
1880
1881     for (int index = 0; index < listeners.length; ++index)
1882       listeners[index].treeWillExpand(event);
1883   }
1884
1885   /**
1886    * Returns the model of this <code>JTree</code> object.
1887    * 
1888    * @return the associated <code>TreeModel</code>
1889    */
1890   public TreeModel getModel()
1891   {
1892     return treeModel;
1893   }
1894
1895   /**
1896    * Sets the model to use in <code>JTree</code>.
1897    * 
1898    * @param model the <code>TreeModel</code> to use
1899    */
1900   public void setModel(TreeModel model)
1901   {
1902     if (treeModel == model)
1903       return;
1904
1905     // Remove listeners from old model.
1906     if (treeModel != null && treeModelListener != null)
1907       treeModel.removeTreeModelListener(treeModelListener);
1908
1909     // add treeModelListener to the new model
1910     if (treeModelListener == null)
1911       treeModelListener = createTreeModelListener();
1912     if (model != null) // as setModel(null) is allowed
1913       model.addTreeModelListener(treeModelListener);
1914
1915     TreeModel oldValue = treeModel;
1916     treeModel = model;
1917     clearToggledPaths();
1918
1919     if (treeModel != null)
1920       {
1921         if (treeModelListener == null)
1922           treeModelListener = createTreeModelListener();
1923         if (treeModelListener != null)
1924           treeModel.addTreeModelListener(treeModelListener);
1925         Object root = treeModel.getRoot();
1926         if (root != null && !treeModel.isLeaf(root))
1927           {
1928             nodeStates.put(new TreePath(root), Boolean.TRUE);
1929           }
1930       }
1931
1932     firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model);
1933   }
1934
1935   /**
1936    * Checks if this <code>JTree</code> object is editable.
1937    * 
1938    * @return <code>true</code> if this tree object is editable,
1939    *         <code>false</code> otherwise
1940    */
1941   public boolean isEditable()
1942   {
1943     return editable;
1944   }
1945
1946   /**
1947    * Sets the <code>editable</code> property.
1948    * 
1949    * @param flag <code>true</code> to make this tree object editable,
1950    *        <code>false</code> otherwise
1951    */
1952   public void setEditable(boolean flag)
1953   {
1954     if (editable == flag)
1955       return;
1956
1957     boolean oldValue = editable;
1958     editable = flag;
1959     firePropertyChange(EDITABLE_PROPERTY, oldValue, editable);
1960   }
1961
1962   /**
1963    * Checks if the root element is visible.
1964    * 
1965    * @return <code>true</code> if the root element is visible,
1966    *         <code>false</code> otherwise
1967    */
1968   public boolean isRootVisible()
1969   {
1970     return rootVisible;
1971   }
1972
1973   public void setRootVisible(boolean flag)
1974   {
1975     if (rootVisible == flag)
1976       return;
1977
1978     // If the root is currently selected, unselect it
1979     if (rootVisible && !flag)
1980       {
1981         TreeSelectionModel model = getSelectionModel();
1982         // The root is always shown in the first row
1983         TreePath rootPath = getPathForRow(0);
1984         model.removeSelectionPath(rootPath);
1985       }
1986     
1987     boolean oldValue = rootVisible;
1988     rootVisible = flag;
1989     firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag);
1990     
1991   }
1992
1993   public boolean getShowsRootHandles()
1994   {
1995     return showsRootHandles;
1996   }
1997
1998   public void setShowsRootHandles(boolean flag)
1999   {
2000     clientShowsRootHandlesSet = true;
2001
2002     if (showsRootHandles == flag)
2003       return;
2004     
2005     boolean oldValue = showsRootHandles;
2006     showsRootHandles = flag;
2007     firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue, flag);
2008   }
2009
2010   public TreeCellEditor getCellEditor()
2011   {
2012     return cellEditor;
2013   }
2014
2015   public void setCellEditor(TreeCellEditor editor)
2016   {
2017     if (cellEditor == editor)
2018       return;
2019
2020     TreeCellEditor oldValue = cellEditor;
2021     cellEditor = editor;
2022     firePropertyChange(CELL_EDITOR_PROPERTY, oldValue, editor);
2023   }
2024
2025   public TreeCellRenderer getCellRenderer()
2026   {
2027     return cellRenderer;
2028   }
2029
2030   public void setCellRenderer(TreeCellRenderer newRenderer)
2031   {
2032     if (cellRenderer == newRenderer)
2033       return;
2034
2035     TreeCellRenderer oldValue = cellRenderer;
2036     cellRenderer = newRenderer;
2037     firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, newRenderer);
2038   }
2039
2040   public TreeSelectionModel getSelectionModel()
2041   {
2042     return selectionModel;
2043   }
2044
2045   public void setSelectionModel(TreeSelectionModel model)
2046   {
2047     if (selectionModel == model)
2048       return;
2049
2050     if (selectionModel != null)
2051       selectionModel.removeTreeSelectionListener(selectionRedirector);
2052
2053     TreeSelectionModel oldValue = selectionModel;
2054     selectionModel = model;
2055
2056     if (selectionModel != null)
2057       selectionModel.addTreeSelectionListener(selectionRedirector);
2058
2059     firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, model);
2060     revalidate();
2061     repaint();
2062   }
2063
2064   public int getVisibleRowCount()
2065   {
2066     return visibleRowCount;
2067   }
2068
2069   public void setVisibleRowCount(int rows)
2070   {
2071     if (visibleRowCount == rows)
2072       return;
2073
2074     int oldValue = visibleRowCount;
2075     visibleRowCount = rows;
2076     firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldValue, rows);
2077   }
2078
2079   public boolean isLargeModel()
2080   {
2081     return largeModel;
2082   }
2083
2084   public void setLargeModel(boolean large)
2085   {
2086     if (largeModel == large)
2087       return;
2088
2089     boolean oldValue = largeModel;
2090     largeModel = large;
2091     firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, large);
2092   }
2093
2094   public int getRowHeight()
2095   {
2096     return rowHeight;
2097   }
2098
2099   public void setRowHeight(int height)
2100   {
2101     clientRowHeightSet = true;
2102
2103     if (rowHeight == height)
2104       return;
2105
2106     int oldValue = rowHeight;
2107     rowHeight = height;
2108     firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, height);
2109   }
2110
2111   public boolean isFixedRowHeight()
2112   {
2113     return rowHeight > 0;
2114   }
2115
2116   public boolean getInvokesStopCellEditing()
2117   {
2118     return invokesStopCellEditing;
2119   }
2120
2121   public void setInvokesStopCellEditing(boolean invoke)
2122   {
2123     if (invokesStopCellEditing == invoke)
2124       return;
2125
2126     boolean oldValue = invokesStopCellEditing;
2127     invokesStopCellEditing = invoke;
2128     firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, 
2129                        oldValue, invoke);
2130   }
2131
2132   /**
2133    * @since 1.3
2134    */
2135   public int getToggleClickCount()
2136   {
2137     return toggleClickCount;
2138   }
2139
2140   /**
2141    * @since 1.3
2142    */
2143   public void setToggleClickCount(int count)
2144   {
2145     if (toggleClickCount == count)
2146       return;
2147
2148     int oldValue = toggleClickCount;
2149     toggleClickCount = count;
2150     firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldValue, count);
2151   }
2152
2153   public void scrollPathToVisible(TreePath path)
2154   {
2155     if (path == null)
2156       return;
2157     Rectangle rect = getPathBounds(path);
2158     scrollRectToVisible(rect);
2159   }
2160
2161   public void scrollRowToVisible(int row)
2162   {
2163     scrollPathToVisible(getPathForRow(row));
2164   }
2165
2166   public boolean getScrollsOnExpand()
2167   {
2168     return scrollsOnExpand;
2169   }
2170
2171   public void setScrollsOnExpand(boolean scroll)
2172   {
2173     clientScrollsOnExpandSet = true;
2174     if (scrollsOnExpand == scroll)
2175       return;
2176
2177     boolean oldValue = scrollsOnExpand;
2178     scrollsOnExpand = scroll;
2179     firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll);
2180   }
2181
2182   public void setSelectionPath(TreePath path)
2183   {
2184     selectionModel.setSelectionPath(path);
2185   }
2186
2187   public void setSelectionPaths(TreePath[] paths)
2188   {
2189     selectionModel.setSelectionPaths(paths);
2190   }
2191
2192   public void setSelectionRow(int row)
2193   {
2194     TreePath path = getPathForRow(row);
2195
2196     if (path != null)
2197       selectionModel.setSelectionPath(path);
2198   }
2199
2200   public void setSelectionRows(int[] rows)
2201   {
2202     // Make sure we have an UI so getPathForRow() does not return null.
2203     if (rows == null || getUI() == null)
2204       return;
2205
2206     TreePath[] paths = new TreePath[rows.length];
2207
2208     for (int i = rows.length - 1; i >= 0; --i)
2209       paths[i] = getPathForRow(rows[i]);
2210
2211     setSelectionPaths(paths);
2212   }
2213
2214   public void setSelectionInterval(int index0, int index1)
2215   {
2216     TreePath[] paths = getPathBetweenRows(index0, index1);
2217
2218     if (paths != null)
2219       setSelectionPaths(paths);
2220   }
2221
2222   public void addSelectionPath(TreePath path)
2223   {
2224     selectionModel.addSelectionPath(path);
2225   }
2226
2227   public void addSelectionPaths(TreePath[] paths)
2228   {
2229     selectionModel.addSelectionPaths(paths);
2230   }
2231
2232   public void addSelectionRow(int row)
2233   {
2234     TreePath path = getPathForRow(row);
2235
2236     if (path != null)
2237       selectionModel.addSelectionPath(path);
2238   }
2239
2240   public void addSelectionRows(int[] rows)
2241   {
2242     // Make sure we have an UI so getPathForRow() does not return null.
2243     if (rows == null || getUI() == null)
2244       return;
2245
2246     TreePath[] paths = new TreePath[rows.length];
2247
2248     for (int i = rows.length - 1; i >= 0; --i)
2249       paths[i] = getPathForRow(rows[i]);
2250
2251     addSelectionPaths(paths);
2252   }
2253   
2254   /**
2255    * Select all rows between the two given indexes, inclusive. The method
2256    * will not select the inner leaves and braches of the currently collapsed
2257    * nodes in this interval.
2258    * 
2259    * @param index0 the starting row, inclusive
2260    * @param index1 the ending row, inclusive
2261    */
2262   public void addSelectionInterval(int index0, int index1)
2263   {
2264     TreePath[] paths = getPathBetweenRows(index0, index1);
2265
2266     if (paths != null)
2267       addSelectionPaths(paths);
2268   }
2269
2270   public void removeSelectionPath(TreePath path)
2271   {
2272     selectionModel.removeSelectionPath(path);
2273   }
2274
2275   public void removeSelectionPaths(TreePath[] paths)
2276   {
2277     selectionModel.removeSelectionPaths(paths);
2278   }
2279
2280   public void removeSelectionRow(int row)
2281   {
2282     TreePath path = getPathForRow(row);
2283
2284     if (path != null)
2285       selectionModel.removeSelectionPath(path);
2286   }
2287
2288   public void removeSelectionRows(int[] rows)
2289   {
2290     if (rows == null || getUI() == null)
2291       return;
2292
2293     TreePath[] paths = new TreePath[rows.length];
2294
2295     for (int i = rows.length - 1; i >= 0; --i)
2296       paths[i] = getPathForRow(rows[i]);
2297
2298     removeSelectionPaths(paths);
2299   }
2300
2301   public void removeSelectionInterval(int index0, int index1)
2302   {
2303     TreePath[] paths = getPathBetweenRows(index0, index1);
2304
2305     if (paths != null)
2306       removeSelectionPaths(paths);
2307   }
2308
2309   public void clearSelection()
2310   {
2311     selectionModel.clearSelection();
2312     setLeadSelectionPath(null);
2313   }
2314
2315   public TreePath getLeadSelectionPath()
2316   {
2317     if (selectionModel == null)
2318       return null;
2319     else
2320       return selectionModel.getLeadSelectionPath();
2321   }
2322
2323   /**
2324    * @since 1.3
2325    */
2326   public void setLeadSelectionPath(TreePath path)
2327   {
2328     if (selectionModel != null)
2329       {
2330         TreePath oldValue = selectionModel.getLeadSelectionPath();
2331         if (path.equals(oldValue))
2332           return;
2333        
2334         // Repaint the previous and current rows with the lead selection path.
2335         if (path != null)
2336           {
2337             repaint(getPathBounds(path));
2338             selectionModel.addSelectionPath(path);
2339           }
2340         
2341         if (oldValue != null)
2342           repaint(getPathBounds(oldValue));
2343         
2344         firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path);
2345       }
2346   }
2347
2348   /**
2349    * @since 1.3
2350    */
2351   public TreePath getAnchorSelectionPath()
2352   {
2353     return anchorSelectionPath;
2354   }
2355
2356   /**
2357    * @since 1.3
2358    */
2359   public void setAnchorSelectionPath(TreePath path)
2360   {
2361     if (anchorSelectionPath == path)
2362       return;
2363
2364     TreePath oldValue = anchorSelectionPath;
2365     anchorSelectionPath = path;
2366     firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, path);
2367   }
2368
2369   public int getLeadSelectionRow()
2370   {
2371     return selectionModel.getLeadSelectionRow();
2372   }
2373
2374   public int getMaxSelectionRow()
2375   {
2376     return selectionModel.getMaxSelectionRow();
2377   }
2378
2379   public int getMinSelectionRow()
2380   {
2381     return selectionModel.getMinSelectionRow();
2382   }
2383
2384   public int getSelectionCount()
2385   {
2386     return selectionModel.getSelectionCount();
2387   }
2388
2389   public TreePath getSelectionPath()
2390   {
2391     return selectionModel.getSelectionPath();
2392   }
2393
2394   public TreePath[] getSelectionPaths()
2395   {
2396     return selectionModel.getSelectionPaths();
2397   }
2398
2399   public int[] getSelectionRows()
2400   {
2401     return selectionModel.getSelectionRows();
2402   }
2403
2404   public boolean isPathSelected(TreePath path)
2405   {
2406     return selectionModel.isPathSelected(path);
2407   }
2408
2409   public boolean isRowSelected(int row)
2410   {
2411     return selectionModel.isPathSelected(getPathForRow(row));
2412   }
2413
2414   public boolean isSelectionEmpty()
2415   {
2416     return selectionModel.isSelectionEmpty();
2417   }
2418
2419   /**
2420    * Return the value of the <code>dragEnabled</code> property.
2421    * 
2422    * @return the value
2423    * 
2424    * @since 1.4
2425    */
2426   public boolean getDragEnabled()
2427   {
2428     return dragEnabled;
2429   }
2430
2431   /**
2432    * Set the <code>dragEnabled</code> property.
2433    * 
2434    * @param enabled new value
2435    * 
2436    * @since 1.4
2437    */
2438   public void setDragEnabled(boolean enabled)
2439   {
2440     dragEnabled = enabled;
2441   }
2442
2443   public int getRowCount()
2444   {
2445     TreeUI ui = getUI();
2446
2447     if (ui != null)
2448       return ui.getRowCount(this);
2449
2450     return 0;
2451   }
2452
2453   public void collapsePath(TreePath path)
2454   {
2455     try
2456       {
2457         fireTreeWillCollapse(path);
2458       }
2459     catch (ExpandVetoException ev)
2460       {
2461         // We do nothing if attempt has been vetoed.
2462       }
2463     setExpandedState(path, false);
2464     fireTreeCollapsed(path);
2465   }
2466
2467   public void collapseRow(int row)
2468   {
2469     if (row < 0 || row >= getRowCount())
2470       return;
2471
2472     TreePath path = getPathForRow(row);
2473
2474     if (path != null)
2475       collapsePath(path);
2476   }
2477
2478   public void expandPath(TreePath path)
2479   {
2480     // Don't expand if path is null
2481     // or is already expanded.
2482     if (path == null || isExpanded(path))
2483       return;
2484
2485     try
2486       {
2487         fireTreeWillExpand(path);
2488       }
2489     catch (ExpandVetoException ev)
2490       {
2491         // We do nothing if attempt has been vetoed.
2492       }
2493
2494     setExpandedState(path, true);
2495     fireTreeExpanded(path);
2496   }
2497
2498   public void expandRow(int row)
2499   {
2500     if (row < 0 || row >= getRowCount())
2501       return;
2502
2503     TreePath path = getPathForRow(row);
2504
2505     if (path != null)
2506       expandPath(path);
2507   }
2508
2509   public boolean isCollapsed(TreePath path)
2510   {
2511     return !isExpanded(path);
2512   }
2513
2514   public boolean isCollapsed(int row)
2515   {
2516     if (row < 0 || row >= getRowCount())
2517       return false;
2518
2519     TreePath path = getPathForRow(row);
2520
2521     if (path != null)
2522       return isCollapsed(path);
2523
2524     return false;
2525   }
2526
2527   public boolean isExpanded(TreePath path)
2528   {
2529     if (path == null)
2530       return false;
2531
2532     Object state = nodeStates.get(path);
2533
2534     if ((state == null) || (state != EXPANDED))
2535       return false;
2536
2537     TreePath parent = path.getParentPath();
2538
2539     if (parent != null)
2540       return isExpanded(parent);
2541
2542     return true;
2543   }
2544
2545   public boolean isExpanded(int row)
2546   {
2547     if (row < 0 || row >= getRowCount())
2548       return false;
2549
2550     TreePath path = getPathForRow(row);
2551
2552     if (path != null)
2553       return isExpanded(path);
2554
2555     return false;
2556   }
2557
2558   /**
2559    * @since 1.3
2560    */
2561   public boolean getExpandsSelectedPaths()
2562   {
2563     return expandsSelectedPaths;
2564   }
2565
2566   /**
2567    * @since 1.3
2568    */
2569   public void setExpandsSelectedPaths(boolean flag)
2570   {
2571     if (expandsSelectedPaths == flag)
2572       return;
2573
2574     boolean oldValue = expandsSelectedPaths;
2575     expandsSelectedPaths = flag;
2576     firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue, flag);
2577   }
2578
2579   public Rectangle getPathBounds(TreePath path)
2580   {
2581     TreeUI ui = getUI();
2582
2583     if (ui == null)
2584       return null;
2585
2586     return ui.getPathBounds(this, path);
2587   }
2588
2589   public Rectangle getRowBounds(int row)
2590   {
2591     TreePath path = getPathForRow(row);
2592
2593     if (path != null)
2594       return getPathBounds(path);
2595
2596     return null;
2597   }
2598
2599   public boolean isEditing()
2600   {
2601     TreeUI ui = getUI();
2602
2603     if (ui != null)
2604       return ui.isEditing(this);
2605
2606     return false;
2607   }
2608
2609   public boolean stopEditing()
2610   {
2611     TreeUI ui = getUI();
2612
2613     if (isEditing())
2614       if (ui != null)
2615         return ui.stopEditing(this);
2616
2617     return false;
2618   }
2619
2620   public void cancelEditing()
2621   {
2622     TreeUI ui = getUI();
2623       
2624     if (isEditing())
2625       if (ui != null)
2626         ui.cancelEditing(this);
2627   }
2628
2629   public void startEditingAtPath(TreePath path)
2630   {
2631     TreeUI ui = getUI();
2632
2633     if (ui != null)
2634       ui.startEditingAtPath(this, path);
2635   }
2636
2637   public TreePath getEditingPath()
2638   {
2639     TreeUI ui = getUI();
2640
2641     if (ui != null)
2642       return ui.getEditingPath(this);
2643
2644     return null;
2645   }
2646
2647   public TreePath getPathForLocation(int x, int y)
2648   {
2649     TreePath path = getClosestPathForLocation(x, y);
2650
2651     if (path != null)
2652       {
2653         Rectangle rect = getPathBounds(path);
2654
2655         if ((rect != null) && rect.contains(x, y))
2656           return path;
2657       }
2658
2659     return null;
2660   }
2661
2662   public int getRowForLocation(int x, int y)
2663   {
2664     TreePath path = getPathForLocation(x, y);
2665
2666     if (path != null)
2667       return getRowForPath(path);
2668
2669     return -1;
2670   }
2671
2672   public TreePath getClosestPathForLocation(int x, int y)
2673   {
2674     TreeUI ui = getUI();
2675
2676     if (ui != null)
2677       return ui.getClosestPathForLocation(this, x, y);
2678
2679     return null;
2680   }
2681
2682   public int getClosestRowForLocation(int x, int y)
2683   {
2684     TreePath path = getClosestPathForLocation(x, y);
2685
2686     if (path != null)
2687       return getRowForPath(path);
2688
2689     return -1;
2690   }
2691
2692   public Object getLastSelectedPathComponent()
2693   {
2694     TreePath path = getSelectionPath();
2695
2696     if (path != null)
2697       return path.getLastPathComponent();
2698
2699     return null;
2700   }
2701
2702   private void doExpandParents(TreePath path, boolean state)
2703   {
2704     TreePath parent = path.getParentPath();             
2705
2706     if (!isExpanded(parent) && parent != null)
2707       doExpandParents(parent, false);
2708     
2709     nodeStates.put(path, state ? EXPANDED : COLLAPSED);
2710   }
2711
2712   protected void setExpandedState(TreePath path, boolean state)
2713   {
2714     if (path == null)
2715       return;
2716
2717     doExpandParents(path, state);
2718   }
2719
2720   protected void clearToggledPaths()
2721   {
2722     nodeStates.clear();
2723   }
2724
2725   protected Enumeration getDescendantToggledPaths(TreePath parent)
2726   {
2727     if (parent == null)
2728       return null;
2729
2730     Enumeration nodes = nodeStates.keys();
2731     Vector result = new Vector();
2732
2733     while (nodes.hasMoreElements())
2734       {
2735         TreePath path = (TreePath) nodes.nextElement();
2736
2737         if (path.isDescendant(parent))
2738           result.addElement(path);
2739       }
2740
2741     return result.elements();
2742   }
2743
2744   public boolean hasBeenExpanded(TreePath path)
2745   {
2746     if (path == null)
2747       return false;
2748
2749     return nodeStates.get(path) != null;
2750   }
2751
2752   public boolean isVisible(TreePath path)
2753   {
2754     if (path == null)
2755       return false;
2756
2757     TreePath parent = path.getParentPath();
2758
2759     if (parent == null)
2760       return true; // Is root node.
2761
2762     return isExpanded(parent);
2763   }
2764
2765   public void makeVisible(TreePath path)
2766   {
2767     if (path == null)
2768       return;
2769     
2770     expandPath(path.getParentPath());
2771   }
2772
2773   public boolean isPathEditable(TreePath path)
2774   {
2775     return isEditable();
2776   }
2777
2778   /**
2779    * Creates and returns an instance of {@link TreeModelHandler}.
2780    * 
2781    * @return an instance of {@link TreeModelHandler}
2782    */
2783   protected TreeModelListener createTreeModelListener()
2784   {
2785     return new TreeModelHandler();
2786   }
2787
2788   /**
2789    * Returns a sample TreeModel that can be used in a JTree. This can be used
2790    * in Bean- or GUI-Builders to show something interesting.
2791    * 
2792    * @return a sample TreeModel that can be used in a JTree
2793    */
2794   protected static TreeModel getDefaultTreeModel()
2795   {
2796     DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root node");
2797     DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("Child node 1");
2798     DefaultMutableTreeNode child11 =
2799       new DefaultMutableTreeNode("Child node 1.1");
2800     DefaultMutableTreeNode child12 =
2801       new DefaultMutableTreeNode("Child node 1.2");
2802     DefaultMutableTreeNode child13 =
2803       new DefaultMutableTreeNode("Child node 1.3");
2804     DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("Child node 2");
2805     DefaultMutableTreeNode child21 =
2806       new DefaultMutableTreeNode("Child node 2.1");
2807     DefaultMutableTreeNode child22 =
2808       new DefaultMutableTreeNode("Child node 2.2");
2809     DefaultMutableTreeNode child23 =
2810       new DefaultMutableTreeNode("Child node 2.3");
2811     DefaultMutableTreeNode child24 =
2812       new DefaultMutableTreeNode("Child node 2.4");
2813
2814     DefaultMutableTreeNode child3 = new DefaultMutableTreeNode("Child node 3");
2815     root.add(child1);
2816     root.add(child2);
2817     root.add(child3);
2818     child1.add(child11);
2819     child1.add(child12);
2820     child1.add(child13);
2821     child2.add(child21);
2822     child2.add(child22);
2823     child2.add(child23);
2824     child2.add(child24);
2825     return new DefaultTreeModel(root);
2826   }
2827
2828   /**
2829    * Converts the specified value to a String. This is used by the renderers
2830    * of this JTree and its nodes.
2831    * 
2832    * This implementation simply returns <code>value.toString()</code> and
2833    * ignores all other parameters. Subclass this method to control the
2834    * conversion.
2835    * 
2836    * @param value the value that is converted to a String
2837    * @param selected indicates if that value is selected or not
2838    * @param expanded indicates if that value is expanded or not
2839    * @param leaf indicates if that value is a leaf node or not
2840    * @param row the row of the node
2841    * @param hasFocus indicates if that node has focus or not
2842    */
2843   public String convertValueToText(Object value, boolean selected,
2844                                    boolean expanded, boolean leaf, int row, boolean hasFocus)
2845   {
2846     return value.toString();
2847   }
2848
2849   /**
2850    * A String representation of this JTree. This is intended to be used for
2851    * debugging. The returned string may be empty but may not be
2852    * <code>null</code>.
2853    * 
2854    * @return a String representation of this JTree
2855    */
2856   protected String paramString()
2857   {
2858     // TODO: this is completely legal, but it would possibly be nice
2859     // to return some more content, like the tree structure, some properties
2860     // etc ...
2861     return "";
2862   }
2863
2864   /**
2865    * Returns all TreePath objects which are a descendants of the given path
2866    * and are exapanded at the moment of the execution of this method. If the
2867    * state of any node is beeing toggled while this method is executing this
2868    * change may be left unaccounted.
2869    * 
2870    * @param path The parent of this request
2871    *
2872    * @return An Enumeration containing TreePath objects
2873    */
2874   public Enumeration getExpandedDescendants(TreePath path)
2875   {
2876     Enumeration paths = nodeStates.keys();
2877     Vector relevantPaths = new Vector();
2878     while (paths.hasMoreElements())
2879       {
2880         TreePath nextPath = (TreePath) paths.nextElement();
2881         if (nodeStates.get(nextPath) == EXPANDED
2882             && path.isDescendant(nextPath))
2883           {
2884             relevantPaths.add(nextPath);
2885           }
2886       }
2887     return relevantPaths.elements();
2888   }
2889
2890   /**
2891    * Returns the next table element (beginning from the row
2892    * <code>startingRow</code> that starts with <code>prefix</code>.
2893    * Searching is done in the direction specified by <code>bias</code>.
2894    * 
2895    * @param prefix the prefix to search for in the cell values
2896    * @param startingRow the index of the row where to start searching from
2897    * @param bias the search direction, either {@link Position.Bias#Forward} or
2898    *        {@link Position.Bias#Backward}
2899    * 
2900    * @return the path to the found element or -1 if no such element has been
2901    *         found
2902    * 
2903    * @throws IllegalArgumentException if prefix is <code>null</code> or
2904    *         startingRow is not valid
2905    * 
2906    * @since 1.4
2907    */
2908   public TreePath getNextMatch(String prefix, int startingRow,
2909                                Position.Bias bias)
2910   {
2911     if (prefix == null)
2912       throw new IllegalArgumentException("The argument 'prefix' must not be"
2913                                          + " null.");
2914     if (startingRow < 0)
2915       throw new IllegalArgumentException("The argument 'startingRow' must not"
2916                                          + " be less than zero.");
2917
2918     int size = getRowCount();
2919     if (startingRow > size)
2920       throw new IllegalArgumentException("The argument 'startingRow' must not"
2921                                          + " be greater than the number of"
2922                                          + " elements in the TreeModel.");
2923
2924     TreePath foundPath = null;
2925     if (bias == Position.Bias.Forward)
2926       {
2927         for (int i = startingRow; i < size; i++)
2928           {
2929             TreePath path = getPathForRow(i);
2930             Object o = path.getLastPathComponent();
2931             // FIXME: in the following call to convertValueToText the
2932             // last argument (hasFocus) should be done right.
2933             String item = convertValueToText(o, isRowSelected(i),
2934                                              isExpanded(i), treeModel.isLeaf(o),
2935                                              i, false);
2936             if (item.startsWith(prefix))
2937               {
2938                 foundPath = path;
2939                 break;
2940               }
2941           }
2942       }
2943     else
2944       {
2945         for (int i = startingRow; i >= 0; i--)
2946           {
2947             TreePath path = getPathForRow(i);
2948             Object o = path.getLastPathComponent();
2949             // FIXME: in the following call to convertValueToText the
2950             // last argument (hasFocus) should be done right.
2951             String item = convertValueToText(o, isRowSelected(i),
2952                                              isExpanded(i), treeModel.isLeaf(o), i, false);
2953             if (item.startsWith(prefix))
2954               {
2955                 foundPath = path;
2956                 break;
2957               }
2958           }
2959       }
2960     return foundPath;
2961   }
2962
2963   /**
2964    * Removes any paths in the current set of selected paths that are
2965    * descendants of <code>path</code>. If <code>includePath</code> is set
2966    * to <code>true</code> and <code>path</code> itself is selected, then
2967    * it will be removed too.
2968    * 
2969    * @param path the path from which selected descendants are to be removed
2970    * @param includeSelected if <code>true</code> then <code>path</code> itself
2971    *        will also be remove if it's selected
2972    * 
2973    * @return <code>true</code> if something has been removed,
2974    *         <code>false</code> otherwise
2975    * 
2976    * @since 1.3
2977    */
2978   protected boolean removeDescendantSelectedPaths(TreePath path,
2979                                                   boolean includeSelected)
2980   {
2981     boolean removedSomething = false;
2982     TreePath[] selected = getSelectionPaths();
2983     for (int index = 0; index < selected.length; index++)
2984       {
2985         if ((selected[index] == path && includeSelected)
2986             || (selected[index].isDescendant(path)))
2987           {
2988             removeSelectionPath(selected[index]);
2989             removedSomething = true;
2990           }
2991       }
2992     return removedSomething;
2993   }
2994   
2995   /**
2996    * Removes any descendants of the TreePaths in toRemove that have been 
2997    * expanded.
2998    * 
2999    * @param toRemove - Enumeration of TreePaths that need to be removed from
3000    * cache of toggled tree paths.
3001    */
3002   protected void removeDescendantToggledPaths(Enumeration toRemove)
3003   {
3004     while (toRemove.hasMoreElements())
3005       {
3006         TreePath current = (TreePath) toRemove.nextElement();
3007         Enumeration descendants = getDescendantToggledPaths(current);
3008         
3009         while (descendants.hasMoreElements())
3010           {
3011             TreePath currentDes = (TreePath) descendants.nextElement();
3012             if (isExpanded(currentDes))
3013                 nodeStates.remove(currentDes);
3014           }
3015       }
3016   }
3017
3018   /**
3019    * <p>
3020    * Sent when the tree has changed enough that we need to resize the bounds,
3021    * but not enough that we need to remove the expanded node set (e.g nodes were
3022    * expanded or collapsed, or nodes were inserted into the tree). You should
3023    * never have to invoke this, the UI will invoke this as it needs to.
3024    * </p>
3025    * <p>
3026    * If the tree uses {@link DefaultTreeModel}, you must call
3027    * {@link DefaultTreeModel#reload(TreeNode)} or
3028    * {@link DefaultTreeModel#reload()} after adding or removing nodes. Following
3029    * the official Java 1.5 API standard, just calling treeDidChange, repaint()
3030    * or revalidate() does <i>not</i> update the tree appearance properly.
3031    * 
3032    * @see DefaultTreeModel#reload()
3033    */
3034   public void treeDidChange()
3035   {
3036     repaint();
3037   }
3038
3039   /**
3040    * Helper method for
3041    * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
3042    * 
3043    * @param propertyName the name of the property
3044    * @param value the value of the property
3045    *
3046    * @throws IllegalArgumentException if the specified property cannot be set
3047    *         by this method
3048    * @throws ClassCastException if the property value does not match the
3049    *         property type
3050    * @throws NullPointerException if <code>c</code> or
3051    *         <code>propertyValue</code> is <code>null</code>
3052    */
3053   void setUIProperty(String propertyName, Object value)
3054   {
3055     if (propertyName.equals("rowHeight"))
3056       {
3057         if (! clientRowHeightSet)
3058           {
3059             setRowHeight(((Integer) value).intValue());
3060             clientRowHeightSet = false;
3061           }
3062       }
3063     else if (propertyName.equals("scrollsOnExpand"))
3064       {
3065         if (! clientScrollsOnExpandSet)
3066           {
3067             setScrollsOnExpand(((Boolean) value).booleanValue());
3068             clientScrollsOnExpandSet = false;
3069           }
3070       }
3071     else if (propertyName.equals("showsRootHandles"))
3072       {
3073         if (! clientShowsRootHandlesSet)
3074           {
3075             setShowsRootHandles(((Boolean) value).booleanValue());
3076             clientShowsRootHandlesSet = false;
3077           }
3078       }
3079     else
3080       {
3081         super.setUIProperty(propertyName, value);
3082       }
3083   }
3084 }