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