OSDN Git Service

Imported GNU Classpath 0.20
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / plaf / basic / BasicTreeUI.java
1 /* BasicTreeUI.java --
2  Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
3
4  This file is part of GNU Classpath.
5
6  GNU Classpath is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2, or (at your option)
9  any later version.
10
11  GNU Classpath is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  General Public License for more details.
15
16  You should have received a copy of the GNU General Public License
17  along with GNU Classpath; see the file COPYING.  If not, write to the
18  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  02110-1301 USA.
20
21  Linking this library statically or dynamically with other modules is
22  making a combined work based on this library.  Thus, the terms and
23  conditions of the GNU General Public License cover the whole
24  combination.
25
26  As a special exception, the copyright holders of this library give you
27  permission to link this library with independent modules to produce an
28  executable, regardless of the license terms of these independent
29  modules, and to copy and distribute the resulting executable under
30  terms of your choice, provided that you also meet, for each linked
31  independent module, the terms and conditions of the license of that
32  module.  An independent module is a module which is not derived from
33  or based on this library.  If you modify this library, you may extend
34  this exception to your version of the library, but you are not
35  obligated to do so.  If you do not wish to do so, delete this
36  exception statement from your version. */
37
38
39 package javax.swing.plaf.basic;
40
41 import java.awt.Color;
42 import java.awt.Component;
43 import java.awt.Dimension;
44 import java.awt.Font;
45 import java.awt.FontMetrics;
46 import java.awt.Graphics;
47 import java.awt.Insets;
48 import java.awt.Point;
49 import java.awt.Rectangle;
50 import java.awt.event.ActionEvent;
51 import java.awt.event.ActionListener;
52 import java.awt.event.ComponentAdapter;
53 import java.awt.event.ComponentEvent;
54 import java.awt.event.ComponentListener;
55 import java.awt.event.FocusEvent;
56 import java.awt.event.FocusListener;
57 import java.awt.event.KeyAdapter;
58 import java.awt.event.KeyEvent;
59 import java.awt.event.KeyListener;
60 import java.awt.event.MouseAdapter;
61 import java.awt.event.MouseEvent;
62 import java.awt.event.MouseListener;
63 import java.awt.event.MouseMotionListener;
64 import java.beans.PropertyChangeEvent;
65 import java.beans.PropertyChangeListener;
66 import java.util.Enumeration;
67 import java.util.Hashtable;
68
69 import javax.swing.AbstractAction;
70 import javax.swing.Action;
71 import javax.swing.ActionMap;
72 import javax.swing.CellRendererPane;
73 import javax.swing.Icon;
74 import javax.swing.InputMap;
75 import javax.swing.JComponent;
76 import javax.swing.JScrollBar;
77 import javax.swing.JScrollPane;
78 import javax.swing.JTextField;
79 import javax.swing.JTree;
80 import javax.swing.KeyStroke;
81 import javax.swing.LookAndFeel;
82 import javax.swing.SwingUtilities;
83 import javax.swing.Timer;
84 import javax.swing.UIManager;
85 import javax.swing.event.CellEditorListener;
86 import javax.swing.event.ChangeEvent;
87 import javax.swing.event.MouseInputListener;
88 import javax.swing.event.TreeExpansionEvent;
89 import javax.swing.event.TreeExpansionListener;
90 import javax.swing.event.TreeModelEvent;
91 import javax.swing.event.TreeModelListener;
92 import javax.swing.event.TreeSelectionEvent;
93 import javax.swing.event.TreeSelectionListener;
94 import javax.swing.plaf.ActionMapUIResource;
95 import javax.swing.plaf.ComponentUI;
96 import javax.swing.plaf.InputMapUIResource;
97 import javax.swing.plaf.TreeUI;
98 import javax.swing.text.Caret;
99 import javax.swing.tree.AbstractLayoutCache;
100 import javax.swing.tree.DefaultTreeCellEditor;
101 import javax.swing.tree.DefaultTreeCellRenderer;
102 import javax.swing.tree.FixedHeightLayoutCache;
103 import javax.swing.tree.TreeCellEditor;
104 import javax.swing.tree.TreeCellRenderer;
105 import javax.swing.tree.TreeModel;
106 import javax.swing.tree.TreeNode;
107 import javax.swing.tree.TreePath;
108 import javax.swing.tree.TreeSelectionModel;
109
110 /**
111  * A delegate providing the user interface for <code>JTree</code> according to
112  * the Basic look and feel.
113  * 
114  * @see javax.swing.JTree
115  * @author Lillian Angel (langel@redhat.com)
116  * @author Sascha Brawer (brawer@dandelis.ch)
117  */
118 public class BasicTreeUI extends TreeUI
119 {
120   /** Collapse Icon for the tree. */
121   protected transient Icon collapsedIcon;
122
123   /** Expanded Icon for the tree. */
124   protected transient Icon expandedIcon;
125
126   /** Distance between left margin and where vertical dashes will be drawn. */
127   protected int leftChildIndent;
128
129   /**
130    * Distance between leftChildIndent and where cell contents will be drawn.
131    */
132   protected int rightChildIndent;
133
134   /**
135    * Total fistance that will be indented. The sum of leftChildIndent and
136    * rightChildIndent .
137    */
138   protected int totalChildIndent;
139
140   /** Index of the row that was last selected. */
141   protected int lastSelectedRow;
142
143   /** Component that we're going to be drawing onto. */
144   protected JTree tree;
145
146   /** Renderer that is being used to do the actual cell drawing. */
147   protected transient TreeCellRenderer currentCellRenderer;
148
149   /**
150    * Set to true if the renderer that is currently in the tree was created by
151    * this instance.
152    */
153   protected boolean createdRenderer;
154
155   /** Editor for the tree. */
156   protected transient TreeCellEditor cellEditor;
157
158   /**
159    * Set to true if editor that is currently in the tree was created by this
160    * instance.
161    */
162   protected boolean createdCellEditor;
163
164   /**
165    * Set to false when editing and shouldSelectCall() returns true meaning the
166    * node should be selected before editing, used in completeEditing.
167    */
168   protected boolean stopEditingInCompleteEditing;
169
170   /** Used to paint the TreeCellRenderer. */
171   protected CellRendererPane rendererPane;
172
173   /** Size needed to completely display all the nodes. */
174   protected Dimension preferredSize;
175
176   /** Minimum size needed to completely display all the nodes. */
177   protected Dimension preferredMinSize;
178
179   /** Is the preferredSize valid? */
180   protected boolean validCachedPreferredSize;
181
182   /** Object responsible for handling sizing and expanded issues. */
183   protected AbstractLayoutCache treeState;
184
185   /** Used for minimizing the drawing of vertical lines. */
186   protected Hashtable drawingCache;
187
188   /**
189    * True if doing optimizations for a largeModel. Subclasses that don't support
190    * this may wish to override createLayoutCache to not return a
191    * FixedHeightLayoutCache instance.
192    */
193   protected boolean largeModel;
194
195   /** Responsible for telling the TreeState the size needed for a node. */
196   protected AbstractLayoutCache.NodeDimensions nodeDimensions;
197
198   /** Used to determine what to display. */
199   protected TreeModel treeModel;
200
201   /** Model maintaining the selection. */
202   protected TreeSelectionModel treeSelectionModel;
203
204   /**
205    * How much the depth should be offset to properly calculate x locations. This
206    * is based on whether or not the root is visible, and if the root handles are
207    * visible.
208    */
209   protected int depthOffset;
210
211   /**
212    * When editing, this will be the Component that is doing the actual editing.
213    */
214   protected Component editingComponent;
215
216   /** Path that is being edited. */
217   protected TreePath editingPath;
218
219   /**
220    * Row that is being edited. Should only be referenced if editingComponent is
221    * null.
222    */
223   protected int editingRow;
224
225   /** Set to true if the editor has a different size than the renderer. */
226   protected boolean editorHasDifferentSize;
227
228   /** The action listener for the editor's Timer. */
229   Timer editorTimer = new EditorUpdateTimer();
230
231   /** The new value of the node after editing. */
232   Object newVal;
233
234   /** The action bound to KeyStrokes. */
235   TreeAction action;
236
237   /** Boolean to keep track of editing. */
238   boolean isEditing;
239
240   /** The current path of the visible nodes in the tree. */
241   TreePath currentVisiblePath;
242
243   /** The gap between the icon and text. */
244   int gap = 4;
245
246   /** The max height of the nodes in the tree. */
247   int maxHeight = 0;
248
249   /** Listeners */
250   private PropertyChangeListener propertyChangeListener;
251
252   private FocusListener focusListener;
253
254   private TreeSelectionListener treeSelectionListener;
255
256   private MouseListener mouseListener;
257
258   private KeyListener keyListener;
259
260   private PropertyChangeListener selectionModelPropertyChangeListener;
261
262   private ComponentListener componentListener;
263
264   CellEditorListener cellEditorListener;
265
266   private TreeExpansionListener treeExpansionListener;
267
268   private TreeModelListener treeModelListener;
269
270   /**
271    * Creates a new BasicTreeUI object.
272    */
273   public BasicTreeUI()
274   {
275     validCachedPreferredSize = false;
276     drawingCache = new Hashtable();
277     nodeDimensions = createNodeDimensions();
278     configureLayoutCache();
279
280     propertyChangeListener = createPropertyChangeListener();
281     focusListener = createFocusListener();
282     treeSelectionListener = createTreeSelectionListener();
283     mouseListener = createMouseListener();
284     keyListener = createKeyListener();
285     selectionModelPropertyChangeListener = createSelectionModelPropertyChangeListener();
286     componentListener = createComponentListener();
287     cellEditorListener = createCellEditorListener();
288     treeExpansionListener = createTreeExpansionListener();
289     treeModelListener = createTreeModelListener();
290
291     editingRow = -1;
292     lastSelectedRow = -1;
293   }
294
295   /**
296    * Returns an instance of the UI delegate for the specified component.
297    * 
298    * @param c
299    *          the <code>JComponent</code> for which we need a UI delegate for.
300    * @return the <code>ComponentUI</code> for c.
301    */
302   public static ComponentUI createUI(JComponent c)
303   {
304     return new BasicTreeUI();
305   }
306
307   /**
308    * Returns the Hash color.
309    * 
310    * @return the <code>Color</code> of the Hash.
311    */
312   protected Color getHashColor()
313   {
314     return UIManager.getColor("Tree.hash");
315   }
316
317   /**
318    * Sets the Hash color.
319    * 
320    * @param color
321    *          the <code>Color</code> to set the Hash to.
322    */
323   protected void setHashColor(Color color)
324   {
325     // FIXME: Putting something in the UIDefaults map is certainly wrong.
326     UIManager.put("Tree.hash", color);
327   }
328
329   /**
330    * Sets the left child's indent value.
331    * 
332    * @param newAmount
333    *          is the new indent value for the left child.
334    */
335   public void setLeftChildIndent(int newAmount)
336   {
337     leftChildIndent = newAmount;
338   }
339
340   /**
341    * Returns the indent value for the left child.
342    * 
343    * @return the indent value for the left child.
344    */
345   public int getLeftChildIndent()
346   {
347     return leftChildIndent;
348   }
349
350   /**
351    * Sets the right child's indent value.
352    * 
353    * @param newAmount
354    *          is the new indent value for the right child.
355    */
356   public void setRightChildIndent(int newAmount)
357   {
358     rightChildIndent = newAmount;
359   }
360
361   /**
362    * Returns the indent value for the right child.
363    * 
364    * @return the indent value for the right child.
365    */
366   public int getRightChildIndent()
367   {
368     return rightChildIndent;
369   }
370
371   /**
372    * Sets the expanded icon.
373    * 
374    * @param newG
375    *          is the new expanded icon.
376    */
377   public void setExpandedIcon(Icon newG)
378   {
379     expandedIcon = newG;
380   }
381
382   /**
383    * Returns the current expanded icon.
384    * 
385    * @return the current expanded icon.
386    */
387   public Icon getExpandedIcon()
388   {
389     return expandedIcon;
390   }
391
392   /**
393    * Sets the collapsed icon.
394    * 
395    * @param newG
396    *          is the new collapsed icon.
397    */
398   public void setCollapsedIcon(Icon newG)
399   {
400     collapsedIcon = newG;
401   }
402
403   /**
404    * Returns the current collapsed icon.
405    * 
406    * @return the current collapsed icon.
407    */
408   public Icon getCollapsedIcon()
409   {
410     return collapsedIcon;
411   }
412
413   /**
414    * Updates the componentListener, if necessary.
415    * 
416    * @param largeModel
417    *          sets this.largeModel to it.
418    */
419   protected void setLargeModel(boolean largeModel)
420   {
421     if (largeModel != this.largeModel)
422       {
423         tree.removeComponentListener(componentListener);
424         this.largeModel = largeModel;
425         tree.addComponentListener(componentListener);
426       }
427   }
428
429   /**
430    * Returns true if largeModel is set
431    * 
432    * @return true if largeModel is set, otherwise false.
433    */
434   protected boolean isLargeModel()
435   {
436     return largeModel;
437   }
438
439   /**
440    * Sets the row height.
441    * 
442    * @param rowHeight
443    *          is the height to set this.rowHeight to.
444    */
445   protected void setRowHeight(int rowHeight)
446   {
447     if (rowHeight == 0)
448       rowHeight = Math.max(getMaxHeight(tree), 20);
449     treeState.setRowHeight(rowHeight);
450   }
451
452   /**
453    * Returns the current row height.
454    * 
455    * @return current row height.
456    */
457   protected int getRowHeight()
458   {
459     return treeState.getRowHeight();
460   }
461
462   /**
463    * Sets the TreeCellRenderer to <code>tcr</code>. This invokes
464    * <code>updateRenderer</code>.
465    * 
466    * @param tcr
467    *          is the new TreeCellRenderer.
468    */
469   protected void setCellRenderer(TreeCellRenderer tcr)
470   {
471     currentCellRenderer = tcr;
472     updateRenderer();
473   }
474
475   /**
476    * Return currentCellRenderer, which will either be the trees renderer, or
477    * defaultCellRenderer, which ever was not null.
478    * 
479    * @return the current Cell Renderer
480    */
481   protected TreeCellRenderer getCellRenderer()
482   {
483     if (currentCellRenderer != null)
484       return currentCellRenderer;
485
486     return createDefaultCellRenderer();
487   }
488
489   /**
490    * Sets the tree's model.
491    * 
492    * @param model
493    *          to set the treeModel to.
494    */
495   protected void setModel(TreeModel model)
496   {
497     tree.setModel(model);
498     treeModel = tree.getModel();
499   }
500
501   /**
502    * Returns the tree's model
503    * 
504    * @return treeModel
505    */
506   protected TreeModel getModel()
507   {
508     return treeModel;
509   }
510
511   /**
512    * Sets the root to being visible.
513    * 
514    * @param newValue
515    *          sets the visibility of the root
516    */
517   protected void setRootVisible(boolean newValue)
518   {
519     tree.setRootVisible(newValue);
520   }
521
522   /**
523    * Returns true if the root is visible.
524    * 
525    * @return true if the root is visible.
526    */
527   protected boolean isRootVisible()
528   {
529     return tree.isRootVisible();
530   }
531
532   /**
533    * Determines whether the node handles are to be displayed.
534    * 
535    * @param newValue
536    *          sets whether or not node handles should be displayed.
537    */
538   protected void setShowsRootHandles(boolean newValue)
539   {
540     tree.setShowsRootHandles(newValue);
541   }
542
543   /**
544    * Returns true if the node handles are to be displayed.
545    * 
546    * @return true if the node handles are to be displayed.
547    */
548   protected boolean getShowsRootHandles()
549   {
550     return tree.getShowsRootHandles();
551   }
552
553   /**
554    * Sets the cell editor.
555    * 
556    * @param editor
557    *          to set the cellEditor to.
558    */
559   protected void setCellEditor(TreeCellEditor editor)
560   {
561     cellEditor = editor;
562     createdCellEditor = true;
563   }
564
565   /**
566    * Returns the <code>TreeCellEditor</code> for this tree.
567    * 
568    * @return the cellEditor for this tree.
569    */
570   protected TreeCellEditor getCellEditor()
571   {
572     return cellEditor;
573   }
574
575   /**
576    * Configures the receiver to allow, or not allow, editing.
577    * 
578    * @param newValue
579    *          sets the receiver to allow editing if true.
580    */
581   protected void setEditable(boolean newValue)
582   {
583     tree.setEditable(newValue);
584   }
585
586   /**
587    * Returns true if the receiver allows editing.
588    * 
589    * @return true if the receiver allows editing.
590    */
591   protected boolean isEditable()
592   {
593     return tree.isEditable();
594   }
595
596   /**
597    * Resets the selection model. The appropriate listeners are installed on the
598    * model.
599    * 
600    * @param newLSM
601    *          resets the selection model.
602    */
603   protected void setSelectionModel(TreeSelectionModel newLSM)
604   {
605     if (newLSM != null)
606       {
607         treeSelectionModel = newLSM;
608         tree.setSelectionModel(treeSelectionModel);
609       }
610   }
611
612   /**
613    * Returns the current selection model.
614    * 
615    * @return the current selection model.
616    */
617   protected TreeSelectionModel getSelectionModel()
618   {
619     return treeSelectionModel;
620   }
621
622   /**
623    * Returns the Rectangle enclosing the label portion that the last item in
624    * path will be drawn to. Will return null if any component in path is
625    * currently valid.
626    * 
627    * @param tree
628    *          is the current tree the path will be drawn to.
629    * @param path
630    *          is the current path the tree to draw to.
631    * @return the Rectangle enclosing the label portion that the last item in the
632    *         path will be drawn to.
633    */
634   public Rectangle getPathBounds(JTree tree, TreePath path)
635   {
636     int row = -1;
637     Object cell = null;
638     if (path != null)
639       {
640         row = getRowForPath(tree, path);
641         cell = path.getLastPathComponent();
642       }
643     return nodeDimensions.getNodeDimensions(cell, row, getLevel(cell),
644                                             tree.isExpanded(path),
645                                             new Rectangle());
646   }
647
648   /**
649    * Returns the max height of all the nodes in the tree.
650    * 
651    * @param tree -
652    *          the current tree
653    * @return the max height.
654    */
655   private int getMaxHeight(JTree tree)
656   {
657     if (maxHeight != 0)
658       return maxHeight;
659
660     Icon e = UIManager.getIcon("Tree.openIcon");
661     Icon c = UIManager.getIcon("Tree.closedIcon");
662     Icon l = UIManager.getIcon("Tree.leafIcon");
663     int rc = getRowCount(tree);
664     int iconHeight = 0;
665
666     for (int row = 0; row < rc; row++)
667       {
668         if (isLeaf(row))
669           iconHeight = l.getIconHeight();
670         else if (tree.isExpanded(row))
671           iconHeight = e.getIconHeight();
672         else
673           iconHeight = c.getIconHeight();
674
675         maxHeight = Math.max(maxHeight, iconHeight + gap);
676       }
677
678     return maxHeight;
679   }
680
681   /**
682    * Returns the path for passed in row. If row is not visible null is returned.
683    * 
684    * @param tree
685    *          is the current tree to return path for.
686    * @param row
687    *          is the row number of the row to return.
688    * @return the path for passed in row. If row is not visible null is returned.
689    */
690   public TreePath getPathForRow(JTree tree, int row)
691   {
692     if (treeModel != null && currentVisiblePath != null)
693       {
694         Object[] nodes = currentVisiblePath.getPath();
695         if (row < nodes.length)
696           return new TreePath(getPathToRoot(nodes[row], 0));
697       }
698     return null;
699   }
700
701   /**
702    * Returns the row that the last item identified in path is visible at. Will
703    * return -1 if any of the elments in the path are not currently visible.
704    * 
705    * @param tree
706    *          is the current tree to return the row for.
707    * @param path
708    *          is the path used to find the row.
709    * @return the row that the last item identified in path is visible at. Will
710    *         return -1 if any of the elments in the path are not currently
711    *         visible.
712    */
713   public int getRowForPath(JTree tree, TreePath path)
714   {
715     int row = 0;
716     Object dest = path.getLastPathComponent();
717     int rowCount = getRowCount(tree);
718     if (currentVisiblePath != null)
719       {
720         Object[] nodes = currentVisiblePath.getPath();
721         while (row < rowCount)
722           {
723             if (dest.equals(nodes[row]))
724               return row;
725             row++;
726           }
727       }
728     return -1;
729   }
730
731   /**
732    * Returns the number of rows that are being displayed.
733    * 
734    * @param tree
735    *          is the current tree to return the number of rows for.
736    * @return the number of rows being displayed.
737    */
738   public int getRowCount(JTree tree)
739   {
740     if (currentVisiblePath != null)
741       return currentVisiblePath.getPathCount();
742     return 0;
743   }
744
745   /**
746    * Returns the path to the node that is closest to x,y. If there is nothing
747    * currently visible this will return null, otherwise it'll always return a
748    * valid path. If you need to test if the returned object is exactly at x,y
749    * you should get the bounds for the returned path and test x,y against that.
750    * 
751    * @param tree
752    *          the tree to search for the closest path
753    * @param x
754    *          is the x coordinate of the location to search
755    * @param y
756    *          is the y coordinate of the location to search
757    * @return the tree path closes to x,y.
758    */
759   public TreePath getClosestPathForLocation(JTree tree, int x, int y)
760   {
761     int row = Math.round(y / getMaxHeight(tree));
762     TreePath path = getPathForRow(tree, row);
763
764     // no row is visible at this node
765     while (row > 0 && path == null)
766       {
767         --row;
768         path = getPathForRow(tree, row);
769       }
770
771     return path;
772   }
773
774   /**
775    * Returns true if the tree is being edited. The item that is being edited can
776    * be returned by getEditingPath().
777    * 
778    * @param tree
779    *          is the tree to check for editing.
780    * @return true if the tree is being edited.
781    */
782   public boolean isEditing(JTree tree)
783   {
784     return isEditing;
785   }
786
787   /**
788    * Stops the current editing session. This has no effect if the tree is not
789    * being edited. Returns true if the editor allows the editing session to
790    * stop.
791    * 
792    * @param tree
793    *          is the tree to stop the editing on
794    * @return true if the editor allows the editing session to stop.
795    */
796   public boolean stopEditing(JTree tree)
797   {
798     if (isEditing(tree))
799       completeEditing(true, false, false);
800     return !isEditing(tree);
801   }
802
803   /**
804    * Cancels the current editing session.
805    * 
806    * @param tree
807    *          is the tree to cancel the editing session on.
808    */
809   public void cancelEditing(JTree tree)
810   {
811     if (isEditing(tree))
812       completeEditing(false, true, false);
813   }
814
815   /**
816    * Selects the last item in path and tries to edit it. Editing will fail if
817    * the CellEditor won't allow it for the selected item.
818    * 
819    * @param tree
820    *          is the tree to edit on.
821    * @param path
822    *          is the path in tree to edit on.
823    */
824   public void startEditingAtPath(JTree tree, TreePath path)
825   {
826     startEditing(path, null);
827   }
828
829   /**
830    * Returns the path to the element that is being editted.
831    * 
832    * @param tree
833    *          is the tree to get the editing path from.
834    * @return the path that is being edited.
835    */
836   public TreePath getEditingPath(JTree tree)
837   {
838     return editingPath;
839   }
840
841   /**
842    * Invoked after the tree instance variable has been set, but before any
843    * default/listeners have been installed.
844    */
845   protected void prepareForUIInstall()
846   {
847     // TODO: Implement this properly.
848   }
849
850   /**
851    * Invoked from installUI after all the defaults/listeners have been
852    * installed.
853    */
854   protected void completeUIInstall()
855   {
856     // TODO: Implement this properly.
857   }
858
859   /**
860    * Invoked from uninstallUI after all the defaults/listeners have been
861    * uninstalled.
862    */
863   protected void completeUIUninstall()
864   {
865     // TODO: Implement this properly.
866   }
867
868   /**
869    * Installs the subcomponents of the tree, which is the renderer pane.
870    */
871   protected void installComponents()
872   {
873     currentCellRenderer = createDefaultCellRenderer();
874     rendererPane = createCellRendererPane();
875     createdRenderer = true;
876     setCellRenderer(currentCellRenderer);
877   }
878
879   /**
880    * Creates an instance of NodeDimensions that is able to determine the size of
881    * a given node in the tree.
882    * 
883    * @return the NodeDimensions of a given node in the tree
884    */
885   protected AbstractLayoutCache.NodeDimensions createNodeDimensions()
886   {
887     return new NodeDimensionsHandler();
888   }
889
890   /**
891    * Creates a listener that is reponsible for the updates the UI based on how
892    * the tree changes.
893    * 
894    * @return the PropertyChangeListener that is reposnsible for the updates
895    */
896   protected PropertyChangeListener createPropertyChangeListener()
897   {
898     return new PropertyChangeHandler();
899   }
900
901   /**
902    * Creates the listener responsible for updating the selection based on mouse
903    * events.
904    * 
905    * @return the MouseListener responsible for updating.
906    */
907   protected MouseListener createMouseListener()
908   {
909     return new MouseHandler();
910   }
911
912   /**
913    * Creates the listener that is responsible for updating the display when
914    * focus is lost/grained.
915    * 
916    * @return the FocusListener responsible for updating.
917    */
918   protected FocusListener createFocusListener()
919   {
920     return new FocusHandler();
921   }
922
923   /**
924    * Creates the listener reponsible for getting key events from the tree.
925    * 
926    * @return the KeyListener responsible for getting key events.
927    */
928   protected KeyListener createKeyListener()
929   {
930     return new KeyHandler();
931   }
932
933   /**
934    * Creates the listener responsible for getting property change events from
935    * the selection model.
936    * 
937    * @returns the PropertyChangeListener reponsible for getting property change
938    *          events from the selection model.
939    */
940   protected PropertyChangeListener createSelectionModelPropertyChangeListener()
941   {
942     return new SelectionModelPropertyChangeHandler();
943   }
944
945   /**
946    * Creates the listener that updates the display based on selection change
947    * methods.
948    * 
949    * @return the TreeSelectionListener responsible for updating.
950    */
951   protected TreeSelectionListener createTreeSelectionListener()
952   {
953     return new TreeSelectionHandler();
954   }
955
956   /**
957    * Creates a listener to handle events from the current editor
958    * 
959    * @return the CellEditorListener that handles events from the current editor
960    */
961   protected CellEditorListener createCellEditorListener()
962   {
963     return new CellEditorHandler();
964   }
965
966   /**
967    * Creates and returns a new ComponentHandler. This is used for the large
968    * model to mark the validCachedPreferredSize as invalid when the component
969    * moves.
970    * 
971    * @return a new ComponentHandler.
972    */
973   protected ComponentListener createComponentListener()
974   {
975     return new ComponentHandler();
976   }
977
978   /**
979    * Creates and returns the object responsible for updating the treestate when
980    * a nodes expanded state changes.
981    * 
982    * @return the TreeExpansionListener responsible for updating the treestate
983    */
984   protected TreeExpansionListener createTreeExpansionListener()
985   {
986     return new TreeExpansionHandler();
987   }
988
989   /**
990    * Creates the object responsible for managing what is expanded, as well as
991    * the size of nodes.
992    * 
993    * @return the object responsible for managing what is expanded.
994    */
995   protected AbstractLayoutCache createLayoutCache()
996   {
997     return new FixedHeightLayoutCache();
998   }
999
1000   /**
1001    * Returns the renderer pane that renderer components are placed in.
1002    * 
1003    * @return the rendererpane that render components are placed in.
1004    */
1005   protected CellRendererPane createCellRendererPane()
1006   {
1007     return new CellRendererPane();
1008   }
1009
1010   /**
1011    * Creates a default cell editor.
1012    * 
1013    * @return the default cell editor.
1014    */
1015   protected TreeCellEditor createDefaultCellEditor()
1016   {
1017     if (currentCellRenderer != null)
1018       return new DefaultTreeCellEditor(
1019                                        tree,
1020                                        (DefaultTreeCellRenderer) currentCellRenderer,
1021                                        cellEditor);
1022     return new DefaultTreeCellEditor(
1023                                      tree,
1024                                      (DefaultTreeCellRenderer) createDefaultCellRenderer(),
1025                                      cellEditor);
1026   }
1027
1028   /**
1029    * Returns the default cell renderer that is used to do the stamping of each
1030    * node.
1031    * 
1032    * @return the default cell renderer that is used to do the stamping of each
1033    *         node.
1034    */
1035   protected TreeCellRenderer createDefaultCellRenderer()
1036   {
1037     return new DefaultTreeCellRenderer();
1038   }
1039
1040   /**
1041    * Returns a listener that can update the tree when the model changes.
1042    * 
1043    * @return a listener that can update the tree when the model changes.
1044    */
1045   protected TreeModelListener createTreeModelListener()
1046   {
1047     return new TreeModelHandler();
1048   }
1049
1050   /**
1051    * Uninstall all registered listeners
1052    */
1053   protected void uninstallListeners()
1054   {
1055     tree.removePropertyChangeListener(propertyChangeListener);
1056     tree.removeFocusListener(focusListener);
1057     tree.removeTreeSelectionListener(treeSelectionListener);
1058     tree.removeMouseListener(mouseListener);
1059     tree.removeKeyListener(keyListener);
1060     tree.removePropertyChangeListener(selectionModelPropertyChangeListener);
1061     tree.removeComponentListener(componentListener);
1062     tree.removeTreeExpansionListener(treeExpansionListener);
1063
1064     TreeCellEditor tce = tree.getCellEditor();
1065     if (tce != null)
1066       tce.removeCellEditorListener(cellEditorListener);
1067     if (treeModel != null)
1068       treeModel.removeTreeModelListener(treeModelListener);
1069   }
1070
1071   /**
1072    * Uninstall all keyboard actions.
1073    */
1074   protected void uninstallKeyboardActions()
1075   {
1076     action = null;
1077     tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).setParent(
1078                                                                               null);
1079     tree.getActionMap().setParent(null);
1080   }
1081
1082   /**
1083    * Uninstall the rendererPane.
1084    */
1085   protected void uninstallComponents()
1086   {
1087     currentCellRenderer = null;
1088     rendererPane = null;
1089     createdRenderer = false;
1090     setCellRenderer(currentCellRenderer);
1091   }
1092
1093   /**
1094    * The vertical element of legs between nodes starts at the bottom of the
1095    * parent node by default. This method makes the leg start below that.
1096    * 
1097    * @return the vertical leg buffer
1098    */
1099   protected int getVerticalLegBuffer()
1100   {
1101     return getRowHeight() / 2;
1102   }
1103
1104   /**
1105    * The horizontal element of legs between nodes starts at the right of the
1106    * left-hand side of the child node by default. This method makes the leg end
1107    * before that.
1108    * 
1109    * @return the horizontal leg buffer
1110    */
1111   protected int getHorizontalLegBuffer()
1112   {
1113     return rightChildIndent / 2;
1114   }
1115
1116   /**
1117    * Make all the nodes that are expanded in JTree expanded in LayoutCache. This
1118    * invokes updateExpandedDescendants with the root path.
1119    */
1120   protected void updateLayoutCacheExpandedNodes()
1121   {
1122     if (treeModel != null)
1123       updateExpandedDescendants(new TreePath(treeModel.getRoot()));
1124   }
1125
1126   /**
1127    * Updates the expanded state of all the descendants of the <code>path</code>
1128    * by getting the expanded descendants from the tree and forwarding to the
1129    * tree state.
1130    * 
1131    * @param path
1132    *          the path used to update the expanded states
1133    */
1134   protected void updateExpandedDescendants(TreePath path)
1135   {
1136     Enumeration expanded = tree.getExpandedDescendants(path);
1137     while (expanded.hasMoreElements())
1138       treeState.setExpandedState(((TreePath) expanded.nextElement()), true);
1139   }
1140
1141   /**
1142    * Returns a path to the last child of <code>parent</code>
1143    * 
1144    * @param parent
1145    *          is the topmost path to specified
1146    * @return a path to the last child of parent
1147    */
1148   protected TreePath getLastChildPath(TreePath parent)
1149   {
1150     return ((TreePath) parent.getLastPathComponent());
1151   }
1152
1153   /**
1154    * Updates how much each depth should be offset by.
1155    */
1156   protected void updateDepthOffset()
1157   {
1158     depthOffset += getVerticalLegBuffer();
1159   }
1160
1161   /**
1162    * Updates the cellEditor based on editability of the JTree that we're
1163    * contained in. If the tree is editable but doesn't have a cellEditor, a
1164    * basic one will be used.
1165    */
1166   protected void updateCellEditor()
1167   {
1168     if (tree.isEditable() && cellEditor == null)
1169       setCellEditor(createDefaultCellEditor());
1170     createdCellEditor = true;
1171   }
1172
1173   /**
1174    * Messaged from the tree we're in when the renderer has changed.
1175    */
1176   protected void updateRenderer()
1177   {
1178     if (tree != null)
1179       {
1180         if (tree.getCellRenderer() == null)
1181           {
1182             if (currentCellRenderer == null)
1183               currentCellRenderer = createDefaultCellRenderer();
1184             tree.setCellRenderer(currentCellRenderer);
1185           }
1186       }
1187   }
1188
1189   /**
1190    * Resets the treeState instance based on the tree we're providing the look
1191    * and feel for.
1192    */
1193   protected void configureLayoutCache()
1194   {
1195     treeState = createLayoutCache();
1196   }
1197
1198   /**
1199    * Marks the cached size as being invalid, and messages the tree with
1200    * <code>treeDidChange</code>.
1201    */
1202   protected void updateSize()
1203   {
1204     preferredSize = null;
1205     updateCachedPreferredSize();
1206     tree.treeDidChange();
1207   }
1208
1209   /**
1210    * Updates the <code>preferredSize</code> instance variable, which is
1211    * returned from <code>getPreferredSize()</code>.
1212    */
1213   protected void updateCachedPreferredSize()
1214   {
1215     int maxWidth = 0;
1216     boolean isLeaf = false;
1217     if (currentVisiblePath != null)
1218       {
1219         Object[] path = currentVisiblePath.getPath();
1220         for (int i = 0; i < path.length; i++)
1221           {
1222             TreePath curr = new TreePath(getPathToRoot(path[i], 0));
1223             Rectangle bounds = getPathBounds(tree, curr);
1224             if (treeModel != null)
1225               isLeaf = treeModel.isLeaf(path[i]);
1226             if (!isLeaf && hasControlIcons())
1227               bounds.width += getCurrentControlIcon(curr).getIconWidth();
1228             maxWidth = Math.max(maxWidth, bounds.x + bounds.width);
1229           }
1230
1231         maxHeight = 0;
1232         maxHeight = getMaxHeight(tree);
1233         preferredSize = new Dimension(maxWidth, (maxHeight * path.length));
1234       }
1235     else
1236       preferredSize = new Dimension(0, 0);
1237     validCachedPreferredSize = true;
1238   }
1239
1240   /**
1241    * Messaged from the VisibleTreeNode after it has been expanded.
1242    * 
1243    * @param path
1244    *          is the path that has been expanded.
1245    */
1246   protected void pathWasExpanded(TreePath path)
1247   {
1248     validCachedPreferredSize = false;
1249     tree.repaint();
1250   }
1251
1252   /**
1253    * Messaged from the VisibleTreeNode after it has collapsed
1254    */
1255   protected void pathWasCollapsed(TreePath path)
1256   {
1257     validCachedPreferredSize = false;
1258     tree.repaint();
1259   }
1260
1261   /**
1262    * Install all defaults for the tree.
1263    */
1264   protected void installDefaults()
1265   {
1266     LookAndFeel.installColorsAndFont(tree, "Tree.background",
1267                                      "Tree.foreground", "Tree.font");
1268     tree.setOpaque(true);
1269
1270     rightChildIndent = UIManager.getInt("Tree.rightChildIndent");
1271     leftChildIndent = UIManager.getInt("Tree.leftChildIndent");
1272     setRowHeight(UIManager.getInt("Tree.rowHeight"));
1273     tree.setRowHeight(getRowHeight());
1274     tree.requestFocusInWindow(false);
1275     tree.setScrollsOnExpand(UIManager.getBoolean("Tree.scrollsOnExpand"));
1276     setExpandedIcon(UIManager.getIcon("Tree.expandedIcon"));
1277     setCollapsedIcon(UIManager.getIcon("Tree.collapsedIcon"));
1278   }
1279
1280   /**
1281    * Install all keyboard actions for this
1282    */
1283   protected void installKeyboardActions()
1284   {
1285     InputMap focusInputMap = (InputMap) UIManager.get("Tree.focusInputMap");
1286     InputMapUIResource parentInputMap = new InputMapUIResource();
1287     ActionMap parentActionMap = new ActionMapUIResource();
1288     action = new TreeAction();
1289     Object keys[] = focusInputMap.allKeys();
1290
1291     for (int i = 0; i < keys.length; i++)
1292       {
1293         parentInputMap.put(
1294                            KeyStroke.getKeyStroke(
1295                                                   ((KeyStroke) keys[i]).getKeyCode(),
1296                                                   convertModifiers(((KeyStroke) keys[i]).getModifiers())),
1297                            (String) focusInputMap.get((KeyStroke) keys[i]));
1298
1299         parentInputMap.put(
1300                            KeyStroke.getKeyStroke(
1301                                                   ((KeyStroke) keys[i]).getKeyCode(),
1302                                                   ((KeyStroke) keys[i]).getModifiers()),
1303                            (String) focusInputMap.get((KeyStroke) keys[i]));
1304
1305         parentActionMap.put(
1306                             (String) focusInputMap.get((KeyStroke) keys[i]),
1307                             new ActionListenerProxy(
1308                                                     action,
1309                                                     (String) focusInputMap.get((KeyStroke) keys[i])));
1310
1311       }
1312
1313     parentInputMap.setParent(tree.getInputMap(
1314                                               JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).getParent());
1315     parentActionMap.setParent(tree.getActionMap().getParent());
1316     tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).setParent(
1317                                                                               parentInputMap);
1318     tree.getActionMap().setParent(parentActionMap);
1319   }
1320
1321   /**
1322    * Converts the modifiers.
1323    * 
1324    * @param mod -
1325    *          modifier to convert
1326    * @returns the new modifier
1327    */
1328   private int convertModifiers(int mod)
1329   {
1330     if ((mod & KeyEvent.SHIFT_DOWN_MASK) != 0)
1331       {
1332         mod |= KeyEvent.SHIFT_MASK;
1333         mod &= ~KeyEvent.SHIFT_DOWN_MASK;
1334       }
1335     if ((mod & KeyEvent.CTRL_DOWN_MASK) != 0)
1336       {
1337         mod |= KeyEvent.CTRL_MASK;
1338         mod &= ~KeyEvent.CTRL_DOWN_MASK;
1339       }
1340     if ((mod & KeyEvent.META_DOWN_MASK) != 0)
1341       {
1342         mod |= KeyEvent.META_MASK;
1343         mod &= ~KeyEvent.META_DOWN_MASK;
1344       }
1345     if ((mod & KeyEvent.ALT_DOWN_MASK) != 0)
1346       {
1347         mod |= KeyEvent.ALT_MASK;
1348         mod &= ~KeyEvent.ALT_DOWN_MASK;
1349       }
1350     if ((mod & KeyEvent.ALT_GRAPH_DOWN_MASK) != 0)
1351       {
1352         mod |= KeyEvent.ALT_GRAPH_MASK;
1353         mod &= ~KeyEvent.ALT_GRAPH_DOWN_MASK;
1354       }
1355     return mod;
1356   }
1357
1358   /**
1359    * Install all listeners for this
1360    */
1361   protected void installListeners()
1362   {
1363     tree.addPropertyChangeListener(propertyChangeListener);
1364     tree.addFocusListener(focusListener);
1365     tree.addTreeSelectionListener(treeSelectionListener);
1366     tree.addMouseListener(mouseListener);
1367     tree.addKeyListener(keyListener);
1368     tree.addPropertyChangeListener(selectionModelPropertyChangeListener);
1369     tree.addComponentListener(componentListener);
1370     tree.addTreeExpansionListener(treeExpansionListener);
1371     if (treeModel != null)
1372       treeModel.addTreeModelListener(treeModelListener);
1373   }
1374
1375   /**
1376    * Install the UI for the component
1377    * 
1378    * @param c
1379    *          the component to install UI for
1380    */
1381   public void installUI(JComponent c)
1382   {
1383     tree = (JTree) c;
1384     prepareForUIInstall();
1385     super.installUI(c);
1386     installDefaults();
1387
1388     installComponents();
1389     installKeyboardActions();
1390     installListeners();
1391
1392     setCellEditor(createDefaultCellEditor());
1393     createdCellEditor = true;
1394     isEditing = false;
1395
1396     setModel(tree.getModel());
1397     treeSelectionModel = tree.getSelectionModel();
1398
1399     completeUIInstall();
1400   }
1401
1402   /**
1403    * Uninstall the defaults for the tree
1404    */
1405   protected void uninstallDefaults()
1406   {
1407     tree.setFont(null);
1408     tree.setForeground(null);
1409     tree.setBackground(null);
1410   }
1411
1412   /**
1413    * Uninstall the UI for the component
1414    * 
1415    * @param c
1416    *          the component to uninstall UI for
1417    */
1418   public void uninstallUI(JComponent c)
1419   {
1420     prepareForUIUninstall();
1421     uninstallDefaults();
1422     uninstallKeyboardActions();
1423     uninstallListeners();
1424     tree = null;
1425     uninstallComponents();
1426     completeUIUninstall();
1427   }
1428
1429   /**
1430    * Paints the specified component appropriate for the look and feel. This
1431    * method is invoked from the ComponentUI.update method when the specified
1432    * component is being painted. Subclasses should override this method and use
1433    * the specified Graphics object to render the content of the component.
1434    * 
1435    * @param g
1436    *          the Graphics context in which to paint
1437    * @param c
1438    *          the component being painted; this argument is often ignored, but
1439    *          might be used if the UI object is stateless and shared by multiple
1440    *          components
1441    */
1442   public void paint(Graphics g, JComponent c)
1443   {
1444     JTree tree = (JTree) c;
1445     updateCurrentVisiblePath();
1446
1447     Rectangle clip = g.getClipBounds();
1448     Insets insets = tree.getInsets();
1449
1450     if (clip != null && treeModel != null && currentVisiblePath != null)
1451       {
1452         int startIndex = tree.getClosestRowForLocation(clip.x, clip.y);
1453         int endIndex = tree.getClosestRowForLocation(clip.x + clip.width,
1454                                                      clip.y + clip.height);
1455
1456         paintVerticalPartOfLeg(g, clip, insets, currentVisiblePath);
1457         for (int i = startIndex; i <= endIndex; i++)
1458           {
1459             Object curr = currentVisiblePath.getPathComponent(i);
1460             boolean isLeaf = treeModel.isLeaf(curr);
1461             TreePath path = new TreePath(getPathToRoot(curr, 0));
1462
1463             boolean isExpanded = tree.isExpanded(path);
1464             Rectangle bounds = getPathBounds(tree, path);
1465             paintHorizontalPartOfLeg(g, clip, insets, bounds, path, i,
1466                                      isExpanded, false, isLeaf);
1467             paintRow(g, clip, insets, bounds, path, i, isExpanded, false,
1468                      isLeaf);
1469           }
1470       }
1471   }
1472
1473   /**
1474    * Ensures that the rows identified by beginRow through endRow are visible.
1475    * 
1476    * @param beginRow
1477    *          is the first row
1478    * @param endRow
1479    *          is the last row
1480    */
1481   protected void ensureRowsAreVisible(int beginRow, int endRow)
1482   {
1483     if (beginRow < endRow)
1484       {
1485         int temp = endRow;
1486         endRow = beginRow;
1487         beginRow = temp;
1488       }
1489
1490     for (int i = beginRow; i < endRow; i++)
1491       {
1492         TreePath path = getPathForRow(tree, i);
1493         if (!tree.isVisible(path))
1494           tree.makeVisible(path);
1495       }
1496   }
1497
1498   /**
1499    * Sets the preferred minimum size.
1500    * 
1501    * @param newSize
1502    *          is the new preferred minimum size.
1503    */
1504   public void setPreferredMinSize(Dimension newSize)
1505   {
1506     preferredMinSize = newSize;
1507   }
1508
1509   /**
1510    * Gets the preferred minimum size.
1511    * 
1512    * @returns the preferred minimum size.
1513    */
1514   public Dimension getPreferredMinSize()
1515   {
1516     return preferredMinSize;
1517   }
1518
1519   /**
1520    * Returns the preferred size to properly display the tree, this is a cover
1521    * method for getPreferredSize(c, false).
1522    * 
1523    * @param c
1524    *          the component whose preferred size is being queried; this argument
1525    *          is often ignored but might be used if the UI object is stateless
1526    *          and shared by multiple components
1527    * @return the preferred size
1528    */
1529   public Dimension getPreferredSize(JComponent c)
1530   {
1531     return getPreferredSize(c, false);
1532   }
1533
1534   /**
1535    * Returns the preferred size to represent the tree in c. If checkConsistancy
1536    * is true, checkConsistancy is messaged first.
1537    * 
1538    * @param c
1539    *          the component whose preferred size is being queried.
1540    * @param checkConsistancy
1541    *          if true must check consistancy
1542    * @return the preferred size
1543    */
1544   public Dimension getPreferredSize(JComponent c, boolean checkConsistancy)
1545   {
1546     // FIXME: checkConsistancy not implemented, c not used
1547     if (!validCachedPreferredSize)
1548       updateCachedPreferredSize();
1549     return preferredSize;
1550   }
1551
1552   /**
1553    * Returns the minimum size for this component. Which will be the min
1554    * preferred size or (0,0).
1555    * 
1556    * @param c
1557    *          the component whose min size is being queried.
1558    * @returns the preferred size or null
1559    */
1560   public Dimension getMinimumSize(JComponent c)
1561   {
1562     Dimension min = getPreferredMinSize();
1563     if (min == null)
1564       return new Dimension();
1565     return min;
1566   }
1567
1568   /**
1569    * Returns the maximum size for the component, which will be the preferred
1570    * size if the instance is currently in JTree or (0,0).
1571    * 
1572    * @param c
1573    *          the component whose preferred size is being queried
1574    * @return the max size or null
1575    */
1576   public Dimension getMaximumSize(JComponent c)
1577   {
1578     if (c instanceof JTree)
1579       return ((JTree) c).getPreferredSize();
1580     return new Dimension();
1581   }
1582
1583   /**
1584    * Messages to stop the editing session. If the UI the receiver is providing
1585    * the look and feel for returns true from
1586    * <code>getInvokesStopCellEditing</code>, stopCellEditing will be invoked
1587    * on the current editor. Then completeEditing will be messaged with false,
1588    * true, false to cancel any lingering editing.
1589    */
1590   protected void completeEditing()
1591   {
1592     completeEditing(false, true, false);
1593   }
1594
1595   /**
1596    * Stops the editing session. If messageStop is true, the editor is messaged
1597    * with stopEditing, if messageCancel is true the editor is messaged with
1598    * cancelEditing. If messageTree is true, the treeModel is messaged with
1599    * valueForPathChanged.
1600    * 
1601    * @param messageStop
1602    *          message to stop editing
1603    * @param messageCancel
1604    *          message to cancel editing
1605    * @param messageTree
1606    *          message to treeModel
1607    */
1608   protected void completeEditing(boolean messageStop, boolean messageCancel,
1609                                  boolean messageTree)
1610   {
1611     if (messageStop)
1612       {
1613         getCellEditor().stopCellEditing();
1614         stopEditingInCompleteEditing = true;
1615       }
1616
1617     if (messageCancel)
1618       {
1619         getCellEditor().cancelCellEditing();
1620         stopEditingInCompleteEditing = true;
1621       }
1622
1623     if (messageTree)
1624       treeModel.valueForPathChanged(tree.getLeadSelectionPath(), newVal);
1625   }
1626
1627   /**
1628    * Will start editing for node if there is a cellEditor and shouldSelectCall
1629    * returns true. This assumes that path is valid and visible.
1630    * 
1631    * @param path
1632    *          is the path to start editing
1633    * @param event
1634    *          is the MouseEvent performed on the path
1635    * @return true if successful
1636    */
1637   protected boolean startEditing(TreePath path, MouseEvent event)
1638   {
1639     int x;
1640     int y;
1641     if (event == null)
1642       {
1643         Rectangle bounds = getPathBounds(tree, path);
1644         x = bounds.x;
1645         y = bounds.y;
1646       }
1647     else
1648       {
1649         x = event.getX();
1650         y = event.getY();
1651       }
1652
1653     updateCellEditor();
1654     TreeCellEditor ed = getCellEditor();
1655     if (ed != null && ed.shouldSelectCell(event) && ed.isCellEditable(event))
1656       {
1657         editingPath = path;
1658         editingRow = tree.getRowForPath(editingPath);
1659
1660         Object val = editingPath.getLastPathComponent();
1661         cellEditor.addCellEditorListener(cellEditorListener);
1662         stopEditingInCompleteEditing = false;
1663         boolean expanded = tree.isExpanded(editingPath);
1664         isEditing = true;
1665         editingComponent = ed.getTreeCellEditorComponent(tree, val, true,
1666                                                          expanded,
1667                                                          isLeaf(editingRow),
1668                                                          editingRow);
1669         editingComponent.getParent().setVisible(true);
1670         editingComponent.getParent().validate();
1671         tree.add(editingComponent.getParent());
1672         editingComponent.getParent().validate();
1673         validCachedPreferredSize = false;
1674
1675         ((JTextField) editingComponent).requestFocusInWindow(false);
1676         editorTimer.start();
1677         return true;
1678       }
1679     return false;
1680   }
1681
1682   /**
1683    * If the <code>mouseX</code> and <code>mouseY</code> are in the expand or
1684    * collapse region of the row, this will toggle the row.
1685    * 
1686    * @param path
1687    *          the path we are concerned with
1688    * @param mouseX
1689    *          is the cursor's x position
1690    * @param mouseY
1691    *          is the cursor's y position
1692    */
1693   protected void checkForClickInExpandControl(TreePath path, int mouseX,
1694                                               int mouseY)
1695   {
1696     if (isLocationInExpandControl(path, mouseX, mouseY))
1697       toggleExpandState(path);
1698   }
1699
1700   /**
1701    * Returns true if the <code>mouseX</code> and <code>mouseY</code> fall in
1702    * the area of row that is used to expand/collpse the node and the node at row
1703    * does not represent a leaf.
1704    * 
1705    * @param path
1706    *          the path we are concerned with
1707    * @param mouseX
1708    *          is the cursor's x position
1709    * @param mouseY
1710    *          is the cursor's y position
1711    * @return true if the <code>mouseX</code> and <code>mouseY</code> fall in
1712    *         the area of row that is used to expand/collpse the node and the
1713    *         node at row does not represent a leaf.
1714    */
1715   protected boolean isLocationInExpandControl(TreePath path, int mouseX,
1716                                               int mouseY)
1717   {
1718     boolean cntlClick = false;
1719     int row = getRowForPath(tree, path);
1720
1721     if (!isLeaf(row))
1722       {
1723         Rectangle bounds = getPathBounds(tree, path);
1724
1725         if (hasControlIcons()
1726             && (mouseX < bounds.x)
1727             && (mouseX > (bounds.x - getCurrentControlIcon(path).getIconWidth() - gap)))
1728           cntlClick = true;
1729       }
1730     return cntlClick;
1731   }
1732
1733   /**
1734    * Messaged when the user clicks the particular row, this invokes
1735    * toggleExpandState.
1736    * 
1737    * @param path
1738    *          the path we are concerned with
1739    * @param mouseX
1740    *          is the cursor's x position
1741    * @param mouseY
1742    *          is the cursor's y position
1743    */
1744   protected void handleExpandControlClick(TreePath path, int mouseX, int mouseY)
1745   {
1746     toggleExpandState(path);
1747   }
1748
1749   /**
1750    * Expands path if it is not expanded, or collapses row if it is expanded. If
1751    * expanding a path and JTree scroll on expand, ensureRowsAreVisible is
1752    * invoked to scroll as many of the children to visible as possible (tries to
1753    * scroll to last visible descendant of path).
1754    * 
1755    * @param path
1756    *          the path we are concerned with
1757    */
1758   protected void toggleExpandState(TreePath path)
1759   {
1760     if (tree.isExpanded(path))
1761       tree.collapsePath(path);
1762     else
1763       tree.expandPath(path);
1764   }
1765
1766   /**
1767    * Returning true signifies a mouse event on the node should toggle the
1768    * selection of only the row under the mouse.
1769    * 
1770    * @param event
1771    *          is the MouseEvent performed on the row.
1772    * @return true signifies a mouse event on the node should toggle the
1773    *         selection of only the row under the mouse.
1774    */
1775   protected boolean isToggleSelectionEvent(MouseEvent event)
1776   {
1777     return (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.SINGLE_TREE_SELECTION);
1778   }
1779
1780   /**
1781    * Returning true signifies a mouse event on the node should select from the
1782    * anchor point.
1783    * 
1784    * @param event
1785    *          is the MouseEvent performed on the node.
1786    * @return true signifies a mouse event on the node should select from the
1787    *         anchor point.
1788    */
1789   protected boolean isMultiSelectEvent(MouseEvent event)
1790   {
1791     return (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
1792   }
1793
1794   /**
1795    * Returning true indicates the row under the mouse should be toggled based on
1796    * the event. This is invoked after checkForClickInExpandControl, implying the
1797    * location is not in the expand (toggle) control.
1798    * 
1799    * @param event
1800    *          is the MouseEvent performed on the row.
1801    * @return true indicates the row under the mouse should be toggled based on
1802    *         the event.
1803    */
1804   protected boolean isToggleEvent(MouseEvent event)
1805   {
1806     return true;
1807   }
1808
1809   /**
1810    * Messaged to update the selection based on a MouseEvent over a particular
1811    * row. If the even is a toggle selection event, the row is either selected,
1812    * or deselected. If the event identifies a multi selection event, the
1813    * selection is updated from the anchor point. Otherwise, the row is selected,
1814    * and if the even specified a toggle event the row is expanded/collapsed.
1815    * 
1816    * @param path
1817    *          is the path selected for an event
1818    * @param event
1819    *          is the MouseEvent performed on the path.
1820    */
1821   protected void selectPathForEvent(TreePath path, MouseEvent event)
1822   {
1823     if (isToggleSelectionEvent(event))
1824       {
1825         if (tree.isPathSelected(path))
1826           tree.removeSelectionPath(path);
1827         else
1828           {
1829             tree.addSelectionPath(path);
1830             tree.setAnchorSelectionPath(path);
1831           }
1832       }
1833     else if (isMultiSelectEvent(event))
1834       {
1835         TreePath anchor = tree.getAnchorSelectionPath();
1836         if (anchor != null)
1837           {
1838             int aRow = getRowForPath(tree, anchor);
1839             tree.addSelectionInterval(aRow, getRowForPath(tree, path));
1840           }
1841         else
1842           tree.addSelectionPath(path);
1843       }
1844     else
1845       tree.addSelectionPath(path);
1846   }
1847
1848   /**
1849    * Returns true if the node at <code>row</code> is a leaf.
1850    * 
1851    * @param row
1852    *          is the row we are concerned with.
1853    * @return true if the node at <code>row</code> is a leaf.
1854    */
1855   protected boolean isLeaf(int row)
1856   {
1857     TreePath pathForRow = getPathForRow(tree, row);
1858     if (pathForRow == null)
1859       return true;
1860
1861     Object node = pathForRow.getLastPathComponent();
1862     return treeModel.isLeaf(node);
1863   }
1864
1865   /**
1866    * This class implements the actions that we want to happen when specific keys
1867    * are pressed for the JTree. The actionPerformed method is called when a key
1868    * that has been registered for the JTree is received.
1869    */
1870   class TreeAction extends AbstractAction
1871   {
1872
1873     /**
1874      * What to do when this action is called.
1875      * 
1876      * @param e
1877      *          the ActionEvent that caused this action.
1878      */
1879     public void actionPerformed(ActionEvent e)
1880     {
1881       TreePath lead = tree.getLeadSelectionPath();
1882
1883       if (e.getActionCommand().equals("selectPreviousChangeLead")
1884           || e.getActionCommand().equals("selectPreviousExtendSelection")
1885           || e.getActionCommand().equals("selectPrevious")
1886           || e.getActionCommand().equals("selectNext")
1887           || e.getActionCommand().equals("selectNextExtendSelection")
1888           || e.getActionCommand().equals("selectNextChangeLead"))
1889         (new TreeIncrementAction(0, "")).actionPerformed(e);
1890       else if (e.getActionCommand().equals("selectParent")
1891                || e.getActionCommand().equals("selectChild"))
1892         (new TreeTraverseAction(0, "")).actionPerformed(e);
1893       else if (e.getActionCommand().equals("selectAll"))
1894         {
1895           TreePath[] paths = new TreePath[tree.getVisibleRowCount()];
1896
1897           Object curr = getNextVisibleNode(treeModel.getRoot());
1898           int i = 0;
1899           while (curr != null && i < paths.length)
1900             {
1901               paths[i] = new TreePath(getPathToRoot(curr, 0));
1902               i++;
1903             }
1904
1905           tree.addSelectionPaths(paths);
1906         }
1907       else if (e.getActionCommand().equals("startEditing"))
1908         tree.startEditingAtPath(lead);
1909       else if (e.getActionCommand().equals("toggle"))
1910         {
1911           if (tree.isEditing())
1912             tree.stopEditing();
1913           else
1914             {
1915               Object last = lead.getLastPathComponent();
1916               TreePath path = new TreePath(getPathToRoot(last, 0));
1917               if (!treeModel.isLeaf(last))
1918                 toggleExpandState(path);
1919             }
1920         }
1921       else if (e.getActionCommand().equals("clearSelection"))
1922         tree.clearSelection();
1923
1924       if (tree.isEditing() && !e.getActionCommand().equals("startEditing"))
1925         tree.cancelEditing();
1926
1927       tree.scrollPathToVisible(lead);
1928     }
1929   }
1930
1931   /**
1932    * This class is used to mimic the behaviour of the JDK when registering
1933    * keyboard actions. It is the same as the private class used in JComponent
1934    * for the same reason. This class receives an action event and dispatches it
1935    * to the true receiver after altering the actionCommand property of the
1936    * event.
1937    */
1938   private static class ActionListenerProxy extends AbstractAction
1939   {
1940     ActionListener target;
1941
1942     String bindingCommandName;
1943
1944     public ActionListenerProxy(ActionListener li, String cmd)
1945     {
1946       target = li;
1947       bindingCommandName = cmd;
1948     }
1949
1950     public void actionPerformed(ActionEvent e)
1951     {
1952       ActionEvent derivedEvent = new ActionEvent(e.getSource(), e.getID(),
1953                                                  bindingCommandName,
1954                                                  e.getModifiers());
1955
1956       target.actionPerformed(derivedEvent);
1957     }
1958   }
1959
1960   /**
1961    * The timer that updates the editor component.
1962    */
1963   private class EditorUpdateTimer extends Timer implements ActionListener
1964   {
1965     /**
1966      * Creates a new EditorUpdateTimer object with a default delay of 0.3
1967      * seconds.
1968      */
1969     public EditorUpdateTimer()
1970     {
1971       super(300, null);
1972       addActionListener(this);
1973     }
1974
1975     /**
1976      * Lets the caret blink and repaints the table.
1977      */
1978     public void actionPerformed(ActionEvent ev)
1979     {
1980       Caret c = ((JTextField) editingComponent).getCaret();
1981       if (c != null)
1982         c.setVisible(!c.isVisible());
1983       tree.repaint();
1984     }
1985
1986     /**
1987      * Updates the blink delay according to the current caret.
1988      */
1989     public void update()
1990     {
1991       stop();
1992       Caret c = ((JTextField) editingComponent).getCaret();
1993       if (c != null)
1994         {
1995           setDelay(c.getBlinkRate());
1996           if (((JTextField) editingComponent).isEditable())
1997             start();
1998           else
1999             c.setVisible(false);
2000         }
2001     }
2002   }
2003
2004   /**
2005    * Updates the preferred size when scrolling, if necessary.
2006    */
2007   public class ComponentHandler extends ComponentAdapter implements
2008       ActionListener
2009   {
2010     /**
2011      * Timer used when inside a scrollpane and the scrollbar is adjusting
2012      */
2013     protected Timer timer;
2014
2015     /** ScrollBar that is being adjusted */
2016     protected JScrollBar scrollBar;
2017
2018     /**
2019      * Constructor
2020      */
2021     public ComponentHandler()
2022     {
2023       // Nothing to do here.
2024     }
2025
2026     /**
2027      * Invoked when the component's position changes.
2028      * 
2029      * @param e
2030      *          the event that occurs when moving the component
2031      */
2032     public void componentMoved(ComponentEvent e)
2033     {
2034       // TODO: What should be done here, if anything?
2035     }
2036
2037     /**
2038      * Creates, if necessary, and starts a Timer to check if needed to resize
2039      * the bounds
2040      */
2041     protected void startTimer()
2042     {
2043       // TODO: Implement this properly.
2044     }
2045
2046     /**
2047      * Returns the JScrollPane housing the JTree, or null if one isn't found.
2048      * 
2049      * @return JScrollPane housing the JTree, or null if one isn't found.
2050      */
2051     protected JScrollPane getScrollPane()
2052     {
2053       return null;
2054     }
2055
2056     /**
2057      * Public as a result of Timer. If the scrollBar is null, or not adjusting,
2058      * this stops the timer and updates the sizing.
2059      * 
2060      * @param ae
2061      *          is the action performed
2062      */
2063     public void actionPerformed(ActionEvent ae)
2064     {
2065       // TODO: Implement this properly.
2066     }
2067   }
2068
2069   /**
2070    * Listener responsible for getting cell editing events and updating the tree
2071    * accordingly.
2072    */
2073   public class CellEditorHandler implements CellEditorListener
2074   {
2075     /**
2076      * Constructor
2077      */
2078     public CellEditorHandler()
2079     {
2080       // Nothing to do here.
2081     }
2082
2083     /**
2084      * Messaged when editing has stopped in the tree. Tells the listeners
2085      * editing has stopped.
2086      * 
2087      * @param e
2088      *          is the notification event
2089      */
2090     public void editingStopped(ChangeEvent e)
2091     {
2092       editingPath = null;
2093       editingRow = -1;
2094       stopEditingInCompleteEditing = false;
2095       if (editingComponent != null)
2096         {
2097           tree.remove(editingComponent.getParent());
2098           editingComponent = null;
2099         }
2100       if (cellEditor != null)
2101         {
2102           newVal = ((JTextField) getCellEditor().getCellEditorValue()).getText();
2103           completeEditing(false, false, true);
2104           if (cellEditor instanceof DefaultTreeCellEditor)
2105             tree.removeTreeSelectionListener((DefaultTreeCellEditor) cellEditor);
2106           cellEditor.removeCellEditorListener(cellEditorListener);
2107           setCellEditor(null);
2108           createdCellEditor = false;
2109         }
2110       isEditing = false;
2111       tree.requestFocusInWindow(false);
2112       editorTimer.stop();
2113       validCachedPreferredSize = false;
2114       tree.repaint();
2115     }
2116
2117     /**
2118      * Messaged when editing has been canceled in the tree. This tells the
2119      * listeners the editor has canceled editing.
2120      * 
2121      * @param e
2122      *          is the notification event
2123      */
2124     public void editingCanceled(ChangeEvent e)
2125     {
2126       editingPath = null;
2127       editingRow = -1;
2128       stopEditingInCompleteEditing = false;
2129       if (editingComponent != null)
2130         tree.remove(editingComponent.getParent());
2131       editingComponent = null;
2132       if (cellEditor != null)
2133         {
2134           if (cellEditor instanceof DefaultTreeCellEditor)
2135             tree.removeTreeSelectionListener((DefaultTreeCellEditor) cellEditor);
2136           cellEditor.removeCellEditorListener(cellEditorListener);
2137           setCellEditor(null);
2138           createdCellEditor = false;
2139         }
2140       tree.requestFocusInWindow(false);
2141       editorTimer.stop();
2142       isEditing = false;
2143       validCachedPreferredSize = false;
2144       tree.repaint();
2145     }
2146   }// CellEditorHandler
2147
2148   /**
2149    * Repaints the lead selection row when focus is lost/grained.
2150    */
2151   public class FocusHandler implements FocusListener
2152   {
2153     /**
2154      * Constructor
2155      */
2156     public FocusHandler()
2157     {
2158       // Nothing to do here.
2159     }
2160
2161     /**
2162      * Invoked when focus is activated on the tree we're in, redraws the lead
2163      * row. Invoked when a component gains the keyboard focus.
2164      * 
2165      * @param e
2166      *          is the focus event that is activated
2167      */
2168     public void focusGained(FocusEvent e)
2169     {
2170       // TODO: Implement this properly.
2171     }
2172
2173     /**
2174      * Invoked when focus is deactivated on the tree we're in, redraws the lead
2175      * row. Invoked when a component loses the keyboard focus.
2176      * 
2177      * @param e
2178      *          is the focus event that is deactivated
2179      */
2180     public void focusLost(FocusEvent e)
2181     {
2182       // TODO: Implement this properly.
2183     }
2184   }
2185
2186   /**
2187    * This is used to get multiple key down events to appropriately genereate
2188    * events.
2189    */
2190   public class KeyHandler extends KeyAdapter
2191   {
2192     /** Key code that is being generated for. */
2193     protected Action repeatKeyAction;
2194
2195     /** Set to true while keyPressed is active */
2196     protected boolean isKeyDown;
2197
2198     /**
2199      * Constructor
2200      */
2201     public KeyHandler()
2202     {
2203       // Nothing to do here.
2204     }
2205
2206     /**
2207      * Invoked when a key has been typed. Moves the keyboard focus to the first
2208      * element whose first letter matches the alphanumeric key pressed by the
2209      * user. Subsequent same key presses move the keyboard focus to the next
2210      * object that starts with the same letter.
2211      * 
2212      * @param e
2213      *          the key typed
2214      */
2215     public void keyTyped(KeyEvent e)
2216     {
2217       // TODO: What should be done here, if anything?
2218     }
2219
2220     /**
2221      * Invoked when a key has been pressed.
2222      * 
2223      * @param e
2224      *          the key pressed
2225      */
2226     public void keyPressed(KeyEvent e)
2227     {
2228       // TODO: What should be done here, if anything?
2229     }
2230
2231     /**
2232      * Invoked when a key has been released
2233      * 
2234      * @param e
2235      *          the key released
2236      */
2237     public void keyReleased(KeyEvent e)
2238     {
2239       // TODO: What should be done here, if anything?
2240     }
2241   }
2242
2243   /**
2244    * MouseListener is responsible for updating the selection based on mouse
2245    * events.
2246    */
2247   public class MouseHandler extends MouseAdapter implements MouseMotionListener
2248   {
2249     /**
2250      * Constructor
2251      */
2252     public MouseHandler()
2253     {
2254       // Nothing to do here.
2255     }
2256
2257     /**
2258      * Invoked when a mouse button has been pressed on a component.
2259      * 
2260      * @param e
2261      *          is the mouse event that occured
2262      */
2263     public void mousePressed(MouseEvent e)
2264     {
2265       Point click = e.getPoint();
2266       TreePath path = getClosestPathForLocation(tree, click.x, click.y);
2267
2268       if (path != null)
2269         {
2270           Rectangle bounds = getPathBounds(tree, path);
2271           int row = getRowForPath(tree, path);
2272           boolean cntlClick = isLocationInExpandControl(path, click.x, click.y);
2273
2274           boolean isLeaf = isLeaf(row);
2275
2276           TreeCellRenderer tcr = getCellRenderer();
2277           Icon icon;
2278           if (isLeaf)
2279             icon = UIManager.getIcon("Tree.leafIcon");
2280           else if (tree.isExpanded(path))
2281             icon = UIManager.getIcon("Tree.openIcon");
2282           else
2283             icon = UIManager.getIcon("Tree.closedIcon");
2284
2285           if (tcr instanceof DefaultTreeCellRenderer)
2286             {
2287               Icon tmp = ((DefaultTreeCellRenderer) tcr).getIcon();
2288               if (tmp != null)
2289                 icon = tmp;
2290             }
2291
2292           // add gap*2 for the space before and after the text
2293           if (icon != null)
2294             bounds.width += icon.getIconWidth() + gap * 2;
2295
2296           boolean inBounds = bounds.contains(click.x, click.y);
2297           if ((inBounds || cntlClick) && tree.isVisible(path))
2298             {
2299               if (inBounds)
2300                 {
2301                   selectPath(tree, path);
2302                   if (e.getClickCount() == 2 && !isLeaf(row))
2303                     toggleExpandState(path);
2304                 }
2305
2306               if (cntlClick)
2307                 {
2308                   handleExpandControlClick(path, click.x, click.y);
2309                   if (cellEditor != null)
2310                     cellEditor.cancelCellEditing();
2311                   tree.scrollPathToVisible(path);
2312                 }
2313               else if (tree.isEditable())
2314                 startEditing(path, e);
2315             }
2316         }
2317     }
2318
2319     /**
2320      * Invoked when a mouse button is pressed on a component and then dragged.
2321      * MOUSE_DRAGGED events will continue to be delivered to the component where
2322      * the drag originated until the mouse button is released (regardless of
2323      * whether the mouse position is within the bounds of the component).
2324      * 
2325      * @param e
2326      *          is the mouse event that occured
2327      */
2328     public void mouseDragged(MouseEvent e)
2329     {
2330       // TODO: What should be done here, if anything?
2331     }
2332
2333     /**
2334      * Invoked when the mouse button has been moved on a component (with no
2335      * buttons no down).
2336      * 
2337      * @param e
2338      *          the mouse event that occured
2339      */
2340     public void mouseMoved(MouseEvent e)
2341     {
2342       // TODO: What should be done here, if anything?
2343     }
2344
2345     /**
2346      * Invoked when a mouse button has been released on a component.
2347      * 
2348      * @param e
2349      *          is the mouse event that occured
2350      */
2351     public void mouseReleased(MouseEvent e)
2352     {
2353       // TODO: What should be done here, if anything?
2354     }
2355   }
2356
2357   /**
2358    * MouseInputHandler handles passing all mouse events, including mouse motion
2359    * events, until the mouse is released to the destination it is constructed
2360    * with.
2361    */
2362   public class MouseInputHandler implements MouseInputListener
2363   {
2364     /** Source that events are coming from */
2365     protected Component source;
2366
2367     /** Destination that receives all events. */
2368     protected Component destination;
2369
2370     /**
2371      * Constructor
2372      * 
2373      * @param source
2374      *          that events are coming from
2375      * @param destination
2376      *          that receives all events
2377      * @param e
2378      *          is the event received
2379      */
2380     public MouseInputHandler(Component source, Component destination,
2381                              MouseEvent e)
2382     {
2383       this.source = source;
2384       this.destination = destination;
2385     }
2386
2387     /**
2388      * Invoked when the mouse button has been clicked (pressed and released) on
2389      * a component.
2390      * 
2391      * @param e
2392      *          mouse event that occured
2393      */
2394     public void mouseClicked(MouseEvent e)
2395     {
2396       // TODO: What should be done here, if anything?
2397     }
2398
2399     /**
2400      * Invoked when a mouse button has been pressed on a component.
2401      * 
2402      * @param e
2403      *          mouse event that occured
2404      */
2405     public void mousePressed(MouseEvent e)
2406     {
2407       // TODO: What should be done here, if anything?
2408     }
2409
2410     /**
2411      * Invoked when a mouse button has been released on a component.
2412      * 
2413      * @param e
2414      *          mouse event that occured
2415      */
2416     public void mouseReleased(MouseEvent e)
2417     {
2418       // TODO: What should be done here, if anything?
2419     }
2420
2421     /**
2422      * Invoked when the mouse enters a component.
2423      * 
2424      * @param e
2425      *          mouse event that occured
2426      */
2427     public void mouseEntered(MouseEvent e)
2428     {
2429       // TODO: What should be done here, if anything?
2430     }
2431
2432     /**
2433      * Invoked when the mouse exits a component.
2434      * 
2435      * @param e
2436      *          mouse event that occured
2437      */
2438     public void mouseExited(MouseEvent e)
2439     {
2440       // TODO: What should be done here, if anything?
2441     }
2442
2443     /**
2444      * Invoked when a mouse button is pressed on a component and then dragged.
2445      * MOUSE_DRAGGED events will continue to be delivered to the component where
2446      * the drag originated until the mouse button is released (regardless of
2447      * whether the mouse position is within the bounds of the component).
2448      * 
2449      * @param e
2450      *          mouse event that occured
2451      */
2452     public void mouseDragged(MouseEvent e)
2453     {
2454       // TODO: What should be done here, if anything?
2455     }
2456
2457     /**
2458      * Invoked when the mouse cursor has been moved onto a component but no
2459      * buttons have been pushed.
2460      * 
2461      * @param e
2462      *          mouse event that occured
2463      */
2464     public void mouseMoved(MouseEvent e)
2465     {
2466       // TODO: What should be done here, if anything?
2467     }
2468
2469     /**
2470      * Removes event from the source
2471      */
2472     protected void removeFromSource()
2473     {
2474       // TODO: Implement this properly.
2475     }
2476   }
2477
2478   /**
2479    * Class responsible for getting size of node, method is forwarded to
2480    * BasicTreeUI method. X location does not include insets, that is handled in
2481    * getPathBounds.
2482    */
2483   public class NodeDimensionsHandler extends AbstractLayoutCache.NodeDimensions
2484   {
2485     /**
2486      * Constructor
2487      */
2488     public NodeDimensionsHandler()
2489     {
2490       // Nothing to do here.
2491     }
2492
2493     /**
2494      * Returns, by reference in bounds, the size and x origin to place value at.
2495      * The calling method is responsible for determining the Y location. If
2496      * bounds is null, a newly created Rectangle should be returned, otherwise
2497      * the value should be placed in bounds and returned.
2498      * 
2499      * @param cell
2500      *          the value to be represented
2501      * @param row
2502      *          row being queried
2503      * @param depth
2504      *          the depth of the row
2505      * @param expanded
2506      *          true if row is expanded
2507      * @param size
2508      *          a Rectangle containing the size needed to represent value
2509      * @return containing the node dimensions, or null if node has no dimension
2510      */
2511     public Rectangle getNodeDimensions(Object cell, int row, int depth,
2512                                        boolean expanded, Rectangle size)
2513     {
2514       if (size == null || cell == null)
2515         return null;
2516
2517       String s = cell.toString();
2518       Font f = tree.getFont();
2519       FontMetrics fm = tree.getToolkit().getFontMetrics(f);
2520
2521       if (s != null)
2522         {
2523           size.x = getRowX(row, depth);
2524           size.width = SwingUtilities.computeStringWidth(fm, s);
2525           size.height = getMaxHeight(tree);
2526           size.y = size.height * row;
2527         }
2528
2529       return size;
2530     }
2531
2532     /**
2533      * Returns the amount to indent the given row
2534      * 
2535      * @return amount to indent the given row.
2536      */
2537     protected int getRowX(int row, int depth)
2538     {
2539       if (row == 0)
2540         return 0;
2541       return depth * rightChildIndent;
2542     }
2543   }// NodeDimensionsHandler
2544
2545   /**
2546    * PropertyChangeListener for the tree. Updates the appropriate varaible, or
2547    * TreeState, based on what changes.
2548    */
2549   public class PropertyChangeHandler implements PropertyChangeListener
2550   {
2551
2552     /**
2553      * Constructor
2554      */
2555     public PropertyChangeHandler()
2556     {
2557       // Nothing to do here.
2558     }
2559
2560     /**
2561      * This method gets called when a bound property is changed.
2562      * 
2563      * @param event
2564      *          A PropertyChangeEvent object describing the event source and the
2565      *          property that has changed.
2566      */
2567     public void propertyChange(PropertyChangeEvent event)
2568     {
2569       if ((event.getPropertyName()).equals("rootVisible"))
2570         {
2571           validCachedPreferredSize = false;
2572           tree.repaint();
2573         }
2574     }
2575   }
2576
2577   /**
2578    * Listener on the TreeSelectionModel, resets the row selection if any of the
2579    * properties of the model change.
2580    */
2581   public class SelectionModelPropertyChangeHandler implements
2582       PropertyChangeListener
2583   {
2584
2585     /**
2586      * Constructor
2587      */
2588     public SelectionModelPropertyChangeHandler()
2589     {
2590       // Nothing to do here.
2591     }
2592
2593     /**
2594      * This method gets called when a bound property is changed.
2595      * 
2596      * @param event
2597      *          A PropertyChangeEvent object describing the event source and the
2598      *          property that has changed.
2599      */
2600     public void propertyChange(PropertyChangeEvent event)
2601     {
2602       // TODO: What should be done here, if anything?
2603     }
2604   }
2605
2606   /**
2607    * ActionListener that invokes cancelEditing when action performed.
2608    */
2609   public class TreeCancelEditingAction extends AbstractAction
2610   {
2611
2612     /**
2613      * Constructor
2614      */
2615     public TreeCancelEditingAction(String name)
2616     {
2617       // TODO: Implement this properly.
2618     }
2619
2620     /**
2621      * Invoked when an action occurs.
2622      * 
2623      * @param e
2624      *          event that occured
2625      */
2626     public void actionPerformed(ActionEvent e)
2627     {
2628       // TODO: Implement this properly.
2629     }
2630
2631     /**
2632      * Returns true if the action is enabled.
2633      * 
2634      * @return true if the action is enabled, false otherwise
2635      */
2636     public boolean isEnabled()
2637     {
2638       // TODO: Implement this properly.
2639       return false;
2640     }
2641   }
2642
2643   /**
2644    * Updates the TreeState in response to nodes expanding/collapsing.
2645    */
2646   public class TreeExpansionHandler implements TreeExpansionListener
2647   {
2648
2649     /**
2650      * Constructor
2651      */
2652     public TreeExpansionHandler()
2653     {
2654       // Nothing to do here.
2655     }
2656
2657     /**
2658      * Called whenever an item in the tree has been expanded.
2659      * 
2660      * @param event
2661      *          is the event that occured
2662      */
2663     public void treeExpanded(TreeExpansionEvent event)
2664     {
2665       validCachedPreferredSize = false;
2666       tree.repaint();
2667     }
2668
2669     /**
2670      * Called whenever an item in the tree has been collapsed.
2671      * 
2672      * @param event
2673      *          is the event that occured
2674      */
2675     public void treeCollapsed(TreeExpansionEvent event)
2676     {
2677       validCachedPreferredSize = false;
2678       tree.repaint();
2679     }
2680   }// TreeExpansionHandler
2681
2682   /**
2683    * TreeHomeAction is used to handle end/home actions. Scrolls either the first
2684    * or last cell to be visible based on direction.
2685    */
2686   public class TreeHomeAction extends AbstractAction
2687   {
2688
2689     /** direction is either home or end */
2690     protected int direction;
2691
2692     /**
2693      * Constructor
2694      * 
2695      * @param direction -
2696      *          it is home or end
2697      * @param name
2698      *          is the name of the direction
2699      */
2700     public TreeHomeAction(int direction, String name)
2701     {
2702       // TODO: Implement this properly
2703     }
2704
2705     /**
2706      * Invoked when an action occurs.
2707      * 
2708      * @param e
2709      *          is the event that occured
2710      */
2711     public void actionPerformed(ActionEvent e)
2712     {
2713       // TODO: Implement this properly
2714     }
2715
2716     /**
2717      * Returns true if the action is enabled.
2718      * 
2719      * @return true if the action is enabled.
2720      */
2721     public boolean isEnabled()
2722     {
2723       // TODO: Implement this properly
2724       return false;
2725     }
2726   }
2727
2728   /**
2729    * TreeIncrementAction is used to handle up/down actions. Selection is moved
2730    * up or down based on direction.
2731    */
2732   public class TreeIncrementAction extends AbstractAction
2733   {
2734
2735     /** Specifies the direction to adjust the selection by. */
2736     protected int direction;
2737
2738     /**
2739      * Constructor
2740      * 
2741      * @param direction
2742      *          up or down
2743      * @param name
2744      *          is the name of the direction
2745      */
2746     public TreeIncrementAction(int direction, String name)
2747     {
2748       // TODO: Implement this properly
2749     }
2750
2751     /**
2752      * Invoked when an action occurs.
2753      * 
2754      * @param e
2755      *          is the event that occured
2756      */
2757     public void actionPerformed(ActionEvent e)
2758     {
2759       Object last = tree.getLeadSelectionPath().getLastPathComponent();
2760
2761       if (e.getActionCommand().equals("selectPreviousChangeLead"))
2762         {
2763           Object prev = getPreviousVisibleNode(last);
2764
2765           if (prev != null)
2766             {
2767               TreePath newPath = new TreePath(getPathToRoot(prev, 0));
2768               selectPath(tree, newPath);
2769               tree.setLeadSelectionPath(newPath);
2770             }
2771         }
2772       else if (e.getActionCommand().equals("selectPreviousExtendSelection"))
2773         {
2774           Object prev = getPreviousVisibleNode(last);
2775           if (prev != null)
2776             {
2777               TreePath newPath = new TreePath(getPathToRoot(prev, 0));
2778               tree.addSelectionPath(newPath);
2779               tree.setLeadSelectionPath(newPath);
2780             }
2781         }
2782       else if (e.getActionCommand().equals("selectPrevious"))
2783         {
2784           Object prev = getPreviousVisibleNode(last);
2785
2786           if (prev != null)
2787             {
2788               TreePath newPath = new TreePath(getPathToRoot(prev, 0));
2789               selectPath(tree, newPath);
2790             }
2791         }
2792       else if (e.getActionCommand().equals("selectNext"))
2793         {
2794           Object next = getNextVisibleNode(last);
2795
2796           if (next != null)
2797             {
2798               TreePath newPath = new TreePath(getPathToRoot(next, 0));
2799               selectPath(tree, newPath);
2800             }
2801         }
2802       else if (e.getActionCommand().equals("selectNextExtendSelection"))
2803         {
2804           Object next = getNextVisibleNode(last);
2805           if (next != null)
2806             {
2807               TreePath newPath = new TreePath(getPathToRoot(next, 0));
2808               tree.addSelectionPath(newPath);
2809               tree.setLeadSelectionPath(newPath);
2810             }
2811         }
2812       else if (e.getActionCommand().equals("selectNextChangeLead"))
2813         {
2814           Object next = getNextVisibleNode(last);
2815           if (next != null)
2816             {
2817               TreePath newPath = new TreePath(getPathToRoot(next, 0));
2818               selectPath(tree, newPath);
2819               tree.setLeadSelectionPath(newPath);
2820             }
2821         }
2822     }
2823
2824     /**
2825      * Returns true if the action is enabled.
2826      * 
2827      * @return true if the action is enabled.
2828      */
2829     public boolean isEnabled()
2830     {
2831       // TODO: Implement this properly
2832       return false;
2833     }
2834   }
2835
2836   /**
2837    * Forwards all TreeModel events to the TreeState.
2838    */
2839   public class TreeModelHandler implements TreeModelListener
2840   {
2841     /**
2842      * Constructor
2843      */
2844     public TreeModelHandler()
2845     {
2846       // Nothing to do here.
2847     }
2848
2849     /**
2850      * Invoked after a node (or a set of siblings) has changed in some way. The
2851      * node(s) have not changed locations in the tree or altered their children
2852      * arrays, but other attributes have changed and may affect presentation.
2853      * Example: the name of a file has changed, but it is in the same location
2854      * in the file system. To indicate the root has changed, childIndices and
2855      * children will be null. Use e.getPath() to get the parent of the changed
2856      * node(s). e.getChildIndices() returns the index(es) of the changed
2857      * node(s).
2858      * 
2859      * @param e
2860      *          is the event that occured
2861      */
2862     public void treeNodesChanged(TreeModelEvent e)
2863     {
2864       validCachedPreferredSize = false;
2865       tree.repaint();
2866     }
2867
2868     /**
2869      * Invoked after nodes have been inserted into the tree. Use e.getPath() to
2870      * get the parent of the new node(s). e.getChildIndices() returns the
2871      * index(es) of the new node(s) in ascending order.
2872      * 
2873      * @param e
2874      *          is the event that occured
2875      */
2876     public void treeNodesInserted(TreeModelEvent e)
2877     {
2878       validCachedPreferredSize = false;
2879       tree.repaint();
2880     }
2881
2882     /**
2883      * Invoked after nodes have been removed from the tree. Note that if a
2884      * subtree is removed from the tree, this method may only be invoked once
2885      * for the root of the removed subtree, not once for each individual set of
2886      * siblings removed. Use e.getPath() to get the former parent of the deleted
2887      * node(s). e.getChildIndices() returns, in ascending order, the index(es)
2888      * the node(s) had before being deleted.
2889      * 
2890      * @param e
2891      *          is the event that occured
2892      */
2893     public void treeNodesRemoved(TreeModelEvent e)
2894     {
2895       validCachedPreferredSize = false;
2896       tree.repaint();
2897     }
2898
2899     /**
2900      * Invoked after the tree has drastically changed structure from a given
2901      * node down. If the path returned by e.getPath() is of length one and the
2902      * first element does not identify the current root node the first element
2903      * should become the new root of the tree. Use e.getPath() to get the path
2904      * to the node. e.getChildIndices() returns null.
2905      * 
2906      * @param e
2907      *          is the event that occured
2908      */
2909     public void treeStructureChanged(TreeModelEvent e)
2910     {
2911       if (e.getPath().length == 1
2912           && !e.getPath()[0].equals(treeModel.getRoot()))
2913         tree.expandPath(new TreePath(treeModel.getRoot()));
2914       validCachedPreferredSize = false;
2915       tree.repaint();
2916     }
2917   }// TreeModelHandler
2918
2919   /**
2920    * TreePageAction handles page up and page down events.
2921    */
2922   public class TreePageAction extends AbstractAction
2923   {
2924     /** Specifies the direction to adjust the selection by. */
2925     protected int direction;
2926
2927     /**
2928      * Constructor
2929      * 
2930      * @param direction
2931      *          up or down
2932      * @param name
2933      *          is the name of the direction
2934      */
2935     public TreePageAction(int direction, String name)
2936     {
2937       this.direction = direction;
2938     }
2939
2940     /**
2941      * Invoked when an action occurs.
2942      * 
2943      * @param e
2944      *          is the event that occured
2945      */
2946     public void actionPerformed(ActionEvent e)
2947     {
2948       // TODO: Implement this properly.
2949     }
2950
2951     /**
2952      * Returns true if the action is enabled.
2953      * 
2954      * @return true if the action is enabled.
2955      */
2956     public boolean isEnabled()
2957     {
2958       return false;
2959     }
2960   }// TreePageAction
2961
2962   /**
2963    * Listens for changes in the selection model and updates the display
2964    * accordingly.
2965    */
2966   public class TreeSelectionHandler implements TreeSelectionListener
2967   {
2968     /**
2969      * Constructor
2970      */
2971     public TreeSelectionHandler()
2972     {
2973       // Nothing to do here.
2974     }
2975
2976     /**
2977      * Messaged when the selection changes in the tree we're displaying for.
2978      * Stops editing, messages super and displays the changed paths.
2979      * 
2980      * @param event
2981      *          the event that characterizes the change.
2982      */
2983     public void valueChanged(TreeSelectionEvent event)
2984     {
2985       if (tree.isEditing())
2986         tree.cancelEditing();
2987     }
2988   }// TreeSelectionHandler
2989
2990   /**
2991    * For the first selected row expandedness will be toggled.
2992    */
2993   public class TreeToggleAction extends AbstractAction
2994   {
2995     /**
2996      * Constructor
2997      * 
2998      * @param name
2999      *          is the name of <code>Action</code> field
3000      */
3001     public TreeToggleAction(String name)
3002     {
3003       // Nothing to do here.
3004     }
3005
3006     /**
3007      * Invoked when an action occurs.
3008      * 
3009      * @param e
3010      *          the event that occured
3011      */
3012     public void actionPerformed(ActionEvent e)
3013     {
3014       // TODO: Implement this properly.
3015     }
3016
3017     /**
3018      * Returns true if the action is enabled.
3019      * 
3020      * @return true if the action is enabled, false otherwise
3021      */
3022     public boolean isEnabled()
3023     {
3024       return false;
3025     }
3026   } // TreeToggleAction
3027
3028   /**
3029    * TreeTraverseAction is the action used for left/right keys. Will toggle the
3030    * expandedness of a node, as well as potentially incrementing the selection.
3031    */
3032   public class TreeTraverseAction extends AbstractAction
3033   {
3034     /**
3035      * Determines direction to traverse, 1 means expand, -1 means collapse.
3036      */
3037     protected int direction;
3038
3039     /**
3040      * Constructor
3041      * 
3042      * @param direction
3043      *          to traverse
3044      * @param name
3045      *          is the name of the direction
3046      */
3047     public TreeTraverseAction(int direction, String name)
3048     {
3049       this.direction = direction;
3050     }
3051
3052     /**
3053      * Invoked when an action occurs.
3054      * 
3055      * @param e
3056      *          the event that occured
3057      */
3058     public void actionPerformed(ActionEvent e)
3059     {
3060       Object last = tree.getLeadSelectionPath().getLastPathComponent();
3061
3062       if (e.getActionCommand().equals("selectParent"))
3063         {
3064           TreePath path = new TreePath(getPathToRoot(last, 0));
3065           Object p = getParent(treeModel.getRoot(), last);
3066
3067           if (!treeModel.isLeaf(last))
3068             toggleExpandState(path);
3069           else if (p != null)
3070             selectPath(tree, new TreePath(getPathToRoot(p, 0)));
3071         }
3072       else if (e.getActionCommand().equals("selectChild"))
3073         {
3074           TreePath path = new TreePath(getPathToRoot(last, 0));
3075
3076           if (!treeModel.isLeaf(last))
3077             toggleExpandState(path);
3078           else
3079             {
3080               Object next = getNextVisibleNode(last);
3081
3082               if (next != null)
3083                 selectPath(tree, new TreePath(getPathToRoot(next, 0)));
3084             }
3085         }
3086     }
3087
3088     /**
3089      * Returns true if the action is enabled.
3090      * 
3091      * @return true if the action is enabled, false otherwise
3092      */
3093     public boolean isEnabled()
3094     {
3095       // TODO: Implement this properly
3096       return false;
3097     }
3098   }
3099
3100   /**
3101    * Returns true if the LookAndFeel implements the control icons. Package
3102    * private for use in inner classes.
3103    * 
3104    * @returns true if there are control icons
3105    */
3106   boolean hasControlIcons()
3107   {
3108     if (expandedIcon != null || collapsedIcon != null)
3109       return true;
3110     return false;
3111   }
3112
3113   /**
3114    * Returns control icon. It is null if the LookAndFeel does not implements the
3115    * control icons. Package private for use in inner classes.
3116    * 
3117    * @return control icon if it exists.
3118    */
3119   Icon getCurrentControlIcon(TreePath path)
3120   {
3121     if (tree.isExpanded(path))
3122       return expandedIcon;
3123     return collapsedIcon;
3124   }
3125
3126   /**
3127    * Returns the parent of the current node
3128    * 
3129    * @param root
3130    *          is the root of the tree
3131    * @param node
3132    *          is the current node
3133    * @return is the parent of the current node
3134    */
3135   Object getParent(Object root, Object node)
3136   {
3137     if (root == null || node == null || root.equals(node))
3138       return null;
3139
3140     if (node instanceof TreeNode)
3141       return ((TreeNode) node).getParent();
3142     return findNode(root, node);
3143   }
3144
3145   /**
3146    * Recursively checks the tree for the specified node, starting at the root.
3147    * 
3148    * @param root
3149    *          is starting node to start searching at.
3150    * @param node
3151    *          is the node to search for
3152    * @return the parent node of node
3153    */
3154   private Object findNode(Object root, Object node)
3155   {
3156     if (!treeModel.isLeaf(root) && !root.equals(node))
3157       {
3158         int size = treeModel.getChildCount(root);
3159         for (int j = 0; j < size; j++)
3160           {
3161             Object child = treeModel.getChild(root, j);
3162             if (node.equals(child))
3163               return root;
3164
3165             Object n = findNode(child, node);
3166             if (n != null)
3167               return n;
3168           }
3169       }
3170     return null;
3171   }
3172
3173   /**
3174    * Get previous visible node in the tree. Package private for use in inner
3175    * classes.
3176    * 
3177    * @param node -
3178    *          current node
3179    * @return the next visible node in the JTree. Return null if there are no
3180    *         more.
3181    */
3182   Object getPreviousVisibleNode(Object node)
3183   {
3184     if (currentVisiblePath != null)
3185       {
3186         Object[] nodes = currentVisiblePath.getPath();
3187         int i = 0;
3188         while (i < nodes.length && !node.equals(nodes[i]))
3189           i++;
3190         // return the next node
3191         if (i - 1 >= 0)
3192           return nodes[i - 1];
3193       }
3194     return null;
3195   }
3196
3197   /**
3198    * Returns the next node in the tree Package private for use in inner classes.
3199    * 
3200    * @param curr -
3201    *          current node
3202    * @return the next node in the tree
3203    */
3204   Object getNextNode(Object curr)
3205   {
3206     if (!treeModel.isLeaf(curr) && treeModel.getChildCount(curr) > 0)
3207       return treeModel.getChild(curr, 0);
3208
3209     Object node = curr;
3210     Object sibling = null;
3211     do
3212       {
3213         sibling = getNextSibling(node);
3214         node = getParent(treeModel.getRoot(), node);
3215       }
3216     while (sibling == null && node != null);
3217
3218     return sibling;
3219   }
3220
3221   /**
3222    * Returns the previous node in the tree Package private for use in inner
3223    * classes.
3224    * 
3225    * @param node
3226    *          current node
3227    * @return the previous node in the tree
3228    */
3229   Object getPreviousNode(Object node)
3230   {
3231     Object parent = getParent(treeModel.getRoot(), node);
3232     if (parent == null)
3233       return null;
3234
3235     Object sibling = getPreviousSibling(node);
3236
3237     if (sibling == null)
3238       return parent;
3239
3240     int size = 0;
3241     if (!treeModel.isLeaf(sibling))
3242       size = treeModel.getChildCount(sibling);
3243     while (size > 0)
3244       {
3245         sibling = treeModel.getChild(sibling, size - 1);
3246         if (!treeModel.isLeaf(sibling))
3247           size = treeModel.getChildCount(sibling);
3248         else
3249           size = 0;
3250       }
3251
3252     return sibling;
3253   }
3254
3255   /**
3256    * Returns the next sibling in the tree Package private for use in inner
3257    * classes.
3258    * 
3259    * @param node -
3260    *          current node
3261    * @return the next sibling in the tree
3262    */
3263   Object getNextSibling(Object node)
3264   {
3265     Object parent = getParent(treeModel.getRoot(), node);
3266     if (parent == null)
3267       return null;
3268
3269     int index = treeModel.getIndexOfChild(parent, node) + 1;
3270
3271     int size = 0;
3272     if (!treeModel.isLeaf(parent))
3273       size = treeModel.getChildCount(parent);
3274     if (index == 0 || index >= size)
3275       return null;
3276
3277     return treeModel.getChild(parent, index);
3278   }
3279
3280   /**
3281    * Returns the previous sibling in the tree Package private for use in inner
3282    * classes.
3283    * 
3284    * @param node -
3285    *          current node
3286    * @return the previous sibling in the tree
3287    */
3288   Object getPreviousSibling(Object node)
3289   {
3290     Object parent = getParent(treeModel.getRoot(), node);
3291     if (parent == null)
3292       return null;
3293
3294     int index = treeModel.getIndexOfChild(parent, node) - 1;
3295
3296     int size = 0;
3297     if (!treeModel.isLeaf(parent))
3298       size = treeModel.getChildCount(parent);
3299     if (index < 0 || index >= size)
3300       return null;
3301
3302     return treeModel.getChild(parent, index);
3303   }
3304
3305   /**
3306    * Selects the specified path in the tree depending on modes. Package private
3307    * for use in inner classes.
3308    * 
3309    * @param tree
3310    *          is the tree we are selecting the path in
3311    * @param path
3312    *          is the path we are selecting
3313    */
3314   void selectPath(JTree tree, TreePath path)
3315   {
3316     if (path != null)
3317       {
3318         if (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.SINGLE_TREE_SELECTION)
3319           {
3320             tree.getSelectionModel().clearSelection();
3321             tree.addSelectionPath(path);
3322             tree.setLeadSelectionPath(path);
3323           }
3324         else if (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION)
3325           {
3326             // TODO
3327           }
3328         else
3329           {
3330             tree.addSelectionPath(path);
3331             tree.setLeadSelectionPath(path);
3332             tree.getSelectionModel().setSelectionMode(
3333                                                       TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
3334           }
3335       }
3336   }
3337
3338   /**
3339    * Returns the path from node to the root. Package private for use in inner
3340    * classes.
3341    * 
3342    * @param node
3343    *          the node to get the path to
3344    * @param depth
3345    *          the depth of the tree to return a path for
3346    * @return an array of tree nodes that represent the path to node.
3347    */
3348   Object[] getPathToRoot(Object node, int depth)
3349   {
3350     if (node == null)
3351       {
3352         if (depth == 0)
3353           return null;
3354
3355         return new Object[depth];
3356       }
3357
3358     Object[] path = getPathToRoot(getParent(treeModel.getRoot(), node),
3359                                   depth + 1);
3360     path[path.length - depth - 1] = node;
3361     return path;
3362   }
3363
3364   /**
3365    * Returns the level of the node in the tree.
3366    * 
3367    * @param node -
3368    *          current node
3369    * @return the number of the level
3370    */
3371   int getLevel(Object node)
3372   {
3373     int count = -1;
3374
3375     Object current = node;
3376
3377     if (treeModel != null)
3378       {
3379         Object root = treeModel.getRoot();
3380         if (!tree.isRootVisible() && tree.isExpanded(new TreePath(root)))
3381           count--;
3382
3383         do
3384           {
3385             current = getParent(root, current);
3386             count++;
3387           }
3388         while (current != null);
3389       }
3390     return count;
3391   }
3392
3393   /**
3394    * Draws a vertical line using the given graphic context
3395    * 
3396    * @param g
3397    *          is the graphic context
3398    * @param c
3399    *          is the component the new line will belong to
3400    * @param x
3401    *          is the horizonal position
3402    * @param top
3403    *          specifies the top of the line
3404    * @param bottom
3405    *          specifies the bottom of the line
3406    */
3407   protected void paintVerticalLine(Graphics g, JComponent c, int x, int top,
3408                                    int bottom)
3409   {
3410     // FIXME: Check if drawing a dashed line or not.
3411     g.setColor(getHashColor());
3412     g.drawLine(x, top, x, bottom);
3413   }
3414
3415   /**
3416    * Draws a horizontal line using the given graphic context
3417    * 
3418    * @param g
3419    *          is the graphic context
3420    * @param c
3421    *          is the component the new line will belong to
3422    * @param y
3423    *          is the vertical position
3424    * @param left
3425    *          specifies the left point of the line
3426    * @param right
3427    *          specifies the right point of the line
3428    */
3429   protected void paintHorizontalLine(Graphics g, JComponent c, int y, int left,
3430                                      int right)
3431   {
3432     // FIXME: Check if drawing a dashed line or not.
3433     g.setColor(getHashColor());
3434     g.drawLine(left, y, right, y);
3435   }
3436
3437   /**
3438    * Draws an icon at around a specific position
3439    * 
3440    * @param c
3441    *          is the component the new line will belong to
3442    * @param g
3443    *          is the graphic context
3444    * @param icon
3445    *          is the icon which will be drawn
3446    * @param x
3447    *          is the center position in x-direction
3448    * @param y
3449    *          is the center position in y-direction
3450    */
3451   protected void drawCentered(Component c, Graphics g, Icon icon, int x, int y)
3452   {
3453     x -= icon.getIconWidth() / 2;
3454     y -= icon.getIconHeight() / 2;
3455
3456     if (x < 0)
3457       x = 0;
3458     if (y < 0)
3459       y = 0;
3460
3461     icon.paintIcon(c, g, x, y);
3462   }
3463
3464   /**
3465    * Draws a dashed horizontal line.
3466    * 
3467    * @param g -
3468    *          the graphics configuration.
3469    * @param y -
3470    *          the y location to start drawing at
3471    * @param x1 -
3472    *          the x location to start drawing at
3473    * @param x2 -
3474    *          the x location to finish drawing at
3475    */
3476   protected void drawDashedHorizontalLine(Graphics g, int y, int x1, int x2)
3477   {
3478     g.setColor(getHashColor());
3479     for (int i = x1; i < x2; i += 2)
3480       g.drawLine(i, y, i + 1, y);
3481   }
3482
3483   /**
3484    * Draws a dashed vertical line.
3485    * 
3486    * @param g -
3487    *          the graphics configuration.
3488    * @param x -
3489    *          the x location to start drawing at
3490    * @param y1 -
3491    *          the y location to start drawing at
3492    * @param y2 -
3493    *          the y location to finish drawing at
3494    */
3495   protected void drawDashedVerticalLine(Graphics g, int x, int y1, int y2)
3496   {
3497     g.setColor(getHashColor());
3498     for (int i = y1; i < y2; i += 2)
3499       g.drawLine(x, i, x, i + 1);
3500   }
3501
3502   /**
3503    * Paints the expand (toggle) part of a row. The receiver should NOT modify
3504    * clipBounds, or insets.
3505    * 
3506    * @param g -
3507    *          the graphics configuration
3508    * @param clipBounds -
3509    * @param insets -
3510    * @param bounds -
3511    *          bounds of expand control
3512    * @param path -
3513    *          path to draw control for
3514    * @param row -
3515    *          row to draw control for
3516    * @param isExpanded -
3517    *          is the row expanded
3518    * @param hasBeenExpanded -
3519    *          has the row already been expanded
3520    * @param isLeaf -
3521    *          is the path a leaf
3522    */
3523   protected void paintExpandControl(Graphics g, Rectangle clipBounds,
3524                                     Insets insets, Rectangle bounds,
3525                                     TreePath path, int row, boolean isExpanded,
3526                                     boolean hasBeenExpanded, boolean isLeaf)
3527   {
3528     if (shouldPaintExpandControl(path, row, isExpanded, hasBeenExpanded, isLeaf))
3529       {
3530         Icon icon = getCurrentControlIcon(path);
3531         int iconW = icon.getIconWidth();
3532         int x = bounds.x - rightChildIndent + iconW / 2;
3533         if (x + iconW > bounds.x)
3534           x = bounds.x - rightChildIndent - gap;
3535         icon.paintIcon(tree, g, x, bounds.y + bounds.height / 2
3536                                    - icon.getIconHeight() / 2);
3537       }
3538   }
3539
3540   /**
3541    * Paints the horizontal part of the leg. The receiver should NOT modify
3542    * clipBounds, or insets. NOTE: parentRow can be -1 if the root is not
3543    * visible.
3544    * 
3545    * @param g -
3546    *          the graphics configuration
3547    * @param clipBounds -
3548    * @param insets -
3549    * @param bounds -
3550    *          bounds of the cell
3551    * @param path -
3552    *          path to draw leg for
3553    * @param row -
3554    *          row to start drawing at
3555    * @param isExpanded -
3556    *          is the row expanded
3557    * @param hasBeenExpanded -
3558    *          has the row already been expanded
3559    * @param isLeaf -
3560    *          is the path a leaf
3561    */
3562   protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds,
3563                                           Insets insets, Rectangle bounds,
3564                                           TreePath path, int row,
3565                                           boolean isExpanded,
3566                                           boolean hasBeenExpanded,
3567                                           boolean isLeaf)
3568   {
3569     if (row != 0)
3570       paintHorizontalLine(g, tree, bounds.y + bounds.height / 2, bounds.x - gap
3571                                                                  - 2, bounds.x);
3572   }
3573
3574   /**
3575    * Paints the vertical part of the leg. The receiver should NOT modify
3576    * clipBounds, insets.
3577    * 
3578    * @param g -
3579    *          the graphics configuration.
3580    * @param clipBounds -
3581    * @param insets -
3582    * @param path -
3583    *          the path to draw the vertical part for.
3584    */
3585   protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds,
3586                                         Insets insets, TreePath path)
3587   {
3588     int max = tree.getVisibleRowCount();
3589     for (int i = 0; i < max; i++)
3590       {
3591         Object curr = path.getPathComponent(i);
3592         TreePath currPath = new TreePath(getPathToRoot(curr, 0));
3593         int numChild = treeModel.getChildCount(curr);
3594         if (numChild > 0 && tree.isExpanded(currPath))
3595           {
3596             Rectangle bounds = getPathBounds(tree, currPath);
3597             Rectangle lastChildBounds = getPathBounds(
3598                                                       tree,
3599                                                       new TreePath(
3600                                                                    getPathToRoot(
3601                                                                                  treeModel.getChild(
3602                                                                                                     curr,
3603                                                                                                     numChild - 1),
3604                                                                                  0)));
3605             paintVerticalLine(g, tree, bounds.x + gap + 2, bounds.y
3606                                                            + bounds.height - 2,
3607                               lastChildBounds.y + lastChildBounds.height / 2);
3608           }
3609       }
3610   }
3611
3612   /**
3613    * Paints the renderer part of a row. The receiver should NOT modify
3614    * clipBounds, or insets.
3615    * 
3616    * @param g -
3617    *          the graphics configuration
3618    * @param clipBounds -
3619    * @param insets -
3620    * @param bounds -
3621    *          bounds of expand control
3622    * @param path -
3623    *          path to draw control for
3624    * @param row -
3625    *          row to draw control for
3626    * @param isExpanded -
3627    *          is the row expanded
3628    * @param hasBeenExpanded -
3629    *          has the row already been expanded
3630    * @param isLeaf -
3631    *          is the path a leaf
3632    */
3633   protected void paintRow(Graphics g, Rectangle clipBounds, Insets insets,
3634                           Rectangle bounds, TreePath path, int row,
3635                           boolean isExpanded, boolean hasBeenExpanded,
3636                           boolean isLeaf)
3637   {
3638     boolean selected = tree.isPathSelected(path);
3639     boolean hasIcons = false;
3640     Object node = path.getLastPathComponent();
3641
3642     if (tree.isVisible(path))
3643       {
3644         if (!validCachedPreferredSize)
3645           updateCachedPreferredSize();
3646
3647         paintExpandControl(g, clipBounds, insets, bounds, path, row,
3648                            isExpanded, hasBeenExpanded, isLeaf);
3649
3650         if (row != 0)
3651           bounds.x += gap;
3652         bounds.width = preferredSize.width + bounds.x;
3653         if (editingComponent != null && editingPath != null && isEditing(tree)
3654             && node.equals(editingPath.getLastPathComponent()))
3655           {
3656             rendererPane.paintComponent(g, editingComponent.getParent(), null,
3657                                         bounds);
3658           }
3659         else
3660           {
3661             TreeCellRenderer dtcr = tree.getCellRenderer();
3662             if (dtcr == null)
3663               dtcr = createDefaultCellRenderer();
3664
3665             Component c = dtcr.getTreeCellRendererComponent(tree, node,
3666                                                             selected,
3667                                                             isExpanded, isLeaf,
3668                                                             row,
3669                                                             tree.hasFocus());
3670             rendererPane.paintComponent(g, c, c.getParent(), bounds);
3671           }
3672       }
3673   }
3674
3675   /**
3676    * Prepares for the UI to uninstall.
3677    */
3678   protected void prepareForUIUninstall()
3679   {
3680     // TODO: Implement this properly.
3681   }
3682
3683   /**
3684    * Returns true if the expand (toggle) control should be drawn for the
3685    * specified row.
3686    * 
3687    * @param path -
3688    *          current path to check for.
3689    * @param row -
3690    *          current row to check for.
3691    * @param isExpanded -
3692    *          true if the path is expanded
3693    * @param hasBeenExpanded -
3694    *          true if the path has been expanded already
3695    * @param isLeaf -
3696    *          true if the row is a lead
3697    */
3698   protected boolean shouldPaintExpandControl(TreePath path, int row,
3699                                              boolean isExpanded,
3700                                              boolean hasBeenExpanded,
3701                                              boolean isLeaf)
3702   {
3703     Object node = path.getLastPathComponent();
3704     return (!isLeaf && getLevel(node) != 0 && hasControlIcons());
3705   }
3706
3707   /**
3708    * Updates the cached current TreePath of all visible nodes in the tree.
3709    */
3710   void updateCurrentVisiblePath()
3711   {
3712     if (treeModel == null)
3713       return;
3714
3715     Object next = treeModel.getRoot();
3716     if (next == null)
3717       return;
3718
3719     TreePath rootPath = new TreePath(next);
3720     Rectangle bounds = getPathBounds(tree, rootPath);
3721
3722     // If root is not a valid size to be visible, or is
3723     // not visible and the tree is expanded, then the next node acts
3724     // as the root
3725     if ((bounds.width == 0 && bounds.height == 0)
3726         || (!isRootVisible() && tree.isExpanded(new TreePath(next))))
3727       {
3728         next = getNextNode(next);
3729         rootPath = new TreePath(next);
3730       }
3731
3732     Object root = next;
3733     TreePath current = null;
3734     while (next != null)
3735       {
3736         if (current == null)
3737           current = rootPath;
3738         else
3739           current = current.pathByAddingChild(next);
3740
3741         do
3742           {
3743             TreePath path = new TreePath(getPathToRoot(next, 0));
3744             if ((tree.isVisible(path) && tree.isExpanded(path))
3745                 || treeModel.isLeaf(next))
3746               next = getNextNode(next);
3747             else
3748               {
3749                 Object pNext = next;
3750                 next = getNextSibling(pNext);
3751                 // if no next sibling, check parent's next sibling.
3752                 if (next == null)
3753                   {
3754                     Object parent = getParent(root, pNext);
3755                     while (next == null && parent != null)
3756                       {
3757                         next = getNextSibling(parent);
3758                         if (next == null)
3759                           parent = getParent(root, parent);
3760                       }
3761                   }
3762               }
3763           }
3764         while (next != null
3765                && !tree.isVisible(new TreePath(getPathToRoot(next, 0))));
3766       }
3767
3768     currentVisiblePath = current;
3769     tree.setVisibleRowCount(getRowCount(tree));
3770
3771     if (tree.getSelectionModel() != null && tree.getSelectionCount() == 0
3772         && currentVisiblePath != null)
3773       selectPath(
3774                  tree,
3775                  new TreePath(
3776                               getPathToRoot(
3777                                             currentVisiblePath.getPathComponent(0),
3778                                             0)));
3779   }
3780
3781   /**
3782    * Get next visible node in the currentVisiblePath. Package private for use in
3783    * inner classes.
3784    * 
3785    * @param node
3786    *          current node
3787    * @return the next visible node in the JTree. Return null if there are no
3788    *         more.
3789    */
3790   Object getNextVisibleNode(Object node)
3791   {
3792     if (currentVisiblePath != null)
3793       {
3794         Object[] nodes = currentVisiblePath.getPath();
3795         int i = 0;
3796         while (i < nodes.length && !node.equals(nodes[i]))
3797           i++;
3798         // return the next node
3799         if (i + 1 < nodes.length)
3800           return nodes[i + 1];
3801       }
3802     return null;
3803   }
3804 } // BasicTreeUI