Save This Page
Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]
    1   /*
    2    * Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package javax.swing.plaf.basic;
   27   
   28   import javax.swing;
   29   import javax.swing.event;
   30   import java.awt;
   31   import java.awt.event;
   32   import java.awt.datatransfer;
   33   import java.awt.dnd;
   34   import java.beans;
   35   import java.io;
   36   import java.util.Enumeration;
   37   import java.util.Hashtable;
   38   import java.util.TooManyListenersException;
   39   import java.util.ArrayList;
   40   import java.util.Collections;
   41   import java.util.Comparator;
   42   import javax.swing.plaf.ActionMapUIResource;
   43   import javax.swing.plaf.ComponentUI;
   44   import javax.swing.plaf.UIResource;
   45   import javax.swing.plaf.TreeUI;
   46   import javax.swing.tree;
   47   import javax.swing.text.Position;
   48   import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag;
   49   import sun.swing.SwingUtilities2;
   50   
   51   import sun.swing.DefaultLookup;
   52   import sun.swing.UIAction;
   53   
   54   /**
   55    * The basic L&F for a hierarchical data structure.
   56    * <p>
   57    *
   58    * @author Scott Violet
   59    * @author Shannon Hickey (drag and drop)
   60    */
   61   
   62   public class BasicTreeUI extends TreeUI
   63   {
   64       private static final StringBuilder BASELINE_COMPONENT_KEY =
   65           new StringBuilder("Tree.baselineComponent");
   66   
   67       // Old actions forward to an instance of this.
   68       static private final Actions SHARED_ACTION = new Actions();
   69   
   70       transient protected Icon        collapsedIcon;
   71       transient protected Icon        expandedIcon;
   72   
   73       /**
   74         * Color used to draw hash marks.  If <code>null</code> no hash marks
   75         * will be drawn.
   76         */
   77       private Color hashColor;
   78   
   79       /** Distance between left margin and where vertical dashes will be
   80         * drawn. */
   81       protected int               leftChildIndent;
   82       /** Distance to add to leftChildIndent to determine where cell
   83         * contents will be drawn. */
   84       protected int               rightChildIndent;
   85       /** Total distance that will be indented.  The sum of leftChildIndent
   86         * and rightChildIndent. */
   87       protected int               totalChildIndent;
   88   
   89       /** Minimum preferred size. */
   90       protected Dimension         preferredMinSize;
   91   
   92       /** Index of the row that was last selected. */
   93       protected int               lastSelectedRow;
   94   
   95       /** Component that we're going to be drawing into. */
   96       protected JTree             tree;
   97   
   98       /** Renderer that is being used to do the actual cell drawing. */
   99       transient protected TreeCellRenderer   currentCellRenderer;
  100   
  101       /** Set to true if the renderer that is currently in the tree was
  102        * created by this instance. */
  103       protected boolean           createdRenderer;
  104   
  105       /** Editor for the tree. */
  106       transient protected TreeCellEditor     cellEditor;
  107   
  108       /** Set to true if editor that is currently in the tree was
  109        * created by this instance. */
  110       protected boolean           createdCellEditor;
  111   
  112       /** Set to false when editing and shouldSelectCell() returns true meaning
  113         * the node should be selected before editing, used in completeEditing. */
  114       protected boolean           stopEditingInCompleteEditing;
  115   
  116       /** Used to paint the TreeCellRenderer. */
  117       protected CellRendererPane  rendererPane;
  118   
  119       /** Size needed to completely display all the nodes. */
  120       protected Dimension         preferredSize;
  121   
  122       /** Is the preferredSize valid? */
  123       protected boolean           validCachedPreferredSize;
  124   
  125       /** Object responsible for handling sizing and expanded issues. */
  126       // WARNING: Be careful with the bounds held by treeState. They are
  127       // always in terms of left-to-right. They get mapped to right-to-left
  128       // by the various methods of this class.
  129       protected AbstractLayoutCache  treeState;
  130   
  131   
  132       /** Used for minimizing the drawing of vertical lines. */
  133       protected Hashtable<TreePath,Boolean> drawingCache;
  134   
  135       /** True if doing optimizations for a largeModel. Subclasses that
  136        * don't support this may wish to override createLayoutCache to not
  137        * return a FixedHeightLayoutCache instance. */
  138       protected boolean           largeModel;
  139   
  140       /** Reponsible for telling the TreeState the size needed for a node. */
  141       protected AbstractLayoutCache.NodeDimensions     nodeDimensions;
  142   
  143       /** Used to determine what to display. */
  144       protected TreeModel         treeModel;
  145   
  146       /** Model maintaing the selection. */
  147       protected TreeSelectionModel treeSelectionModel;
  148   
  149       /** How much the depth should be offset to properly calculate
  150        * x locations. This is based on whether or not the root is visible,
  151        * and if the root handles are visible. */
  152       protected int               depthOffset;
  153   
  154       // Following 4 ivars are only valid when editing.
  155   
  156       /** When editing, this will be the Component that is doing the actual
  157         * editing. */
  158       protected Component         editingComponent;
  159   
  160       /** Path that is being edited. */
  161       protected TreePath          editingPath;
  162   
  163       /** Row that is being edited. Should only be referenced if
  164        * editingComponent is not null. */
  165       protected int               editingRow;
  166   
  167       /** Set to true if the editor has a different size than the renderer. */
  168       protected boolean           editorHasDifferentSize;
  169   
  170       /** Row correspondin to lead path. */
  171       private int                 leadRow;
  172       /** If true, the property change event for LEAD_SELECTION_PATH_PROPERTY,
  173        * or ANCHOR_SELECTION_PATH_PROPERTY will not generate a repaint. */
  174       private boolean             ignoreLAChange;
  175   
  176       /** Indicates the orientation. */
  177       private boolean             leftToRight;
  178   
  179       // Cached listeners
  180       private PropertyChangeListener propertyChangeListener;
  181       private PropertyChangeListener selectionModelPropertyChangeListener;
  182       private MouseListener mouseListener;
  183       private FocusListener focusListener;
  184       private KeyListener keyListener;
  185       /** Used for large models, listens for moved/resized events and
  186        * updates the validCachedPreferredSize bit accordingly. */
  187       private ComponentListener   componentListener;
  188       /** Listens for CellEditor events. */
  189       private CellEditorListener  cellEditorListener;
  190       /** Updates the display when the selection changes. */
  191       private TreeSelectionListener treeSelectionListener;
  192       /** Is responsible for updating the display based on model events. */
  193       private TreeModelListener treeModelListener;
  194       /** Updates the treestate as the nodes expand. */
  195       private TreeExpansionListener treeExpansionListener;
  196   
  197       /** UI property indicating whether to paint lines */
  198       private boolean paintLines = true;
  199   
  200       /** UI property for painting dashed lines */
  201       private boolean lineTypeDashed;
  202   
  203       /**
  204        * The time factor to treate the series of typed alphanumeric key
  205        * as prefix for first letter navigation.
  206        */
  207       private long timeFactor = 1000L;
  208   
  209       private Handler handler;
  210   
  211       /**
  212        * A temporary variable for communication between startEditingOnRelease
  213        * and startEditing.
  214        */
  215       private MouseEvent releaseEvent;
  216   
  217       public static ComponentUI createUI(JComponent x) {
  218           return new BasicTreeUI();
  219       }
  220   
  221   
  222       static void loadActionMap(LazyActionMap map) {
  223           map.put(new Actions(Actions.SELECT_PREVIOUS));
  224           map.put(new Actions(Actions.SELECT_PREVIOUS_CHANGE_LEAD));
  225           map.put(new Actions(Actions.SELECT_PREVIOUS_EXTEND_SELECTION));
  226   
  227           map.put(new Actions(Actions.SELECT_NEXT));
  228           map.put(new Actions(Actions.SELECT_NEXT_CHANGE_LEAD));
  229           map.put(new Actions(Actions.SELECT_NEXT_EXTEND_SELECTION));
  230   
  231           map.put(new Actions(Actions.SELECT_CHILD));
  232           map.put(new Actions(Actions.SELECT_CHILD_CHANGE_LEAD));
  233   
  234           map.put(new Actions(Actions.SELECT_PARENT));
  235           map.put(new Actions(Actions.SELECT_PARENT_CHANGE_LEAD));
  236   
  237           map.put(new Actions(Actions.SCROLL_UP_CHANGE_SELECTION));
  238           map.put(new Actions(Actions.SCROLL_UP_CHANGE_LEAD));
  239           map.put(new Actions(Actions.SCROLL_UP_EXTEND_SELECTION));
  240   
  241           map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_SELECTION));
  242           map.put(new Actions(Actions.SCROLL_DOWN_EXTEND_SELECTION));
  243           map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_LEAD));
  244   
  245           map.put(new Actions(Actions.SELECT_FIRST));
  246           map.put(new Actions(Actions.SELECT_FIRST_CHANGE_LEAD));
  247           map.put(new Actions(Actions.SELECT_FIRST_EXTEND_SELECTION));
  248   
  249           map.put(new Actions(Actions.SELECT_LAST));
  250           map.put(new Actions(Actions.SELECT_LAST_CHANGE_LEAD));
  251           map.put(new Actions(Actions.SELECT_LAST_EXTEND_SELECTION));
  252   
  253           map.put(new Actions(Actions.TOGGLE));
  254   
  255           map.put(new Actions(Actions.CANCEL_EDITING));
  256   
  257           map.put(new Actions(Actions.START_EDITING));
  258   
  259           map.put(new Actions(Actions.SELECT_ALL));
  260   
  261           map.put(new Actions(Actions.CLEAR_SELECTION));
  262   
  263           map.put(new Actions(Actions.SCROLL_LEFT));
  264           map.put(new Actions(Actions.SCROLL_RIGHT));
  265   
  266           map.put(new Actions(Actions.SCROLL_LEFT_EXTEND_SELECTION));
  267           map.put(new Actions(Actions.SCROLL_RIGHT_EXTEND_SELECTION));
  268   
  269           map.put(new Actions(Actions.SCROLL_RIGHT_CHANGE_LEAD));
  270           map.put(new Actions(Actions.SCROLL_LEFT_CHANGE_LEAD));
  271   
  272           map.put(new Actions(Actions.EXPAND));
  273           map.put(new Actions(Actions.COLLAPSE));
  274           map.put(new Actions(Actions.MOVE_SELECTION_TO_PARENT));
  275   
  276           map.put(new Actions(Actions.ADD_TO_SELECTION));
  277           map.put(new Actions(Actions.TOGGLE_AND_ANCHOR));
  278           map.put(new Actions(Actions.EXTEND_TO));
  279           map.put(new Actions(Actions.MOVE_SELECTION_TO));
  280   
  281           map.put(TransferHandler.getCutAction());
  282           map.put(TransferHandler.getCopyAction());
  283           map.put(TransferHandler.getPasteAction());
  284       }
  285   
  286   
  287       public BasicTreeUI() {
  288           super();
  289       }
  290   
  291       protected Color getHashColor() {
  292           return hashColor;
  293       }
  294   
  295       protected void setHashColor(Color color) {
  296           hashColor = color;
  297       }
  298   
  299       public void setLeftChildIndent(int newAmount) {
  300           leftChildIndent = newAmount;
  301           totalChildIndent = leftChildIndent + rightChildIndent;
  302           if(treeState != null)
  303               treeState.invalidateSizes();
  304           updateSize();
  305       }
  306   
  307       public int getLeftChildIndent() {
  308           return leftChildIndent;
  309       }
  310   
  311       public void setRightChildIndent(int newAmount) {
  312           rightChildIndent = newAmount;
  313           totalChildIndent = leftChildIndent + rightChildIndent;
  314           if(treeState != null)
  315               treeState.invalidateSizes();
  316           updateSize();
  317       }
  318   
  319       public int getRightChildIndent() {
  320           return rightChildIndent;
  321       }
  322   
  323       public void setExpandedIcon(Icon newG) {
  324           expandedIcon = newG;
  325       }
  326   
  327       public Icon getExpandedIcon() {
  328           return expandedIcon;
  329       }
  330   
  331       public void setCollapsedIcon(Icon newG) {
  332           collapsedIcon = newG;
  333       }
  334   
  335       public Icon getCollapsedIcon() {
  336           return collapsedIcon;
  337       }
  338   
  339       //
  340       // Methods for configuring the behavior of the tree. None of them
  341       // push the value to the JTree instance. You should really only
  342       // call these methods on the JTree.
  343       //
  344   
  345       /**
  346        * Updates the componentListener, if necessary.
  347        */
  348       protected void setLargeModel(boolean largeModel) {
  349           if(getRowHeight() < 1)
  350               largeModel = false;
  351           if(this.largeModel != largeModel) {
  352               completeEditing();
  353               this.largeModel = largeModel;
  354               treeState = createLayoutCache();
  355               configureLayoutCache();
  356               updateLayoutCacheExpandedNodesIfNecessary();
  357               updateSize();
  358           }
  359       }
  360   
  361       protected boolean isLargeModel() {
  362           return largeModel;
  363       }
  364   
  365       /**
  366        * Sets the row height, this is forwarded to the treeState.
  367        */
  368       protected void setRowHeight(int rowHeight) {
  369           completeEditing();
  370           if(treeState != null) {
  371               setLargeModel(tree.isLargeModel());
  372               treeState.setRowHeight(rowHeight);
  373               updateSize();
  374           }
  375       }
  376   
  377       protected int getRowHeight() {
  378           return (tree == null) ? -1 : tree.getRowHeight();
  379       }
  380   
  381       /**
  382        * Sets the TreeCellRenderer to <code>tcr</code>. This invokes
  383        * <code>updateRenderer</code>.
  384        */
  385       protected void setCellRenderer(TreeCellRenderer tcr) {
  386           completeEditing();
  387           updateRenderer();
  388           if(treeState != null) {
  389               treeState.invalidateSizes();
  390               updateSize();
  391           }
  392       }
  393   
  394       /**
  395        * Return currentCellRenderer, which will either be the trees
  396        * renderer, or defaultCellRenderer, which ever wasn't null.
  397        */
  398       protected TreeCellRenderer getCellRenderer() {
  399           return currentCellRenderer;
  400       }
  401   
  402       /**
  403        * Sets the TreeModel.
  404        */
  405       protected void setModel(TreeModel model) {
  406           completeEditing();
  407           if(treeModel != null && treeModelListener != null)
  408               treeModel.removeTreeModelListener(treeModelListener);
  409           treeModel = model;
  410           if(treeModel != null) {
  411               if(treeModelListener != null)
  412                   treeModel.addTreeModelListener(treeModelListener);
  413           }
  414           if(treeState != null) {
  415               treeState.setModel(model);
  416               updateLayoutCacheExpandedNodesIfNecessary();
  417               updateSize();
  418           }
  419       }
  420   
  421       protected TreeModel getModel() {
  422           return treeModel;
  423       }
  424   
  425       /**
  426        * Sets the root to being visible.
  427        */
  428       protected void setRootVisible(boolean newValue) {
  429           completeEditing();
  430           updateDepthOffset();
  431           if(treeState != null) {
  432               treeState.setRootVisible(newValue);
  433               treeState.invalidateSizes();
  434               updateSize();
  435           }
  436       }
  437   
  438       protected boolean isRootVisible() {
  439           return (tree != null) ? tree.isRootVisible() : false;
  440       }
  441   
  442       /**
  443        * Determines whether the node handles are to be displayed.
  444        */
  445       protected void setShowsRootHandles(boolean newValue) {
  446           completeEditing();
  447           updateDepthOffset();
  448           if(treeState != null) {
  449               treeState.invalidateSizes();
  450               updateSize();
  451           }
  452       }
  453   
  454       protected boolean getShowsRootHandles() {
  455           return (tree != null) ? tree.getShowsRootHandles() : false;
  456       }
  457   
  458       /**
  459        * Sets the cell editor.
  460        */
  461       protected void setCellEditor(TreeCellEditor editor) {
  462           updateCellEditor();
  463       }
  464   
  465       protected TreeCellEditor getCellEditor() {
  466           return (tree != null) ? tree.getCellEditor() : null;
  467       }
  468   
  469       /**
  470        * Configures the receiver to allow, or not allow, editing.
  471        */
  472       protected void setEditable(boolean newValue) {
  473           updateCellEditor();
  474       }
  475   
  476       protected boolean isEditable() {
  477           return (tree != null) ? tree.isEditable() : false;
  478       }
  479   
  480       /**
  481        * Resets the selection model. The appropriate listener are installed
  482        * on the model.
  483        */
  484       protected void setSelectionModel(TreeSelectionModel newLSM) {
  485           completeEditing();
  486           if(selectionModelPropertyChangeListener != null &&
  487              treeSelectionModel != null)
  488               treeSelectionModel.removePropertyChangeListener
  489                                 (selectionModelPropertyChangeListener);
  490           if(treeSelectionListener != null && treeSelectionModel != null)
  491               treeSelectionModel.removeTreeSelectionListener
  492                                  (treeSelectionListener);
  493           treeSelectionModel = newLSM;
  494           if(treeSelectionModel != null) {
  495               if(selectionModelPropertyChangeListener != null)
  496                   treeSelectionModel.addPropertyChangeListener
  497                                 (selectionModelPropertyChangeListener);
  498               if(treeSelectionListener != null)
  499                   treeSelectionModel.addTreeSelectionListener
  500                                      (treeSelectionListener);
  501               if(treeState != null)
  502                   treeState.setSelectionModel(treeSelectionModel);
  503           }
  504           else if(treeState != null)
  505               treeState.setSelectionModel(null);
  506           if(tree != null)
  507               tree.repaint();
  508       }
  509   
  510       protected TreeSelectionModel getSelectionModel() {
  511           return treeSelectionModel;
  512       }
  513   
  514       //
  515       // TreeUI methods
  516       //
  517   
  518       /**
  519         * Returns the Rectangle enclosing the label portion that the
  520         * last item in path will be drawn into.  Will return null if
  521         * any component in path is currently valid.
  522         */
  523       public Rectangle getPathBounds(JTree tree, TreePath path) {
  524           if(tree != null && treeState != null) {
  525               return getPathBounds(path, tree.getInsets(), new Rectangle());
  526           }
  527           return null;
  528       }
  529   
  530       private Rectangle getPathBounds(TreePath path, Insets insets,
  531                                       Rectangle bounds) {
  532           bounds = treeState.getBounds(path, bounds);
  533           if (bounds != null) {
  534               if (leftToRight) {
  535                   bounds.x += insets.left;
  536               } else {
  537                   bounds.x = tree.getWidth() - (bounds.x + bounds.width) -
  538                           insets.right;
  539               }
  540               bounds.y += insets.top;
  541           }
  542           return bounds;
  543       }
  544   
  545       /**
  546         * Returns the path for passed in row.  If row is not visible
  547         * null is returned.
  548         */
  549       public TreePath getPathForRow(JTree tree, int row) {
  550           return (treeState != null) ? treeState.getPathForRow(row) : null;
  551       }
  552   
  553       /**
  554         * Returns the row that the last item identified in path is visible
  555         * at.  Will return -1 if any of the elements in path are not
  556         * currently visible.
  557         */
  558       public int getRowForPath(JTree tree, TreePath path) {
  559           return (treeState != null) ? treeState.getRowForPath(path) : -1;
  560       }
  561   
  562       /**
  563         * Returns the number of rows that are being displayed.
  564         */
  565       public int getRowCount(JTree tree) {
  566           return (treeState != null) ? treeState.getRowCount() : 0;
  567       }
  568   
  569       /**
  570         * Returns the path to the node that is closest to x,y.  If
  571         * there is nothing currently visible this will return null, otherwise
  572         * it'll always return a valid path.  If you need to test if the
  573         * returned object is exactly at x, y you should get the bounds for
  574         * the returned path and test x, y against that.
  575         */
  576       public TreePath getClosestPathForLocation(JTree tree, int x, int y) {
  577           if(tree != null && treeState != null) {
  578               // TreeState doesn't care about the x location, hence it isn't
  579               // adjusted
  580               y -= tree.getInsets().top;
  581               return treeState.getPathClosestTo(x, y);
  582           }
  583           return null;
  584       }
  585   
  586       /**
  587         * Returns true if the tree is being edited.  The item that is being
  588         * edited can be returned by getEditingPath().
  589         */
  590       public boolean isEditing(JTree tree) {
  591           return (editingComponent != null);
  592       }
  593   
  594       /**
  595         * Stops the current editing session.  This has no effect if the
  596         * tree isn't being edited.  Returns true if the editor allows the
  597         * editing session to stop.
  598         */
  599       public boolean stopEditing(JTree tree) {
  600           if(editingComponent != null && cellEditor.stopCellEditing()) {
  601               completeEditing(false, false, true);
  602               return true;
  603           }
  604           return false;
  605       }
  606   
  607       /**
  608         * Cancels the current editing session.
  609         */
  610       public void cancelEditing(JTree tree) {
  611           if(editingComponent != null) {
  612               completeEditing(false, true, false);
  613           }
  614       }
  615   
  616       /**
  617         * Selects the last item in path and tries to edit it.  Editing will
  618         * fail if the CellEditor won't allow it for the selected item.
  619         */
  620       public void startEditingAtPath(JTree tree, TreePath path) {
  621           tree.scrollPathToVisible(path);
  622           if(path != null && tree.isVisible(path))
  623               startEditing(path, null);
  624       }
  625   
  626       /**
  627        * Returns the path to the element that is being edited.
  628        */
  629       public TreePath getEditingPath(JTree tree) {
  630           return editingPath;
  631       }
  632   
  633       //
  634       // Install methods
  635       //
  636   
  637       public void installUI(JComponent c) {
  638           if ( c == null ) {
  639               throw new NullPointerException( "null component passed to BasicTreeUI.installUI()" );
  640           }
  641   
  642           tree = (JTree)c;
  643   
  644           prepareForUIInstall();
  645   
  646           // Boilerplate install block
  647           installDefaults();
  648           installKeyboardActions();
  649           installComponents();
  650           installListeners();
  651   
  652           completeUIInstall();
  653       }
  654   
  655       /**
  656        * Invoked after the <code>tree</code> instance variable has been
  657        * set, but before any defaults/listeners have been installed.
  658        */
  659       protected void prepareForUIInstall() {
  660           drawingCache = new Hashtable<TreePath,Boolean>(7);
  661   
  662           // Data member initializations
  663           leftToRight = BasicGraphicsUtils.isLeftToRight(tree);
  664           stopEditingInCompleteEditing = true;
  665           lastSelectedRow = -1;
  666           leadRow = -1;
  667           preferredSize = new Dimension();
  668   
  669           largeModel = tree.isLargeModel();
  670           if(getRowHeight() <= 0)
  671               largeModel = false;
  672           setModel(tree.getModel());
  673       }
  674   
  675       /**
  676        * Invoked from installUI after all the defaults/listeners have been
  677        * installed.
  678        */
  679       protected void completeUIInstall() {
  680           // Custom install code
  681   
  682           this.setShowsRootHandles(tree.getShowsRootHandles());
  683   
  684           updateRenderer();
  685   
  686           updateDepthOffset();
  687   
  688           setSelectionModel(tree.getSelectionModel());
  689   
  690           // Create, if necessary, the TreeState instance.
  691           treeState = createLayoutCache();
  692           configureLayoutCache();
  693   
  694           updateSize();
  695       }
  696   
  697       protected void installDefaults() {
  698           if(tree.getBackground() == null ||
  699              tree.getBackground() instanceof UIResource) {
  700               tree.setBackground(UIManager.getColor("Tree.background"));
  701           }
  702           if(getHashColor() == null || getHashColor() instanceof UIResource) {
  703               setHashColor(UIManager.getColor("Tree.hash"));
  704           }
  705           if (tree.getFont() == null || tree.getFont() instanceof UIResource)
  706               tree.setFont( UIManager.getFont("Tree.font") );
  707           // JTree's original row height is 16.  To correctly display the
  708           // contents on Linux we should have set it to 18, Windows 19 and
  709           // Solaris 20.  As these values vary so much it's too hard to
  710           // be backward compatable and try to update the row height, we're
  711           // therefor NOT going to adjust the row height based on font.  If the
  712           // developer changes the font, it's there responsibility to update
  713           // the row height.
  714   
  715           setExpandedIcon( (Icon)UIManager.get( "Tree.expandedIcon" ) );
  716           setCollapsedIcon( (Icon)UIManager.get( "Tree.collapsedIcon" ) );
  717   
  718           setLeftChildIndent(((Integer)UIManager.get("Tree.leftChildIndent")).
  719                              intValue());
  720           setRightChildIndent(((Integer)UIManager.get("Tree.rightChildIndent")).
  721                              intValue());
  722   
  723           LookAndFeel.installProperty(tree, "rowHeight",
  724                                       UIManager.get("Tree.rowHeight"));
  725   
  726           largeModel = (tree.isLargeModel() && tree.getRowHeight() > 0);
  727   
  728           Object scrollsOnExpand = UIManager.get("Tree.scrollsOnExpand");
  729           if (scrollsOnExpand != null) {
  730               LookAndFeel.installProperty(tree, "scrollsOnExpand", scrollsOnExpand);
  731           }
  732   
  733           paintLines = UIManager.getBoolean("Tree.paintLines");
  734           lineTypeDashed = UIManager.getBoolean("Tree.lineTypeDashed");
  735   
  736           Long l = (Long)UIManager.get("Tree.timeFactor");
  737           timeFactor = (l!=null) ? l.longValue() : 1000L;
  738   
  739           Object showsRootHandles = UIManager.get("Tree.showsRootHandles");
  740           if (showsRootHandles != null) {
  741               LookAndFeel.installProperty(tree,
  742                       JTree.SHOWS_ROOT_HANDLES_PROPERTY, showsRootHandles);
  743           }
  744       }
  745   
  746       protected void installListeners() {
  747           if ( (propertyChangeListener = createPropertyChangeListener())
  748                != null ) {
  749               tree.addPropertyChangeListener(propertyChangeListener);
  750           }
  751           if ( (mouseListener = createMouseListener()) != null ) {
  752               tree.addMouseListener(mouseListener);
  753               if (mouseListener instanceof MouseMotionListener) {
  754                   tree.addMouseMotionListener((MouseMotionListener)mouseListener);
  755               }
  756           }
  757           if ((focusListener = createFocusListener()) != null ) {
  758               tree.addFocusListener(focusListener);
  759           }
  760           if ((keyListener = createKeyListener()) != null) {
  761               tree.addKeyListener(keyListener);
  762           }
  763           if((treeExpansionListener = createTreeExpansionListener()) != null) {
  764               tree.addTreeExpansionListener(treeExpansionListener);
  765           }
  766           if((treeModelListener = createTreeModelListener()) != null &&
  767              treeModel != null) {
  768               treeModel.addTreeModelListener(treeModelListener);
  769           }
  770           if((selectionModelPropertyChangeListener =
  771               createSelectionModelPropertyChangeListener()) != null &&
  772              treeSelectionModel != null) {
  773               treeSelectionModel.addPropertyChangeListener
  774                   (selectionModelPropertyChangeListener);
  775           }
  776           if((treeSelectionListener = createTreeSelectionListener()) != null &&
  777              treeSelectionModel != null) {
  778               treeSelectionModel.addTreeSelectionListener(treeSelectionListener);
  779           }
  780   
  781           TransferHandler th = tree.getTransferHandler();
  782           if (th == null || th instanceof UIResource) {
  783               tree.setTransferHandler(defaultTransferHandler);
  784               // default TransferHandler doesn't support drop
  785               // so we don't want drop handling
  786               if (tree.getDropTarget() instanceof UIResource) {
  787                   tree.setDropTarget(null);
  788               }
  789           }
  790   
  791           LookAndFeel.installProperty(tree, "opaque", Boolean.TRUE);
  792       }
  793   
  794       protected void installKeyboardActions() {
  795           InputMap km = getInputMap(JComponent.
  796                                     WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  797   
  798           SwingUtilities.replaceUIInputMap(tree, JComponent.
  799                                            WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
  800                                            km);
  801           km = getInputMap(JComponent.WHEN_FOCUSED);
  802           SwingUtilities.replaceUIInputMap(tree, JComponent.WHEN_FOCUSED, km);
  803   
  804           LazyActionMap.installLazyActionMap(tree, BasicTreeUI.class,
  805                                              "Tree.actionMap");
  806       }
  807   
  808       InputMap getInputMap(int condition) {
  809           if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
  810               return (InputMap)DefaultLookup.get(tree, this,
  811                                                  "Tree.ancestorInputMap");
  812           }
  813           else if (condition == JComponent.WHEN_FOCUSED) {
  814               InputMap keyMap = (InputMap)DefaultLookup.get(tree, this,
  815                                                         "Tree.focusInputMap");
  816               InputMap rtlKeyMap;
  817   
  818               if (tree.getComponentOrientation().isLeftToRight() ||
  819                     ((rtlKeyMap = (InputMap)DefaultLookup.get(tree, this,
  820                     "Tree.focusInputMap.RightToLeft")) == null)) {
  821                   return keyMap;
  822               } else {
  823                   rtlKeyMap.setParent(keyMap);
  824                   return rtlKeyMap;
  825               }
  826           }
  827           return null;
  828       }
  829   
  830       /**
  831        * Intalls the subcomponents of the tree, which is the renderer pane.
  832        */
  833       protected void installComponents() {
  834           if ((rendererPane = createCellRendererPane()) != null) {
  835               tree.add( rendererPane );
  836           }
  837       }
  838   
  839       //
  840       // Create methods.
  841       //
  842   
  843       /**
  844        * Creates an instance of NodeDimensions that is able to determine
  845        * the size of a given node in the tree.
  846        */
  847       protected AbstractLayoutCache.NodeDimensions createNodeDimensions() {
  848           return new NodeDimensionsHandler();
  849       }
  850   
  851       /**
  852        * Creates a listener that is responsible that updates the UI based on
  853        * how the tree changes.
  854        */
  855       protected PropertyChangeListener createPropertyChangeListener() {
  856           return getHandler();
  857       }
  858   
  859       private Handler getHandler() {
  860           if (handler == null) {
  861               handler = new Handler();
  862           }
  863           return handler;
  864       }
  865   
  866       /**
  867        * Creates the listener responsible for updating the selection based on
  868        * mouse events.
  869        */
  870       protected MouseListener createMouseListener() {
  871           return getHandler();
  872       }
  873   
  874       /**
  875        * Creates a listener that is responsible for updating the display
  876        * when focus is lost/gained.
  877        */
  878       protected FocusListener createFocusListener() {
  879           return getHandler();
  880       }
  881   
  882       /**
  883        * Creates the listener reponsible for getting key events from
  884        * the tree.
  885        */
  886       protected KeyListener createKeyListener() {
  887           return getHandler();
  888       }
  889   
  890       /**
  891        * Creates the listener responsible for getting property change
  892        * events from the selection model.
  893        */
  894       protected PropertyChangeListener createSelectionModelPropertyChangeListener() {
  895           return getHandler();
  896       }
  897   
  898       /**
  899        * Creates the listener that updates the display based on selection change
  900        * methods.
  901        */
  902       protected TreeSelectionListener createTreeSelectionListener() {
  903           return getHandler();
  904       }
  905   
  906       /**
  907        * Creates a listener to handle events from the current editor.
  908        */
  909       protected CellEditorListener createCellEditorListener() {
  910           return getHandler();
  911       }
  912   
  913       /**
  914        * Creates and returns a new ComponentHandler. This is used for
  915        * the large model to mark the validCachedPreferredSize as invalid
  916        * when the component moves.
  917        */
  918       protected ComponentListener createComponentListener() {
  919           return new ComponentHandler();
  920       }
  921   
  922       /**
  923        * Creates and returns the object responsible for updating the treestate
  924        * when nodes expanded state changes.
  925        */
  926       protected TreeExpansionListener createTreeExpansionListener() {
  927           return getHandler();
  928       }
  929   
  930       /**
  931        * Creates the object responsible for managing what is expanded, as
  932        * well as the size of nodes.
  933        */
  934       protected AbstractLayoutCache createLayoutCache() {
  935           if(isLargeModel() && getRowHeight() > 0) {
  936               return new FixedHeightLayoutCache();
  937           }
  938           return new VariableHeightLayoutCache();
  939       }
  940   
  941       /**
  942        * Returns the renderer pane that renderer components are placed in.
  943        */
  944       protected CellRendererPane createCellRendererPane() {
  945           return new CellRendererPane();
  946       }
  947   
  948       /**
  949         * Creates a default cell editor.
  950         */
  951       protected TreeCellEditor createDefaultCellEditor() {
  952           if(currentCellRenderer != null &&
  953              (currentCellRenderer instanceof DefaultTreeCellRenderer)) {
  954               DefaultTreeCellEditor editor = new DefaultTreeCellEditor
  955                           (tree, (DefaultTreeCellRenderer)currentCellRenderer);
  956   
  957               return editor;
  958           }
  959           return new DefaultTreeCellEditor(tree, null);
  960       }
  961   
  962       /**
  963         * Returns the default cell renderer that is used to do the
  964         * stamping of each node.
  965         */
  966       protected TreeCellRenderer createDefaultCellRenderer() {
  967           return new DefaultTreeCellRenderer();
  968       }
  969   
  970       /**
  971        * Returns a listener that can update the tree when the model changes.
  972        */
  973       protected TreeModelListener createTreeModelListener() {
  974           return getHandler();
  975       }
  976   
  977       //
  978       // Uninstall methods
  979       //
  980   
  981       public void uninstallUI(JComponent c) {
  982           completeEditing();
  983   
  984           prepareForUIUninstall();
  985   
  986           uninstallDefaults();
  987           uninstallListeners();
  988           uninstallKeyboardActions();
  989           uninstallComponents();
  990   
  991           completeUIUninstall();
  992       }
  993   
  994       protected void prepareForUIUninstall() {
  995       }
  996   
  997       protected void completeUIUninstall() {
  998           if(createdRenderer) {
  999               tree.setCellRenderer(null);
 1000           }
 1001           if(createdCellEditor) {
 1002               tree.setCellEditor(null);
 1003           }
 1004           cellEditor = null;
 1005           currentCellRenderer = null;
 1006           rendererPane = null;
 1007           componentListener = null;
 1008           propertyChangeListener = null;
 1009           mouseListener = null;
 1010           focusListener = null;
 1011           keyListener = null;
 1012           setSelectionModel(null);
 1013           treeState = null;
 1014           drawingCache = null;
 1015           selectionModelPropertyChangeListener = null;
 1016           tree = null;
 1017           treeModel = null;
 1018           treeSelectionModel = null;
 1019           treeSelectionListener = null;
 1020           treeExpansionListener = null;
 1021       }
 1022   
 1023       protected void uninstallDefaults() {
 1024           if (tree.getTransferHandler() instanceof UIResource) {
 1025               tree.setTransferHandler(null);
 1026           }
 1027       }
 1028   
 1029       protected void uninstallListeners() {
 1030           if(componentListener != null) {
 1031               tree.removeComponentListener(componentListener);
 1032           }
 1033           if (propertyChangeListener != null) {
 1034               tree.removePropertyChangeListener(propertyChangeListener);
 1035           }
 1036           if (mouseListener != null) {
 1037               tree.removeMouseListener(mouseListener);
 1038               if (mouseListener instanceof MouseMotionListener) {
 1039                   tree.removeMouseMotionListener((MouseMotionListener)mouseListener);
 1040               }
 1041           }
 1042           if (focusListener != null) {
 1043               tree.removeFocusListener(focusListener);
 1044           }
 1045           if (keyListener != null) {
 1046               tree.removeKeyListener(keyListener);
 1047           }
 1048           if(treeExpansionListener != null) {
 1049               tree.removeTreeExpansionListener(treeExpansionListener);
 1050           }
 1051           if(treeModel != null && treeModelListener != null) {
 1052               treeModel.removeTreeModelListener(treeModelListener);
 1053           }
 1054           if(selectionModelPropertyChangeListener != null &&
 1055              treeSelectionModel != null) {
 1056               treeSelectionModel.removePropertyChangeListener
 1057                   (selectionModelPropertyChangeListener);
 1058           }
 1059           if(treeSelectionListener != null && treeSelectionModel != null) {
 1060               treeSelectionModel.removeTreeSelectionListener
 1061                                  (treeSelectionListener);
 1062           }
 1063           handler = null;
 1064       }
 1065   
 1066       protected void uninstallKeyboardActions() {
 1067           SwingUtilities.replaceUIActionMap(tree, null);
 1068           SwingUtilities.replaceUIInputMap(tree, JComponent.
 1069                                            WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
 1070                                            null);
 1071           SwingUtilities.replaceUIInputMap(tree, JComponent.WHEN_FOCUSED, null);
 1072       }
 1073   
 1074       /**
 1075        * Uninstalls the renderer pane.
 1076        */
 1077       protected void uninstallComponents() {
 1078           if(rendererPane != null) {
 1079               tree.remove(rendererPane);
 1080           }
 1081       }
 1082   
 1083       /**
 1084        * Recomputes the right margin, and invalidates any tree states
 1085        */
 1086       private void redoTheLayout() {
 1087           if (treeState != null) {
 1088               treeState.invalidateSizes();
 1089           }
 1090       }
 1091   
 1092       /**
 1093        * Returns the baseline.
 1094        *
 1095        * @throws NullPointerException {@inheritDoc}
 1096        * @throws IllegalArgumentException {@inheritDoc}
 1097        * @see javax.swing.JComponent#getBaseline(int, int)
 1098        * @since 1.6
 1099        */
 1100       public int getBaseline(JComponent c, int width, int height) {
 1101           super.getBaseline(c, width, height);
 1102           UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults();
 1103           Component renderer = (Component)lafDefaults.get(
 1104                   BASELINE_COMPONENT_KEY);
 1105           if (renderer == null) {
 1106               TreeCellRenderer tcr = createDefaultCellRenderer();
 1107               renderer = tcr.getTreeCellRendererComponent(
 1108                       tree, "a", false, false, false, -1, false);
 1109               lafDefaults.put(BASELINE_COMPONENT_KEY, renderer);
 1110           }
 1111           int rowHeight = tree.getRowHeight();
 1112           int baseline;
 1113           if (rowHeight > 0) {
 1114               baseline = renderer.getBaseline(Integer.MAX_VALUE, rowHeight);
 1115           }
 1116           else {
 1117               Dimension pref = renderer.getPreferredSize();
 1118               baseline = renderer.getBaseline(pref.width, pref.height);
 1119           }
 1120           return baseline + tree.getInsets().top;
 1121       }
 1122   
 1123       /**
 1124        * Returns an enum indicating how the baseline of the component
 1125        * changes as the size changes.
 1126        *
 1127        * @throws NullPointerException {@inheritDoc}
 1128        * @see javax.swing.JComponent#getBaseline(int, int)
 1129        * @since 1.6
 1130        */
 1131       public Component.BaselineResizeBehavior getBaselineResizeBehavior(
 1132               JComponent c) {
 1133           super.getBaselineResizeBehavior(c);
 1134           return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
 1135       }
 1136   
 1137       //
 1138       // Painting routines.
 1139       //
 1140   
 1141       public void paint(Graphics g, JComponent c) {
 1142           if (tree != c) {
 1143               throw new InternalError("incorrect component");
 1144           }
 1145   
 1146           // Should never happen if installed for a UI
 1147           if(treeState == null) {
 1148               return;
 1149           }
 1150   
 1151           Rectangle        paintBounds = g.getClipBounds();
 1152           Insets           insets = tree.getInsets();
 1153           TreePath         initialPath = getClosestPathForLocation
 1154                                          (tree, 0, paintBounds.y);
 1155           Enumeration      paintingEnumerator = treeState.getVisiblePathsFrom
 1156                                                 (initialPath);
 1157           int              row = treeState.getRowForPath(initialPath);
 1158           int              endY = paintBounds.y + paintBounds.height;
 1159   
 1160           drawingCache.clear();
 1161   
 1162           if(initialPath != null && paintingEnumerator != null) {
 1163               TreePath   parentPath = initialPath;
 1164   
 1165               // Draw the lines, knobs, and rows
 1166   
 1167               // Find each parent and have them draw a line to their last child
 1168               parentPath = parentPath.getParentPath();
 1169               while(parentPath != null) {
 1170                   paintVerticalPartOfLeg(g, paintBounds, insets, parentPath);
 1171                   drawingCache.put(parentPath, Boolean.TRUE);
 1172                   parentPath = parentPath.getParentPath();
 1173               }
 1174   
 1175               boolean         done = false;
 1176               // Information for the node being rendered.
 1177               boolean         isExpanded;
 1178               boolean         hasBeenExpanded;
 1179               boolean         isLeaf;
 1180               Rectangle       boundsBuffer = new Rectangle();
 1181               Rectangle       bounds;
 1182               TreePath        path;
 1183               boolean         rootVisible = isRootVisible();
 1184   
 1185               while(!done && paintingEnumerator.hasMoreElements()) {
 1186                   path = (TreePath)paintingEnumerator.nextElement();
 1187                   if(path != null) {
 1188                       isLeaf = treeModel.isLeaf(path.getLastPathComponent());
 1189                       if(isLeaf)
 1190                           isExpanded = hasBeenExpanded = false;
 1191                       else {
 1192                           isExpanded = treeState.getExpandedState(path);
 1193                           hasBeenExpanded = tree.hasBeenExpanded(path);
 1194                       }
 1195                       bounds = getPathBounds(path, insets, boundsBuffer);
 1196                       if(bounds == null)
 1197                           // This will only happen if the model changes out
 1198                           // from under us (usually in another thread).
 1199                           // Swing isn't multithreaded, but I'll put this
 1200                           // check in anyway.
 1201                           return;
 1202                       // See if the vertical line to the parent has been drawn.
 1203                       parentPath = path.getParentPath();
 1204                       if(parentPath != null) {
 1205                           if(drawingCache.get(parentPath) == null) {
 1206                               paintVerticalPartOfLeg(g, paintBounds,
 1207                                                      insets, parentPath);
 1208                               drawingCache.put(parentPath, Boolean.TRUE);
 1209                           }
 1210                           paintHorizontalPartOfLeg(g, paintBounds, insets,
 1211                                                    bounds, path, row,
 1212                                                    isExpanded,
 1213                                                    hasBeenExpanded, isLeaf);
 1214                       }
 1215                       else if(rootVisible && row == 0) {
 1216                           paintHorizontalPartOfLeg(g, paintBounds, insets,
 1217                                                    bounds, path, row,
 1218                                                    isExpanded,
 1219                                                    hasBeenExpanded, isLeaf);
 1220                       }
 1221                       if(shouldPaintExpandControl(path, row, isExpanded,
 1222                                                   hasBeenExpanded, isLeaf)) {
 1223                           paintExpandControl(g, paintBounds, insets, bounds,
 1224                                              path, row, isExpanded,
 1225                                              hasBeenExpanded, isLeaf);
 1226                       }
 1227                       paintRow(g, paintBounds, insets, bounds, path,
 1228                                    row, isExpanded, hasBeenExpanded, isLeaf);
 1229                       if((bounds.y + bounds.height) >= endY)
 1230                           done = true;
 1231                   }
 1232                   else {
 1233                       done = true;
 1234                   }
 1235                   row++;
 1236               }
 1237           }
 1238   
 1239           paintDropLine(g);
 1240   
 1241           // Empty out the renderer pane, allowing renderers to be gc'ed.
 1242           rendererPane.removeAll();
 1243   
 1244           drawingCache.clear();
 1245       }
 1246   
 1247       private boolean isDropLine(JTree.DropLocation loc) {
 1248           return loc != null && loc.getPath() != null && loc.getChildIndex() != -1;
 1249       }
 1250   
 1251       private void paintDropLine(Graphics g) {
 1252           JTree.DropLocation loc = tree.getDropLocation();
 1253           if (!isDropLine(loc)) {
 1254               return;
 1255           }
 1256   
 1257           Color c = UIManager.getColor("Tree.dropLineColor");
 1258           if (c != null) {
 1259               g.setColor(c);
 1260               Rectangle rect = getDropLineRect(loc);
 1261               g.fillRect(rect.x, rect.y, rect.width, rect.height);
 1262           }
 1263       }
 1264   
 1265       private Rectangle getDropLineRect(JTree.DropLocation loc) {
 1266           Rectangle rect = null;
 1267           TreePath path = loc.getPath();
 1268           int index = loc.getChildIndex();
 1269           boolean ltr = leftToRight;
 1270   
 1271           Insets insets = tree.getInsets();
 1272   
 1273           if (tree.getRowCount() == 0) {
 1274               rect = new Rectangle(insets.left,
 1275                                    insets.top,
 1276                                    tree.getWidth() - insets.left - insets.right,
 1277                                    0);
 1278           } else {
 1279               TreeModel model = getModel();
 1280               Object root = model.getRoot();
 1281   
 1282               if (path.getLastPathComponent() == root
 1283                       && index >= model.getChildCount(root)) {
 1284   
 1285                   rect = tree.getRowBounds(tree.getRowCount() - 1);
 1286                   rect.y = rect.y + rect.height;
 1287                   Rectangle xRect;
 1288   
 1289                   if (!tree.isRootVisible()) {
 1290                       xRect = tree.getRowBounds(0);
 1291                   } else if (model.getChildCount(root) == 0){
 1292                       xRect = tree.getRowBounds(0);
 1293                       xRect.x += totalChildIndent;
 1294                       xRect.width -= totalChildIndent + totalChildIndent;
 1295                   } else {
 1296                       TreePath lastChildPath = path.pathByAddingChild(
 1297                           model.getChild(root, model.getChildCount(root) - 1));
 1298                       xRect = tree.getPathBounds(lastChildPath);
 1299                   }
 1300   
 1301                   rect.x = xRect.x;
 1302                   rect.width = xRect.width;
 1303               } else {
 1304                   rect = tree.getPathBounds(path.pathByAddingChild(
 1305                       model.getChild(path.getLastPathComponent(), index)));
 1306               }
 1307           }
 1308   
 1309           if (rect.y != 0) {
 1310               rect.y--;
 1311           }
 1312   
 1313           if (!ltr) {
 1314               rect.x = rect.x + rect.width - 100;
 1315           }
 1316   
 1317           rect.width = 100;
 1318           rect.height = 2;
 1319   
 1320           return rect;
 1321       }
 1322   
 1323       /**
 1324        * Paints the horizontal part of the leg. The receiver should
 1325        * NOT modify <code>clipBounds</code>, or <code>insets</code>.<p>
 1326        * NOTE: <code>parentRow</code> can be -1 if the root is not visible.
 1327        */
 1328       protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds,
 1329                                               Insets insets, Rectangle bounds,
 1330                                               TreePath path, int row,
 1331                                               boolean isExpanded,
 1332                                               boolean hasBeenExpanded, boolean
 1333                                               isLeaf) {
 1334           if (!paintLines) {
 1335               return;
 1336           }
 1337   
 1338           // Don't paint the legs for the root'ish node if the
 1339           int depth = path.getPathCount() - 1;
 1340           if((depth == 0 || (depth == 1 && !isRootVisible())) &&
 1341              !getShowsRootHandles()) {
 1342               return;
 1343           }
 1344   
 1345           int clipLeft = clipBounds.x;
 1346           int clipRight = clipBounds.x + clipBounds.width;
 1347           int clipTop = clipBounds.y;
 1348           int clipBottom = clipBounds.y + clipBounds.height;
 1349           int lineY = bounds.y + bounds.height / 2;
 1350   
 1351           if (leftToRight) {
 1352               int leftX = bounds.x - getRightChildIndent();
 1353               int nodeX = bounds.x - getHorizontalLegBuffer();
 1354   
 1355               if(lineY >= clipTop
 1356                       && lineY < clipBottom
 1357                       && nodeX >= clipLeft
 1358                       && leftX < clipRight
 1359                       && leftX < nodeX) {
 1360   
 1361                   g.setColor(getHashColor());
 1362                   paintHorizontalLine(g, tree, lineY, leftX, nodeX - 1);
 1363               }
 1364           } else {
 1365               int nodeX = bounds.x + bounds.width + getHorizontalLegBuffer();
 1366               int rightX = bounds.x + bounds.width + getRightChildIndent();
 1367   
 1368               if(lineY >= clipTop
 1369                       && lineY < clipBottom
 1370                       && rightX >= clipLeft
 1371                       && nodeX < clipRight
 1372                       && nodeX < rightX) {
 1373   
 1374                   g.setColor(getHashColor());
 1375                   paintHorizontalLine(g, tree, lineY, nodeX, rightX - 1);
 1376               }
 1377           }
 1378       }
 1379   
 1380       /**
 1381        * Paints the vertical part of the leg. The receiver should
 1382        * NOT modify <code>clipBounds</code>, <code>insets</code>.<p>
 1383        */
 1384       protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds,
 1385                                             Insets insets, TreePath path) {
 1386           if (!paintLines) {
 1387               return;
 1388           }
 1389   
 1390           int depth = path.getPathCount() - 1;
 1391           if (depth == 0 && !getShowsRootHandles() && !isRootVisible()) {
 1392               return;
 1393           }
 1394           int lineX = getRowX(-1, depth + 1);
 1395           if (leftToRight) {
 1396               lineX = lineX - getRightChildIndent() + insets.left;
 1397           }
 1398           else {
 1399               lineX = tree.getWidth() - lineX - insets.right +
 1400                       getRightChildIndent() - 1;
 1401           }
 1402           int clipLeft = clipBounds.x;
 1403           int clipRight = clipBounds.x + (clipBounds.width - 1);
 1404   
 1405           if (lineX >= clipLeft && lineX <= clipRight) {
 1406               int clipTop = clipBounds.y;
 1407               int clipBottom = clipBounds.y + clipBounds.height;
 1408               Rectangle parentBounds = getPathBounds(tree, path);
 1409               Rectangle lastChildBounds = getPathBounds(tree,
 1410                                                        getLastChildPath(path));
 1411   
 1412               if(lastChildBounds == null)
 1413                   // This shouldn't happen, but if the model is modified
 1414                   // in another thread it is possible for this to happen.
 1415                   // Swing isn't multithreaded, but I'll add this check in
 1416                   // anyway.
 1417                   return;
 1418   
 1419               int       top;
 1420   
 1421               if(parentBounds == null) {
 1422                   top = Math.max(insets.top + getVerticalLegBuffer(),
 1423                                  clipTop);
 1424               }
 1425               else
 1426                   top = Math.max(parentBounds.y + parentBounds.height +
 1427                                  getVerticalLegBuffer(), clipTop);
 1428               if(depth == 0 && !isRootVisible()) {
 1429                   TreeModel      model = getModel();
 1430   
 1431                   if(model != null) {
 1432                       Object        root = model.getRoot();
 1433   
 1434                       if(model.getChildCount(root) > 0) {
 1435                           parentBounds = getPathBounds(tree, path.
 1436                                     pathByAddingChild(model.getChild(root, 0)));
 1437                           if(parentBounds != null)
 1438                               top = Math.max(insets.top + getVerticalLegBuffer(),
 1439                                              parentBounds.y +
 1440                                              parentBounds.height / 2);
 1441                       }
 1442                   }
 1443               }
 1444   
 1445               int bottom = Math.min(lastChildBounds.y +
 1446                                     (lastChildBounds.height / 2), clipBottom);
 1447   
 1448               if (top <= bottom) {
 1449                   g.setColor(getHashColor());
 1450                   paintVerticalLine(g, tree, lineX, top, bottom);
 1451               }
 1452           }
 1453       }
 1454   
 1455       /**
 1456        * Paints the expand (toggle) part of a row. The receiver should
 1457        * NOT modify <code>clipBounds</code>, or <code>insets</code>.
 1458        */
 1459       protected void paintExpandControl(Graphics g,
 1460                                         Rectangle clipBounds, Insets insets,
 1461                                         Rectangle bounds, TreePath path,
 1462                                         int row, boolean isExpanded,
 1463                                         boolean hasBeenExpanded,
 1464                                         boolean isLeaf) {
 1465           Object       value = path.getLastPathComponent();
 1466   
 1467           // Draw icons if not a leaf and either hasn't been loaded,
 1468           // or the model child count is > 0.
 1469           if (!isLeaf && (!hasBeenExpanded ||
 1470                           treeModel.getChildCount(value) > 0)) {
 1471               int middleXOfKnob;
 1472               if (leftToRight) {
 1473                   middleXOfKnob = bounds.x - getRightChildIndent() + 1;
 1474               } else {
 1475                   middleXOfKnob = bounds.x + bounds.width + getRightChildIndent() - 1;
 1476               }
 1477               int middleYOfKnob = bounds.y + (bounds.height / 2);
 1478   
 1479               if (isExpanded) {
 1480                   Icon expandedIcon = getExpandedIcon();
 1481                   if(expandedIcon != null)
 1482                     drawCentered(tree, g, expandedIcon, middleXOfKnob,
 1483                                  middleYOfKnob );
 1484               }
 1485               else {
 1486                   Icon collapsedIcon = getCollapsedIcon();
 1487                   if(collapsedIcon != null)
 1488                     drawCentered(tree, g, collapsedIcon, middleXOfKnob,
 1489                                  middleYOfKnob);
 1490               }
 1491           }
 1492       }
 1493   
 1494       /**
 1495        * Paints the renderer part of a row. The receiver should
 1496        * NOT modify <code>clipBounds</code>, or <code>insets</code>.
 1497        */
 1498       protected void paintRow(Graphics g, Rectangle clipBounds,
 1499                               Insets insets, Rectangle bounds, TreePath path,
 1500                               int row, boolean isExpanded,
 1501                               boolean hasBeenExpanded, boolean isLeaf) {
 1502           // Don't paint the renderer if editing this row.
 1503           if(editingComponent != null && editingRow == row)
 1504               return;
 1505   
 1506           int leadIndex;
 1507   
 1508           if(tree.hasFocus()) {
 1509               leadIndex = getLeadSelectionRow();
 1510           }
 1511           else
 1512               leadIndex = -1;
 1513   
 1514           Component component;
 1515   
 1516           component = currentCellRenderer.getTreeCellRendererComponent
 1517                         (tree, path.getLastPathComponent(),
 1518                          tree.isRowSelected(row), isExpanded, isLeaf, row,
 1519                          (leadIndex == row));
 1520   
 1521           rendererPane.paintComponent(g, component, tree, bounds.x, bounds.y,
 1522                                       bounds.width, bounds.height, true);
 1523       }
 1524   
 1525       /**
 1526        * Returns true if the expand (toggle) control should be drawn for
 1527        * the specified row.
 1528        */
 1529       protected boolean shouldPaintExpandControl(TreePath path, int row,
 1530                                                  boolean isExpanded,
 1531                                                  boolean hasBeenExpanded,
 1532                                                  boolean isLeaf) {
 1533           if(isLeaf)
 1534               return false;
 1535   
 1536           int              depth = path.getPathCount() - 1;
 1537   
 1538           if((depth == 0 || (depth == 1 && !isRootVisible())) &&
 1539              !getShowsRootHandles())
 1540               return false;
 1541           return true;
 1542       }
 1543   
 1544       /**
 1545        * Paints a vertical line.
 1546        */
 1547       protected void paintVerticalLine(Graphics g, JComponent c, int x, int top,
 1548                                       int bottom) {
 1549           if (lineTypeDashed) {
 1550               drawDashedVerticalLine(g, x, top, bottom);
 1551           } else {
 1552               g.drawLine(x, top, x, bottom);
 1553           }
 1554       }
 1555   
 1556       /**
 1557        * Paints a horizontal line.
 1558        */
 1559       protected void paintHorizontalLine(Graphics g, JComponent c, int y,
 1560                                         int left, int right) {
 1561           if (lineTypeDashed) {
 1562               drawDashedHorizontalLine(g, y, left, right);
 1563           } else {
 1564               g.drawLine(left, y, right, y);
 1565           }
 1566       }
 1567   
 1568       /**
 1569        * The vertical element of legs between nodes starts at the bottom of the
 1570        * parent node by default.  This method makes the leg start below that.
 1571        */
 1572       protected int getVerticalLegBuffer() {
 1573           return 0;
 1574       }
 1575   
 1576       /**
 1577        * The horizontal element of legs between nodes starts at the
 1578        * right of the left-hand side of the child node by default.  This
 1579        * method makes the leg end before that.
 1580        */
 1581       protected int getHorizontalLegBuffer() {
 1582           return 0;
 1583       }
 1584   
 1585       private int findCenteredX(int x, int iconWidth) {
 1586           return leftToRight
 1587                  ? x - (int)Math.ceil(iconWidth / 2.0)
 1588                  : x - (int)Math.floor(iconWidth / 2.0);
 1589       }
 1590   
 1591       //
 1592       // Generic painting methods
 1593       //
 1594   
 1595       // Draws the icon centered at (x,y)
 1596       protected void drawCentered(Component c, Graphics graphics, Icon icon,
 1597                                   int x, int y) {
 1598           icon.paintIcon(c, graphics,
 1599                         findCenteredX(x, icon.getIconWidth()),
 1600                         y - icon.getIconHeight() / 2);
 1601       }
 1602   
 1603       // This method is slow -- revisit when Java2D is ready.
 1604       // assumes x1 <= x2
 1605       protected void drawDashedHorizontalLine(Graphics g, int y, int x1, int x2){
 1606           // Drawing only even coordinates helps join line segments so they
 1607           // appear as one line.  This can be defeated by translating the
 1608           // Graphics by an odd amount.
 1609           x1 += (x1 % 2);
 1610   
 1611           for (int x = x1; x <= x2; x+=2) {
 1612               g.drawLine(x, y, x, y);
 1613           }
 1614       }
 1615   
 1616       // This method is slow -- revisit when Java2D is ready.
 1617       // assumes y1 <= y2
 1618       protected void drawDashedVerticalLine(Graphics g, int x, int y1, int y2) {
 1619           // Drawing only even coordinates helps join line segments so they
 1620           // appear as one line.  This can be defeated by translating the
 1621           // Graphics by an odd amount.
 1622           y1 += (y1 % 2);
 1623   
 1624           for (int y = y1; y <= y2; y+=2) {
 1625               g.drawLine(x, y, x, y);
 1626           }
 1627       }
 1628   
 1629       //
 1630       // Various local methods
 1631       //
 1632   
 1633       /**
 1634        * Returns the location, along the x-axis, to render a particular row
 1635        * at. The return value does not include any Insets specified on the JTree.
 1636        * This does not check for the validity of the row or depth, it is assumed
 1637        * to be correct and will not throw an Exception if the row or depth
 1638        * doesn't match that of the tree.
 1639        *
 1640        * @param row Row to return x location for
 1641        * @param depth Depth of the row
 1642        * @return amount to indent the given row.
 1643        * @since 1.5
 1644        */
 1645       protected int getRowX(int row, int depth) {
 1646           return totalChildIndent * (depth + depthOffset);
 1647       }
 1648   
 1649       /**
 1650        * Makes all the nodes that are expanded in JTree expanded in LayoutCache.
 1651        * This invokes updateExpandedDescendants with the root path.
 1652        */
 1653       protected void updateLayoutCacheExpandedNodes() {
 1654           if(treeModel != null && treeModel.getRoot() != null)
 1655               updateExpandedDescendants(new TreePath(treeModel.getRoot()));
 1656       }
 1657   
 1658       private void updateLayoutCacheExpandedNodesIfNecessary() {
 1659           if (treeModel != null && treeModel.getRoot() != null) {
 1660               TreePath rootPath = new TreePath(treeModel.getRoot());
 1661               if (tree.isExpanded(rootPath)) {
 1662                   updateLayoutCacheExpandedNodes();
 1663               } else {
 1664                   treeState.setExpandedState(rootPath, false);
 1665               }
 1666           }
 1667       }
 1668   
 1669       /**
 1670        * Updates the expanded state of all the descendants of <code>path</code>
 1671        * by getting the expanded descendants from the tree and forwarding
 1672        * to the tree state.
 1673        */
 1674       protected void updateExpandedDescendants(TreePath path) {
 1675           completeEditing();
 1676           if(treeState != null) {
 1677               treeState.setExpandedState(path, true);
 1678   
 1679               Enumeration   descendants = tree.getExpandedDescendants(path);
 1680   
 1681               if(descendants != null) {
 1682                   while(descendants.hasMoreElements()) {
 1683                       path = (TreePath)descendants.nextElement();
 1684                       treeState.setExpandedState(path, true);
 1685                   }
 1686               }
 1687               updateLeadRow();
 1688               updateSize();
 1689           }
 1690       }
 1691   
 1692       /**
 1693        * Returns a path to the last child of <code>parent</code>.
 1694        */
 1695       protected TreePath getLastChildPath(TreePath parent) {
 1696           if(treeModel != null) {
 1697               int         childCount = treeModel.getChildCount
 1698                   (parent.getLastPathComponent());
 1699   
 1700               if(childCount > 0)
 1701                   return parent.pathByAddingChild(treeModel.getChild
 1702                              (parent.getLastPathComponent(), childCount - 1));
 1703           }
 1704           return null;
 1705       }
 1706   
 1707       /**
 1708        * Updates how much each depth should be offset by.
 1709        */
 1710       protected void updateDepthOffset() {
 1711           if(isRootVisible()) {
 1712               if(getShowsRootHandles())
 1713                   depthOffset = 1;
 1714               else
 1715                   depthOffset = 0;
 1716           }
 1717           else if(!getShowsRootHandles())
 1718               depthOffset = -1;
 1719           else
 1720               depthOffset = 0;
 1721       }
 1722   
 1723       /**
 1724         * Updates the cellEditor based on the editability of the JTree that
 1725         * we're contained in.  If the tree is editable but doesn't have a
 1726         * cellEditor, a basic one will be used.
 1727         */
 1728       protected void updateCellEditor() {
 1729           TreeCellEditor        newEditor;
 1730   
 1731           completeEditing();
 1732           if(tree == null)
 1733               newEditor = null;
 1734           else {
 1735               if(tree.isEditable()) {
 1736                   newEditor = tree.getCellEditor();
 1737                   if(newEditor == null) {
 1738                       newEditor = createDefaultCellEditor();
 1739                       if(newEditor != null) {
 1740                           tree.setCellEditor(newEditor);
 1741                           createdCellEditor = true;
 1742                       }
 1743                   }
 1744               }
 1745               else
 1746                   newEditor = null;
 1747           }
 1748           if(newEditor != cellEditor) {
 1749               if(cellEditor != null && cellEditorListener != null)
 1750                   cellEditor.removeCellEditorListener(cellEditorListener);
 1751               cellEditor = newEditor;
 1752               if(cellEditorListener == null)
 1753                   cellEditorListener = createCellEditorListener();
 1754               if(newEditor != null && cellEditorListener != null)
 1755                   newEditor.addCellEditorListener(cellEditorListener);
 1756               createdCellEditor = false;
 1757           }
 1758       }
 1759   
 1760       /**
 1761         * Messaged from the tree we're in when the renderer has changed.
 1762         */
 1763       protected void updateRenderer() {
 1764           if(tree != null) {
 1765               TreeCellRenderer      newCellRenderer;
 1766   
 1767               newCellRenderer = tree.getCellRenderer();
 1768               if(newCellRenderer == null) {
 1769                   tree.setCellRenderer(createDefaultCellRenderer());
 1770                   createdRenderer = true;
 1771               }
 1772               else {
 1773                   createdRenderer = false;
 1774                   currentCellRenderer = newCellRenderer;
 1775                   if(createdCellEditor) {
 1776                       tree.setCellEditor(null);
 1777                   }
 1778               }
 1779           }
 1780           else {
 1781               createdRenderer = false;
 1782               currentCellRenderer = null;
 1783           }
 1784           updateCellEditor();
 1785       }
 1786   
 1787       /**
 1788        * Resets the TreeState instance based on the tree we're providing the
 1789        * look and feel for.
 1790        */
 1791       protected void configureLayoutCache() {
 1792           if(treeState != null && tree != null) {
 1793               if(nodeDimensions == null)
 1794                   nodeDimensions = createNodeDimensions();
 1795               treeState.setNodeDimensions(nodeDimensions);
 1796               treeState.setRootVisible(tree.isRootVisible());
 1797               treeState.setRowHeight(tree.getRowHeight());
 1798               treeState.setSelectionModel(getSelectionModel());
 1799               // Only do this if necessary, may loss state if call with
 1800               // same model as it currently has.
 1801               if(treeState.getModel() != tree.getModel())
 1802                   treeState.setModel(tree.getModel());
 1803               updateLayoutCacheExpandedNodesIfNecessary();
 1804               // Create a listener to update preferred size when bounds
 1805               // changes, if necessary.
 1806               if(isLargeModel()) {
 1807                   if(componentListener == null) {
 1808                       componentListener = createComponentListener();
 1809                       if(componentListener != null)
 1810                           tree.addComponentListener(componentListener);
 1811                   }
 1812               }
 1813               else if(componentListener != null) {
 1814                   tree.removeComponentListener(componentListener);
 1815                   componentListener = null;
 1816               }
 1817           }
 1818           else if(componentListener != null) {
 1819               tree.removeComponentListener(componentListener);
 1820               componentListener = null;
 1821           }
 1822       }
 1823   
 1824       /**
 1825        * Marks the cached size as being invalid, and messages the
 1826        * tree with <code>treeDidChange</code>.
 1827        */
 1828       protected void updateSize() {
 1829           validCachedPreferredSize = false;
 1830           tree.treeDidChange();
 1831       }
 1832   
 1833       private void updateSize0() {
 1834           validCachedPreferredSize = false;
 1835           tree.revalidate();
 1836       }
 1837   
 1838       /**
 1839        * Updates the <code>preferredSize</code> instance variable,
 1840        * which is returned from <code>getPreferredSize()</code>.<p>
 1841        * For left to right orientations, the size is determined from the
 1842        * current AbstractLayoutCache. For RTL orientations, the preferred size
 1843        * becomes the width minus the minimum x position.
 1844        */
 1845       protected void updateCachedPreferredSize() {
 1846           if(treeState != null) {
 1847               Insets               i = tree.getInsets();
 1848   
 1849               if(isLargeModel()) {
 1850                   Rectangle            visRect = tree.getVisibleRect();
 1851   
 1852                   if (visRect.x == 0 && visRect.y == 0 &&
 1853                           visRect.width == 0 && visRect.height == 0 &&
 1854                           tree.getVisibleRowCount() > 0) {
 1855                       // The tree doesn't have a valid bounds yet. Calculate
 1856                       // based on visible row count.
 1857                       visRect.width = 1;
 1858                       visRect.height = tree.getRowHeight() *
 1859                               tree.getVisibleRowCount();
 1860                   } else {
 1861                       visRect.x -= i.left;
 1862                       visRect.y -= i.top;
 1863                   }
 1864                   preferredSize.width = treeState.getPreferredWidth(visRect);
 1865               }
 1866               else {
 1867                   preferredSize.width = treeState.getPreferredWidth(null);
 1868               }
 1869               preferredSize.height = treeState.getPreferredHeight();
 1870               preferredSize.width += i.left + i.right;
 1871               preferredSize.height += i.top + i.bottom;
 1872           }
 1873           validCachedPreferredSize = true;
 1874       }
 1875   
 1876       /**
 1877         * Messaged from the VisibleTreeNode after it has been expanded.
 1878         */
 1879       protected void pathWasExpanded(TreePath path) {
 1880           if(tree != null) {
 1881               tree.fireTreeExpanded(path);
 1882           }
 1883       }
 1884   
 1885       /**
 1886         * Messaged from the VisibleTreeNode after it has collapsed.
 1887         */
 1888       protected void pathWasCollapsed(TreePath path) {
 1889           if(tree != null) {
 1890               tree.fireTreeCollapsed(path);
 1891           }
 1892       }
 1893   
 1894       /**
 1895         * Ensures that the rows identified by beginRow through endRow are
 1896         * visible.
 1897         */
 1898       protected void ensureRowsAreVisible(int beginRow, int endRow) {
 1899           if(tree != null && beginRow >= 0 && endRow < getRowCount(tree)) {
 1900               boolean scrollVert = DefaultLookup.getBoolean(tree, this,
 1901                                 "Tree.scrollsHorizontallyAndVertically", false);
 1902               if(beginRow == endRow) {
 1903                   Rectangle     scrollBounds = getPathBounds(tree, getPathForRow
 1904                                                              (tree, beginRow));
 1905   
 1906                   if(scrollBounds != null) {
 1907                       if (!scrollVert) {
 1908                           scrollBounds.x = tree.getVisibleRect().x;
 1909                           scrollBounds.width = 1;
 1910                       }
 1911                       tree.scrollRectToVisible(scrollBounds);
 1912                   }
 1913               }
 1914               else {
 1915                   Rectangle   beginRect = getPathBounds(tree, getPathForRow
 1916                                                         (tree, beginRow));
 1917                   Rectangle   visRect = tree.getVisibleRect();
 1918                   Rectangle   testRect = beginRect;
 1919                   int         beginY = beginRect.y;
 1920                   int         maxY = beginY + visRect.height;
 1921   
 1922                   for(int counter = beginRow + 1; counter <= endRow; counter++) {
 1923                       testRect = getPathBounds(tree,
 1924                                                getPathForRow(tree, counter));
 1925                       if((testRect.y + testRect.height) > maxY)
 1926                           counter = endRow;
 1927                   }
 1928                   tree.scrollRectToVisible(new Rectangle(visRect.x, beginY, 1,
 1929                                                     testRect.y + testRect.height-
 1930                                                     beginY));
 1931               }
 1932           }
 1933       }
 1934   
 1935       /** Sets the preferred minimum size.
 1936         */
 1937       public void setPreferredMinSize(Dimension newSize) {
 1938           preferredMinSize = newSize;
 1939       }
 1940   
 1941       /** Returns the minimum preferred size.
 1942         */
 1943       public Dimension getPreferredMinSize() {
 1944           if(preferredMinSize == null)
 1945               return null;
 1946           return new Dimension(preferredMinSize);
 1947       }
 1948   
 1949       /** Returns the preferred size to properly display the tree,
 1950         * this is a cover method for getPreferredSize(c, false).
 1951         */
 1952       public Dimension getPreferredSize(JComponent c) {
 1953           return getPreferredSize(c, true);
 1954       }
 1955   
 1956       /** Returns the preferred size to represent the tree in
 1957         * <I>c</I>.  If <I>checkConsistancy</I> is true
 1958         * <b>checkConsistancy</b> is messaged first.
 1959         */
 1960       public Dimension getPreferredSize(JComponent c,
 1961                                         boolean checkConsistancy) {
 1962           Dimension       pSize = this.getPreferredMinSize();
 1963   
 1964           if(!validCachedPreferredSize)
 1965               updateCachedPreferredSize();
 1966           if(tree != null) {
 1967               if(pSize != null)
 1968                   return new Dimension(Math.max(pSize.width,
 1969                                                 preferredSize.width),
 1970                                 Math.max(pSize.height, preferredSize.height));
 1971               return new Dimension(preferredSize.width, preferredSize.height);
 1972           }
 1973           else if(pSize != null)
 1974               return pSize;
 1975           else
 1976               return new Dimension(0, 0);
 1977       }
 1978   
 1979       /**
 1980         * Returns the minimum size for this component.  Which will be
 1981         * the min preferred size or 0, 0.
 1982         */
 1983       public Dimension getMinimumSize(JComponent c) {
 1984           if(this.getPreferredMinSize() != null)
 1985               return this.getPreferredMinSize();
 1986           return new Dimension(0, 0);
 1987       }
 1988   
 1989       /**
 1990         * Returns the maximum size for this component, which will be the
 1991         * preferred size if the instance is currently in a JTree, or 0, 0.
 1992         */
 1993       public Dimension getMaximumSize(JComponent c) {
 1994           if(tree != null)
 1995               return getPreferredSize(tree);
 1996           if(this.getPreferredMinSize() != null)
 1997               return this.getPreferredMinSize();
 1998           return new Dimension(0, 0);
 1999       }
 2000   
 2001   
 2002       /**
 2003        * Messages to stop the editing session. If the UI the receiver
 2004        * is providing the look and feel for returns true from
 2005        * <code>getInvokesStopCellEditing</code>, stopCellEditing will
 2006        * invoked on the current editor. Then completeEditing will
 2007        * be messaged with false, true, false to cancel any lingering
 2008        * editing.
 2009        */
 2010       protected void completeEditing() {
 2011           /* If should invoke stopCellEditing, try that */
 2012           if(tree.getInvokesStopCellEditing() &&
 2013              stopEditingInCompleteEditing && editingComponent != null) {
 2014               cellEditor.stopCellEditing();
 2015           }
 2016           /* Invoke cancelCellEditing, this will do nothing if stopCellEditing
 2017              was successful. */
 2018           completeEditing(false, true, false);
 2019       }
 2020   
 2021       /**
 2022         * Stops the editing session.  If messageStop is true the editor
 2023         * is messaged with stopEditing, if messageCancel is true the
 2024         * editor is messaged with cancelEditing. If messageTree is true
 2025         * the treeModel is messaged with valueForPathChanged.
 2026         */
 2027       protected void completeEditing(boolean messageStop,
 2028                                      boolean messageCancel,
 2029                                      boolean messageTree) {
 2030           if(stopEditingInCompleteEditing && editingComponent != null) {
 2031               Component             oldComponent = editingComponent;
 2032               TreePath              oldPath = editingPath;
 2033               TreeCellEditor        oldEditor = cellEditor;
 2034               Object                newValue = oldEditor.getCellEditorValue();
 2035               Rectangle             editingBounds = getPathBounds(tree,
 2036                                                                   editingPath);
 2037               boolean               requestFocus = (tree != null &&
 2038                                      (tree.hasFocus() || SwingUtilities.
 2039                                       findFocusOwner(editingComponent) != null));
 2040   
 2041               editingComponent = null;
 2042               editingPath = null;
 2043               if(messageStop)
 2044                   oldEditor.stopCellEditing();
 2045               else if(messageCancel)
 2046                   oldEditor.cancelCellEditing();
 2047               tree.remove(oldComponent);
 2048               if(editorHasDifferentSize) {
 2049                   treeState.invalidatePathBounds(oldPath);
 2050                   updateSize();
 2051               }
 2052               else {
 2053                   editingBounds.x = 0;
 2054                   editingBounds.width = tree.getSize().width;
 2055                   tree.repaint(editingBounds);
 2056               }
 2057               if(requestFocus)
 2058                   tree.requestFocus();
 2059               if(messageTree)
 2060                   treeModel.valueForPathChanged(oldPath, newValue);
 2061           }
 2062       }
 2063   
 2064       // cover method for startEditing that allows us to pass extra
 2065       // information into that method via a class variable
 2066       private boolean startEditingOnRelease(TreePath path,
 2067                                             MouseEvent event,
 2068                                             MouseEvent releaseEvent) {
 2069           this.releaseEvent = releaseEvent;
 2070           try {
 2071               return startEditing(path, event);
 2072           } finally {
 2073               this.releaseEvent = null;
 2074           }
 2075       }
 2076   
 2077       /**
 2078         * Will start editing for node if there is a cellEditor and
 2079         * shouldSelectCell returns true.<p>
 2080         * This assumes that path is valid and visible.
 2081         */
 2082       protected boolean startEditing(TreePath path, MouseEvent event) {
 2083           if (isEditing(tree) && tree.getInvokesStopCellEditing() &&
 2084                                  !stopEditing(tree)) {
 2085               return false;
 2086           }
 2087           completeEditing();
 2088           if(cellEditor != null && tree.isPathEditable(path)) {
 2089               int           row = getRowForPath(tree, path);
 2090   
 2091               if(cellEditor.isCellEditable(event)) {
 2092                   editingComponent = cellEditor.getTreeCellEditorComponent
 2093                         (tree, path.getLastPathComponent(),
 2094                          tree.isPathSelected(path), tree.isExpanded(path),
 2095                          treeModel.isLeaf(path.getLastPathComponent()), row);
 2096                   Rectangle           nodeBounds = getPathBounds(tree, path);
 2097   
 2098                   editingRow = row;
 2099   
 2100                   Dimension editorSize = editingComponent.getPreferredSize();
 2101   
 2102                   // Only allow odd heights if explicitly set.
 2103                   if(editorSize.height != nodeBounds.height &&
 2104                      getRowHeight() > 0)
 2105                       editorSize.height = getRowHeight();
 2106   
 2107                   if(editorSize.width != nodeBounds.width ||
 2108                      editorSize.height != nodeBounds.height) {
 2109                       // Editor wants different width or height, invalidate
 2110                       // treeState and relayout.
 2111                       editorHasDifferentSize = true;
 2112                       treeState.invalidatePathBounds(path);
 2113                       updateSize();
 2114                       // To make sure x/y are updated correctly, fetch
 2115                       // the bounds again.
 2116                       nodeBounds = getPathBounds(tree, path);
 2117                   }
 2118                   else
 2119                       editorHasDifferentSize = false;
 2120                   tree.add(editingComponent);
 2121                   editingComponent.setBounds(nodeBounds.x, nodeBounds.y,
 2122                                              nodeBounds.width,
 2123                                              nodeBounds.height);
 2124                   editingPath = path;
 2125                   if (editingComponent instanceof JComponent) {
 2126                       ((JComponent)editingComponent).revalidate();
 2127                   } else {
 2128                       editingComponent.validate();
 2129                   }
 2130                   editingComponent.repaint();
 2131                   if(cellEditor.shouldSelectCell(event)) {
 2132                       stopEditingInCompleteEditing = false;
 2133                       tree.setSelectionRow(row);
 2134                       stopEditingInCompleteEditing = true;
 2135                   }
 2136   
 2137                   Component focusedComponent = SwingUtilities2.
 2138                                    compositeRequestFocus(editingComponent);
 2139                   boolean selectAll = true;
 2140   
 2141                   if(event != null && event instanceof MouseEvent) {
 2142                       /* Find the component that will get forwarded all the
 2143                          mouse events until mouseReleased. */
 2144                       Point          componentPoint = SwingUtilities.convertPoint
 2145                           (tree, new Point(event.getX(), event.getY()),
 2146                            editingComponent);
 2147   
 2148                       /* Create an instance of BasicTreeMouseListener to handle
 2149                          passing the mouse/motion events to the necessary
 2150                          component. */
 2151                       // We really want similar behavior to getMouseEventTarget,
 2152                       // but it is package private.
 2153                       Component activeComponent = SwingUtilities.
 2154                                       getDeepestComponentAt(editingComponent,
 2155                                          componentPoint.x, componentPoint.y);
 2156                       if (activeComponent != null) {
 2157                           MouseInputHandler handler =
 2158                               new MouseInputHandler(tree, activeComponent,
 2159                                                     event, focusedComponent);
 2160   
 2161                           if (releaseEvent != null) {
 2162                               handler.mouseReleased(releaseEvent);
 2163                           }
 2164   
 2165                           selectAll = false;
 2166                       }
 2167                   }
 2168                   if (selectAll && focusedComponent instanceof JTextField) {
 2169                       ((JTextField)focusedComponent).selectAll();
 2170                   }
 2171                   return true;
 2172               }
 2173               else
 2174                   editingComponent = null;
 2175           }
 2176           return false;
 2177       }
 2178   
 2179       //
 2180       // Following are primarily for handling mouse events.
 2181       //
 2182   
 2183       /**
 2184        * If the <code>mouseX</code> and <code>mouseY</code> are in the
 2185        * expand/collapse region of the <code>row</code>, this will toggle
 2186        * the row.
 2187        */
 2188       protected void checkForClickInExpandControl(TreePath path,
 2189                                                   int mouseX, int mouseY) {
 2190         if (isLocationInExpandControl(path, mouseX, mouseY)) {
 2191             handleExpandControlClick(path, mouseX, mouseY);
 2192           }
 2193       }
 2194   
 2195       /**
 2196        * Returns true if <code>mouseX</code> and <code>mouseY</code> fall
 2197        * in the area of row that is used to expand/collapse the node and
 2198        * the node at <code>row</code> does not represent a leaf.
 2199        */
 2200       protected boolean isLocationInExpandControl(TreePath path,
 2201                                                   int mouseX, int mouseY) {
 2202           if(path != null && !treeModel.isLeaf(path.getLastPathComponent())){
 2203               int                     boxWidth;
 2204               Insets                  i = tree.getInsets();
 2205   
 2206               if(getExpandedIcon() != null)
 2207                   boxWidth = getExpandedIcon().getIconWidth();
 2208               else
 2209                   boxWidth = 8;
 2210   
 2211               int boxLeftX = getRowX(tree.getRowForPath(path),
 2212                                      path.getPathCount() - 1);
 2213   
 2214               if (leftToRight) {
 2215                   boxLeftX = boxLeftX + i.left - getRightChildIndent() + 1;
 2216               } else {
 2217                   boxLeftX = tree.getWidth() - boxLeftX - i.right + getRightChildIndent() - 1;
 2218               }
 2219   
 2220               boxLeftX = findCenteredX(boxLeftX, boxWidth);
 2221   
 2222               return (mouseX >= boxLeftX && mouseX < (boxLeftX + boxWidth));
 2223           }
 2224           return false;
 2225       }
 2226   
 2227       /**
 2228        * Messaged when the user clicks the particular row, this invokes
 2229        * toggleExpandState.
 2230        */
 2231       protected void handleExpandControlClick(TreePath path, int mouseX,
 2232                                               int mouseY) {
 2233           toggleExpandState(path);
 2234       }
 2235   
 2236       /**
 2237        * Expands path if it is not expanded, or collapses row if it is expanded.
 2238        * If expanding a path and JTree scrolls on expand, ensureRowsAreVisible
 2239        * is invoked to scroll as many of the children to visible as possible
 2240        * (tries to scroll to last visible descendant of path).
 2241        */
 2242       protected void toggleExpandState(TreePath path) {
 2243           if(!tree.isExpanded(path)) {
 2244               int       row = getRowForPath(tree, path);
 2245   
 2246               tree.expandPath(path);
 2247               updateSize();
 2248               if(row != -1) {
 2249                   if(tree.getScrollsOnExpand())
 2250                       ensureRowsAreVisible(row, row + treeState.
 2251                                            getVisibleChildCount(path));
 2252                   else
 2253                       ensureRowsAreVisible(row, row);
 2254               }
 2255           }
 2256           else {
 2257               tree.collapsePath(path);
 2258               updateSize();
 2259           }
 2260       }
 2261   
 2262       /**
 2263        * Returning true signifies a mouse event on the node should toggle
 2264        * the selection of only the row under mouse.
 2265        */
 2266       protected boolean isToggleSelectionEvent(MouseEvent event) {
 2267           return (SwingUtilities.isLeftMouseButton(event) &&
 2268                   event.isControlDown());
 2269       }
 2270   
 2271       /**
 2272        * Returning true signifies a mouse event on the node should select
 2273        * from the anchor point.
 2274        */
 2275       protected boolean isMultiSelectEvent(MouseEvent event) {
 2276           return (SwingUtilities.isLeftMouseButton(event) &&
 2277                   event.isShiftDown());
 2278       }
 2279   
 2280       /**
 2281        * Returning true indicates the row under the mouse should be toggled
 2282        * based on the event. This is invoked after checkForClickInExpandControl,
 2283        * implying the location is not in the expand (toggle) control
 2284        */
 2285       protected boolean isToggleEvent(MouseEvent event) {
 2286           if(!SwingUtilities.isLeftMouseButton(event)) {
 2287               return false;
 2288           }
 2289           int           clickCount = tree.getToggleClickCount();
 2290   
 2291           if(clickCount <= 0) {
 2292               return false;
 2293           }
 2294           return ((event.getClickCount() % clickCount) == 0);
 2295       }
 2296   
 2297       /**
 2298        * Messaged to update the selection based on a MouseEvent over a
 2299        * particular row. If the event is a toggle selection event, the
 2300        * row is either selected, or deselected. If the event identifies
 2301        * a multi selection event, the selection is updated from the
 2302        * anchor point. Otherwise the row is selected, and if the event
 2303        * specified a toggle event the row is expanded/collapsed.
 2304        */
 2305       protected void selectPathForEvent(TreePath path, MouseEvent event) {
 2306           /* Adjust from the anchor point. */
 2307           if(isMultiSelectEvent(event)) {
 2308               TreePath    anchor = getAnchorSelectionPath();
 2309               int         anchorRow = (anchor == null) ? -1 :
 2310                                       getRowForPath(tree, anchor);
 2311   
 2312               if(anchorRow == -1 || tree.getSelectionModel().
 2313                         getSelectionMode() == TreeSelectionModel.
 2314                         SINGLE_TREE_SELECTION) {
 2315                   tree.setSelectionPath(path);
 2316               }
 2317               else {
 2318                   int          row = getRowForPath(tree, path);
 2319                   TreePath     lastAnchorPath = anchor;
 2320   
 2321                   if (isToggleSelectionEvent(event)) {
 2322                       if (tree.isRowSelected(anchorRow)) {
 2323                           tree.addSelectionInterval(anchorRow, row);
 2324                       } else {
 2325                           tree.removeSelectionInterval(anchorRow, row);
 2326                           tree.addSelectionInterval(row, row);
 2327                       }
 2328                   } else if(row < anchorRow) {
 2329                       tree.setSelectionInterval(row, anchorRow);
 2330                   } else {
 2331                       tree.setSelectionInterval(anchorRow, row);
 2332                   }
 2333                   lastSelectedRow = row;
 2334                   setAnchorSelectionPath(lastAnchorPath);
 2335                   setLeadSelectionPath(path);
 2336               }
 2337           }
 2338   
 2339           // Should this event toggle the selection of this row?
 2340           /* Control toggles just this node. */
 2341           else if(isToggleSelectionEvent(event)) {
 2342               if(tree.isPathSelected(path))
 2343                   tree.removeSelectionPath(path);
 2344               else
 2345                   tree.addSelectionPath(path);
 2346               lastSelectedRow = getRowForPath(tree, path);
 2347               setAnchorSelectionPath(path);
 2348               setLeadSelectionPath(path);
 2349           }
 2350   
 2351           /* Otherwise set the selection to just this interval. */
 2352           else if(SwingUtilities.isLeftMouseButton(event)) {
 2353               tree.setSelectionPath(path);
 2354               if(isToggleEvent(event)) {
 2355                   toggleExpandState(path);
 2356               }
 2357           }
 2358       }
 2359   
 2360       /**
 2361        * @return true if the node at <code>row</code> is a leaf.
 2362        */
 2363       protected boolean isLeaf(int row) {
 2364           TreePath          path = getPathForRow(tree, row);
 2365   
 2366           if(path != null)
 2367               return treeModel.isLeaf(path.getLastPathComponent());
 2368           // Have to return something here...
 2369           return true;
 2370       }
 2371   
 2372       //
 2373       // The following selection methods (lead/anchor) are covers for the
 2374       // methods in JTree.
 2375       //
 2376       private void setAnchorSelectionPath(TreePath newPath) {
 2377           ignoreLAChange = true;
 2378           try {
 2379               tree.setAnchorSelectionPath(newPath);
 2380           } finally{
 2381               ignoreLAChange = false;
 2382           }
 2383       }
 2384   
 2385       private TreePath getAnchorSelectionPath() {
 2386           return tree.getAnchorSelectionPath();
 2387       }
 2388   
 2389       private void setLeadSelectionPath(TreePath newPath) {
 2390           setLeadSelectionPath(newPath, false);
 2391       }
 2392   
 2393       private void setLeadSelectionPath(TreePath newPath, boolean repaint) {
 2394           Rectangle       bounds = repaint ?
 2395                               getPathBounds(tree, getLeadSelectionPath()) : null;
 2396   
 2397           ignoreLAChange = true;
 2398           try {
 2399               tree.setLeadSelectionPath(newPath);
 2400           } finally {
 2401               ignoreLAChange = false;
 2402           }
 2403           leadRow = getRowForPath(tree, newPath);
 2404   
 2405           if(repaint) {
 2406               if(bounds != null)
 2407                   tree.repaint(bounds);
 2408               bounds = getPathBounds(tree, newPath);
 2409               if(bounds != null)
 2410                   tree.repaint(bounds);
 2411           }
 2412       }
 2413   
 2414       private TreePath getLeadSelectionPath() {
 2415           return tree.getLeadSelectionPath();
 2416       }
 2417   
 2418       private void updateLeadRow() {
 2419           leadRow = getRowForPath(tree, getLeadSelectionPath());
 2420       }
 2421   
 2422       private int getLeadSelectionRow() {
 2423           return leadRow;
 2424       }
 2425   
 2426       /**
 2427        * Extends the selection from the anchor to make <code>newLead</code>
 2428        * the lead of the selection. This does not scroll.
 2429        */
 2430       private void extendSelection(TreePath newLead) {
 2431           TreePath           aPath = getAnchorSelectionPath();
 2432           int                aRow = (aPath == null) ? -1 :
 2433                                     getRowForPath(tree, aPath);
 2434           int                newIndex = getRowForPath(tree, newLead);
 2435   
 2436           if(aRow == -1) {
 2437               tree.setSelectionRow(newIndex);
 2438           }
 2439           else {
 2440               if(aRow < newIndex) {
 2441                   tree.setSelectionInterval(aRow, newIndex);
 2442               }
 2443               else {
 2444                   tree.setSelectionInterval(newIndex, aRow);
 2445               }
 2446               setAnchorSelectionPath(aPath);
 2447               setLeadSelectionPath(newLead);
 2448           }
 2449       }
 2450   
 2451       /**
 2452        * Invokes <code>repaint</code> on the JTree for the passed in TreePath,
 2453        * <code>path</code>.
 2454        */
 2455       private void repaintPath(TreePath path) {
 2456           if (path != null) {
 2457               Rectangle bounds = getPathBounds(tree, path);
 2458               if (bounds != null) {
 2459                   tree.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
 2460               }
 2461           }
 2462       }
 2463   
 2464       /**
 2465        * Updates the TreeState in response to nodes expanding/collapsing.
 2466        */
 2467       public class TreeExpansionHandler implements TreeExpansionListener {
 2468           // NOTE: This class exists only for backward compatability. All
 2469           // its functionality has been moved into Handler. If you need to add
 2470           // new functionality add it to the Handler, but make sure this
 2471           // class calls into the Handler.
 2472   
 2473           /**
 2474            * Called whenever an item in the tree has been expanded.
 2475            */
 2476           public void treeExpanded(TreeExpansionEvent event) {
 2477               getHandler().treeExpanded(event);
 2478           }
 2479   
 2480           /**
 2481            * Called whenever an item in the tree has been collapsed.
 2482            */
 2483           public void treeCollapsed(TreeExpansionEvent event) {
 2484               getHandler().treeCollapsed(event);
 2485           }
 2486       } // BasicTreeUI.TreeExpansionHandler
 2487   
 2488   
 2489       /**
 2490        * Updates the preferred size when scrolling (if necessary).
 2491        */
 2492       public class ComponentHandler extends ComponentAdapter implements
 2493