FileDocCategorySizeDatePackage
BasicTreeUI.javaAPI DocJava SE 5 API142158Fri Aug 26 14:58:06 BST 2005javax.swing.plaf.basic

BasicTreeUI

public class BasicTreeUI extends TreeUI
The basic L&F for a hierarchical data structure.

version
1.177 05/06/05
author
Scott Violet
author
Shannon Hickey (improved drag recognition)

Fields Summary
private static final Actions
SHARED_ACTION
private static final Insets
EMPTY_INSETS
protected transient Icon
collapsedIcon
protected transient Icon
expandedIcon
private Color
hashColor
Color used to draw hash marks. If null no hash marks will be drawn.
protected int
leftChildIndent
Distance between left margin and where vertical dashes will be drawn.
protected int
rightChildIndent
Distance to add to leftChildIndent to determine where cell contents will be drawn.
protected int
totalChildIndent
Total distance that will be indented. The sum of leftChildIndent and rightChildIndent.
protected Dimension
preferredMinSize
Minimum preferred size.
protected int
lastSelectedRow
Index of the row that was last selected.
protected JTree
tree
Component that we're going to be drawing into.
protected transient TreeCellRenderer
currentCellRenderer
Renderer that is being used to do the actual cell drawing.
protected boolean
createdRenderer
Set to true if the renderer that is currently in the tree was created by this instance.
protected transient TreeCellEditor
cellEditor
Editor for the tree.
protected boolean
createdCellEditor
Set to true if editor that is currently in the tree was created by this instance.
protected boolean
stopEditingInCompleteEditing
Set to false when editing and shouldSelectCell() returns true meaning the node should be selected before editing, used in completeEditing.
protected CellRendererPane
rendererPane
Used to paint the TreeCellRenderer.
protected Dimension
preferredSize
Size needed to completely display all the nodes.
protected boolean
validCachedPreferredSize
Is the preferredSize valid?
protected AbstractLayoutCache
treeState
Object responsible for handling sizing and expanded issues.
protected Hashtable
drawingCache
Used for minimizing the drawing of vertical lines.
protected boolean
largeModel
True if doing optimizations for a largeModel. Subclasses that don't support this may wish to override createLayoutCache to not return a FixedHeightLayoutCache instance.
protected AbstractLayoutCache$NodeDimensions
nodeDimensions
Reponsible for telling the TreeState the size needed for a node.
protected TreeModel
treeModel
Used to determine what to display.
protected TreeSelectionModel
treeSelectionModel
Model maintaing the selection.
protected int
depthOffset
How much the depth should be offset to properly calculate x locations. This is based on whether or not the root is visible, and if the root handles are visible.
private int
lastWidth
Last width the tree was at when painted. This is used when !leftToRigth to notice the bounds have changed so that we can instruct the TreeState to relayout.
protected Component
editingComponent
When editing, this will be the Component that is doing the actual editing.
protected TreePath
editingPath
Path that is being edited.
protected int
editingRow
Row that is being edited. Should only be referenced if editingComponent is not null.
protected boolean
editorHasDifferentSize
Set to true if the editor has a different size than the renderer.
private int
leadRow
Row correspondin to lead path.
private boolean
ignoreLAChange
If true, the property change event for LEAD_SELECTION_PATH_PROPERTY, or ANCHOR_SELECTION_PATH_PROPERTY will not generate a repaint.
private boolean
leftToRight
Indicates the orientation.
private PropertyChangeListener
propertyChangeListener
private PropertyChangeListener
selectionModelPropertyChangeListener
private MouseListener
mouseListener
private FocusListener
focusListener
private KeyListener
keyListener
private ComponentListener
componentListener
Used for large models, listens for moved/resized events and updates the validCachedPreferredSize bit accordingly.
private CellEditorListener
cellEditorListener
Listens for CellEditor events.
private TreeSelectionListener
treeSelectionListener
Updates the display when the selection changes.
private TreeModelListener
treeModelListener
Is responsible for updating the display based on model events.
private TreeExpansionListener
treeExpansionListener
Updates the treestate as the nodes expand.
private boolean
paintLines
UI property indicating whether to paint lines
private boolean
lineTypeDashed
UI property for painting dashed lines
private long
timeFactor
The time factor to treate the series of typed alphanumeric key as prefix for first letter navigation.
private Handler
handler
private MouseEvent
releaseEvent
A temporary variable for communication between startEditingOnRelease and startEditing.
private static final TreeDragGestureRecognizer
defaultDragRecognizer
private static DropTargetListener
defaultDropTargetListener
private static final TransferHandler
defaultTransferHandler
Constructors Summary
public BasicTreeUI()

	super();
    
Methods Summary
public voidcancelEditing(javax.swing.JTree tree)
Cancels the current editing session.

	if(editingComponent != null) {
	    completeEditing(false, true, false);
	}
    
protected voidcheckForClickInExpandControl(javax.swing.tree.TreePath path, int mouseX, int mouseY)
If the mouseX and mouseY are in the expand/collapse region of the row, this will toggle the row.

      if (isLocationInExpandControl(path, mouseX, mouseY)) {
	  handleExpandControlClick(path, mouseX, mouseY);
	}
    
protected voidcompleteEditing()
Messages to stop the editing session. If the UI the receiver is providing the look and feel for returns true from getInvokesStopCellEditing, stopCellEditing will invoked on the current editor. Then completeEditing will be messaged with false, true, false to cancel any lingering editing.

	/* If should invoke stopCellEditing, try that */
	if(tree.getInvokesStopCellEditing() &&
	   stopEditingInCompleteEditing && editingComponent != null) {
	    cellEditor.stopCellEditing();
	}
	/* Invoke cancelCellEditing, this will do nothing if stopCellEditing
	   was successful. */
	completeEditing(false, true, false);
    
protected voidcompleteEditing(boolean messageStop, boolean messageCancel, boolean messageTree)
Stops the editing session. If messageStop is true the editor is messaged with stopEditing, if messageCancel is true the editor is messaged with cancelEditing. If messageTree is true the treeModel is messaged with valueForPathChanged.

	if(stopEditingInCompleteEditing && editingComponent != null) {
	    Component             oldComponent = editingComponent;
	    TreePath              oldPath = editingPath;
	    TreeCellEditor        oldEditor = cellEditor;
	    Object                newValue = oldEditor.getCellEditorValue();
	    Rectangle             editingBounds = getPathBounds(tree, 
								editingPath);
	    boolean               requestFocus = (tree != null &&
		                   (tree.hasFocus() || SwingUtilities.
				    findFocusOwner(editingComponent) != null));

	    editingComponent = null;
	    editingPath = null;
	    if(messageStop)
		oldEditor.stopCellEditing();
	    else if(messageCancel)
		oldEditor.cancelCellEditing();
	    tree.remove(oldComponent);
	    if(editorHasDifferentSize) {
		treeState.invalidatePathBounds(oldPath);
		updateSize();
	    }
	    else {
		editingBounds.x = 0;
		editingBounds.width = tree.getSize().width;
		tree.repaint(editingBounds);
	    }
	    if(requestFocus)
		tree.requestFocus();
	    if(messageTree)
		treeModel.valueForPathChanged(oldPath, newValue);
	}
    
protected voidcompleteUIInstall()
Invoked from installUI after all the defaults/listeners have been installed.

	// Custom install code

	this.setShowsRootHandles(tree.getShowsRootHandles());

	updateRenderer();

	updateDepthOffset();

	setSelectionModel(tree.getSelectionModel());

	// Create, if necessary, the TreeState instance.
	treeState = createLayoutCache();
	configureLayoutCache();

	updateSize();
    
protected voidcompleteUIUninstall()

	if(createdRenderer) {
	    tree.setCellRenderer(null);
	}
	if(createdCellEditor) {
	    tree.setCellEditor(null);
	}
	cellEditor = null;
	currentCellRenderer = null;
	rendererPane = null;
        componentListener = null;
	propertyChangeListener = null;
	mouseListener = null;
	focusListener = null;
	keyListener = null;
	setSelectionModel(null);
	treeState = null;
	drawingCache = null;
	selectionModelPropertyChangeListener = null;
	tree = null;
	treeModel = null;
	treeSelectionModel = null;
	treeSelectionListener = null;
	treeExpansionListener = null;
    
protected voidconfigureLayoutCache()
Resets the TreeState instance based on the tree we're providing the look and feel for.

	if(treeState != null && tree != null) {
	    if(nodeDimensions == null)
		nodeDimensions = createNodeDimensions();
	    treeState.setNodeDimensions(nodeDimensions);
	    treeState.setRootVisible(tree.isRootVisible());
	    treeState.setRowHeight(tree.getRowHeight());
	    treeState.setSelectionModel(getSelectionModel());
	    // Only do this if necessary, may loss state if call with
	    // same model as it currently has.
	    if(treeState.getModel() != tree.getModel())
		treeState.setModel(tree.getModel());
	    updateLayoutCacheExpandedNodes();
	    // Create a listener to update preferred size when bounds
	    // changes, if necessary.
	    if(isLargeModel()) {
		if(componentListener == null) {
		    componentListener = createComponentListener();
		    if(componentListener != null)
			tree.addComponentListener(componentListener);
		}
	    }
	    else if(componentListener != null) {
		tree.removeComponentListener(componentListener);
		componentListener = null;
	    }
	}
	else if(componentListener != null) {
	    tree.removeComponentListener(componentListener);
	    componentListener = null;
	}
    
protected javax.swing.event.CellEditorListenercreateCellEditorListener()
Creates a listener to handle events from the current editor.

	return getHandler();
    
protected javax.swing.CellRendererPanecreateCellRendererPane()
Returns the renderer pane that renderer components are placed in.

        return new CellRendererPane();
    
protected java.awt.event.ComponentListenercreateComponentListener()
Creates and returns a new ComponentHandler. This is used for the large model to mark the validCachedPreferredSize as invalid when the component moves.

	return new ComponentHandler();
    
protected javax.swing.tree.TreeCellEditorcreateDefaultCellEditor()
Creates a default cell editor.

	if(currentCellRenderer != null &&
	   (currentCellRenderer instanceof DefaultTreeCellRenderer)) {
	    DefaultTreeCellEditor editor = new DefaultTreeCellEditor
		        (tree, (DefaultTreeCellRenderer)currentCellRenderer);

	    return editor;
	}
	return new DefaultTreeCellEditor(tree, null);
    
protected javax.swing.tree.TreeCellRenderercreateDefaultCellRenderer()
Returns the default cell renderer that is used to do the stamping of each node.

	return new DefaultTreeCellRenderer();
    
protected java.awt.event.FocusListenercreateFocusListener()
Creates a listener that is responsible for updating the display when focus is lost/gained.

        return getHandler();
    
protected java.awt.event.KeyListenercreateKeyListener()
Creates the listener reponsible for getting key events from the tree.

        return getHandler();
    
protected javax.swing.tree.AbstractLayoutCachecreateLayoutCache()
Creates the object responsible for managing what is expanded, as well as the size of nodes.

	if(isLargeModel() && getRowHeight() > 0) {
	    return new FixedHeightLayoutCache();
	}
	return new VariableHeightLayoutCache();
    
protected java.awt.event.MouseListenercreateMouseListener()
Creates the listener responsible for updating the selection based on mouse events.

        return getHandler();
    
protected javax.swing.tree.AbstractLayoutCache$NodeDimensionscreateNodeDimensions()
Creates an instance of NodeDimensions that is able to determine the size of a given node in the tree.

	return new NodeDimensionsHandler();
    
protected java.beans.PropertyChangeListenercreatePropertyChangeListener()
Creates a listener that is responsible that updates the UI based on how the tree changes.

        return getHandler();
    
protected java.beans.PropertyChangeListenercreateSelectionModelPropertyChangeListener()
Creates the listener responsible for getting property change events from the selection model.

	return getHandler();
    
protected javax.swing.event.TreeExpansionListenercreateTreeExpansionListener()
Creates and returns the object responsible for updating the treestate when nodes expanded state changes.

	return getHandler();
    
protected javax.swing.event.TreeModelListenercreateTreeModelListener()
Returns a listener that can update the tree when the model changes.

	return getHandler();
    
protected javax.swing.event.TreeSelectionListenercreateTreeSelectionListener()
Creates the listener that updates the display based on selection change methods.

	return getHandler();
    
public static javax.swing.plaf.ComponentUIcreateUI(javax.swing.JComponent x)


         
	return new BasicTreeUI();
    
protected voiddrawCentered(java.awt.Component c, java.awt.Graphics graphics, javax.swing.Icon icon, int x, int y)

	icon.paintIcon(c, graphics, x - icon.getIconWidth()/2, y -
		       icon.getIconHeight()/2);
    
protected voiddrawDashedHorizontalLine(java.awt.Graphics g, int y, int x1, int x2)

	// Drawing only even coordinates helps join line segments so they
	// appear as one line.  This can be defeated by translating the
	// Graphics by an odd amount.
	x1 += (x1 % 2);

	for (int x = x1; x <= x2; x+=2) {
	    g.drawLine(x, y, x, y);
	}
    
protected voiddrawDashedVerticalLine(java.awt.Graphics g, int x, int y1, int y2)

	// Drawing only even coordinates helps join line segments so they
	// appear as one line.  This can be defeated by translating the
	// Graphics by an odd amount.
	y1 += (y1 % 2);

	for (int y = y1; y <= y2; y+=2) {
	    g.drawLine(x, y, x, y);
	}
    
protected voidensureRowsAreVisible(int beginRow, int endRow)
Ensures that the rows identified by beginRow through endRow are visible.

	if(tree != null && beginRow >= 0 && endRow < getRowCount(tree)) {
            boolean scrollVert = DefaultLookup.getBoolean(tree, this,
                              "Tree.scrollsHorizontallyAndVertically", false);
	    if(beginRow == endRow) {
		Rectangle     scrollBounds = getPathBounds(tree, getPathForRow
							   (tree, beginRow));

		if(scrollBounds != null) {
                    if (!scrollVert) {
                        scrollBounds.x = tree.getVisibleRect().x;
                        scrollBounds.width = 1;
                    }
		    tree.scrollRectToVisible(scrollBounds);
		}
	    }
	    else {
		Rectangle   beginRect = getPathBounds(tree, getPathForRow
						      (tree, beginRow));
		Rectangle   visRect = tree.getVisibleRect();
		Rectangle   testRect = beginRect;
		int         beginY = beginRect.y;
		int         maxY = beginY + visRect.height;

		for(int counter = beginRow + 1; counter <= endRow; counter++) {
		    testRect = getPathBounds(tree,
					     getPathForRow(tree, counter));
		    if((testRect.y + testRect.height) > maxY)
			counter = endRow;
		}
		tree.scrollRectToVisible(new Rectangle(visRect.x, beginY, 1,
						  testRect.y + testRect.height-
						  beginY));
	    }
	}
    
private voidextendSelection(javax.swing.tree.TreePath newLead)
Extends the selection from the anchor to make newLead the lead of the selection. This does not scroll.

	TreePath           aPath = getAnchorSelectionPath();
	int                aRow = (aPath == null) ? -1 :
			          getRowForPath(tree, aPath);
	int                newIndex = getRowForPath(tree, newLead);

	if(aRow == -1) {
	    tree.setSelectionRow(newIndex);
	}
	else {
	    if(aRow < newIndex) {
		tree.setSelectionInterval(aRow, newIndex);
	    }
	    else {
		tree.setSelectionInterval(newIndex, aRow);
	    }
	    setAnchorSelectionPath(aPath);
	    setLeadSelectionPath(newLead);
	}
    
private javax.swing.tree.TreePathgetAnchorSelectionPath()

	return tree.getAnchorSelectionPath();
    
protected javax.swing.tree.TreeCellEditorgetCellEditor()

	return (tree != null) ? tree.getCellEditor() : null;
    
protected javax.swing.tree.TreeCellRenderergetCellRenderer()
Return currentCellRenderer, which will either be the trees renderer, or defaultCellRenderer, which ever wasn't null.

	return currentCellRenderer;
    
public javax.swing.tree.TreePathgetClosestPathForLocation(javax.swing.JTree tree, int x, int y)
Returns the path to the node that is closest to x,y. If there is nothing currently visible this will return null, otherwise it'll always return a valid path. If you need to test if the returned object is exactly at x, y you should get the bounds for the returned path and test x, y against that.

	if(tree != null && treeState != null) {
	    Insets          i = tree.getInsets();

	    if(i == null)
		i = EMPTY_INSETS;

	    return treeState.getPathClosestTo(x - i.left, y - i.top);
	}
	return null;
    
public javax.swing.IcongetCollapsedIcon()

	return collapsedIcon;
    
public javax.swing.tree.TreePathgetEditingPath(javax.swing.JTree tree)
Returns the path to the element that is being edited.

	return editingPath;
    
public javax.swing.IcongetExpandedIcon()

	return expandedIcon;
    
private javax.swing.plaf.basic.BasicTreeUI$HandlergetHandler()

        if (handler == null) {
            handler = DRAG_FIX ? new DragFixHandler() : new Handler();
        }
        return handler;
    
protected java.awt.ColorgetHashColor()

        return hashColor;
    
protected intgetHorizontalLegBuffer()
The horizontal element of legs between nodes starts at the right of the left-hand side of the child node by default. This method makes the leg end before that.

	return 0;
    
javax.swing.InputMapgetInputMap(int condition)

	if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
	    return (InputMap)DefaultLookup.get(tree, this,
                                               "Tree.ancestorInputMap");
	}
	else if (condition == JComponent.WHEN_FOCUSED) {
	    InputMap keyMap = (InputMap)DefaultLookup.get(tree, this,
                                                      "Tree.focusInputMap");
	    InputMap rtlKeyMap;

	    if (tree.getComponentOrientation().isLeftToRight() ||
		  ((rtlKeyMap = (InputMap)DefaultLookup.get(tree, this,
                  "Tree.focusInputMap.RightToLeft")) == null)) {
		return keyMap;
	    } else {
		rtlKeyMap.setParent(keyMap);
		return rtlKeyMap;
	    }
	}
	return null;
    
protected javax.swing.tree.TreePathgetLastChildPath(javax.swing.tree.TreePath parent)
Returns a path to the last child of parent.

	if(treeModel != null) {
	    int         childCount = treeModel.getChildCount
		(parent.getLastPathComponent());
	    
	    if(childCount > 0)
		return parent.pathByAddingChild(treeModel.getChild
			   (parent.getLastPathComponent(), childCount - 1));
	}
	return null;
    
private javax.swing.tree.TreePathgetLeadSelectionPath()

	return tree.getLeadSelectionPath();
    
private intgetLeadSelectionRow()

	return leadRow;
    
public intgetLeftChildIndent()

	return leftChildIndent;
    
public java.awt.DimensiongetMaximumSize(javax.swing.JComponent c)
Returns the maximum size for this component, which will be the preferred size if the instance is currently in a JTree, or 0, 0.

	if(tree != null)
	    return getPreferredSize(tree);
	if(this.getPreferredMinSize() != null)
	    return this.getPreferredMinSize();
	return new Dimension(0, 0);
    
private intgetMinX(java.awt.Rectangle bounds)
Returns the minimum x location for the nodes in bounds.

	TreePath      firstPath;
	int           endY;

	if(bounds == null) {
	    firstPath = getPathForRow(tree, 0);
	    endY = Integer.MAX_VALUE;
	}
	else {
	    firstPath = treeState.getPathClosestTo(bounds.x, bounds.y);
	    endY = bounds.height + bounds.y;
	}

	Enumeration   paths = treeState.getVisiblePathsFrom(firstPath);
	int           minX = 0;

	if(paths != null && paths.hasMoreElements()) {
	    Rectangle   pBounds = treeState.getBounds
		                  ((TreePath)paths.nextElement(), null);
	    int         width;

	    if(pBounds != null) {
		minX = pBounds.x + pBounds.width;
		if (pBounds.y >= endY) {
		    return minX;
		}
	    }
	    while (pBounds != null && paths.hasMoreElements()) {
		pBounds = treeState.getBounds((TreePath)paths.nextElement(),
					      pBounds);
		if (pBounds != null && pBounds.y < endY) {
		    minX = Math.min(minX, pBounds.x);
		}
		else {
		    pBounds = null;
		}
	    }
	    return minX;
	}
	return minX;
    
public java.awt.DimensiongetMinimumSize(javax.swing.JComponent c)
Returns the minimum size for this component. Which will be the min preferred size or 0, 0.

	if(this.getPreferredMinSize() != null)
	    return this.getPreferredMinSize();
	return new Dimension(0, 0);
    
protected javax.swing.tree.TreeModelgetModel()

	return treeModel;
    
public java.awt.RectanglegetPathBounds(javax.swing.JTree tree, javax.swing.tree.TreePath path)
Returns the Rectangle enclosing the label portion that the last item in path will be drawn into. Will return null if any component in path is currently valid.

	if(tree != null && treeState != null) {
	    Insets           i = tree.getInsets();
	    Rectangle        bounds = treeState.getBounds(path, null);

	    if(bounds != null && i != null) {
		bounds.x += i.left;
		bounds.y += i.top;
	    }
	    return bounds;
	}
	return null;
    
public javax.swing.tree.TreePathgetPathForRow(javax.swing.JTree tree, int row)
Returns the path for passed in row. If row is not visible null is returned.

	return (treeState != null) ? treeState.getPathForRow(row) : null;
    
public java.awt.DimensiongetPreferredMinSize()
Returns the minimum preferred size.

	if(preferredMinSize == null)
	    return null;
	return new Dimension(preferredMinSize);
    
public java.awt.DimensiongetPreferredSize(javax.swing.JComponent c)
Returns the preferred size to properly display the tree, this is a cover method for getPreferredSize(c, false).

	return getPreferredSize(c, true);
    
public java.awt.DimensiongetPreferredSize(javax.swing.JComponent c, boolean checkConsistancy)
Returns the preferred size to represent the tree in c. If checkConsistancy is true checkConsistancy is messaged first.

	Dimension       pSize = this.getPreferredMinSize();

	if(!validCachedPreferredSize)
	    updateCachedPreferredSize();
	if(tree != null) {
	    if(pSize != null)
		return new Dimension(Math.max(pSize.width,
					      preferredSize.width),
			      Math.max(pSize.height, preferredSize.height));
	    return new Dimension(preferredSize.width, preferredSize.height);
	}
	else if(pSize != null)
	    return pSize;
	else
	    return new Dimension(0, 0);
    
public intgetRightChildIndent()

	return rightChildIndent;
    
public intgetRowCount(javax.swing.JTree tree)
Returns the number of rows that are being displayed.

	return (treeState != null) ? treeState.getRowCount() : 0;
    
public intgetRowForPath(javax.swing.JTree tree, javax.swing.tree.TreePath path)
Returns the row that the last item identified in path is visible at. Will return -1 if any of the elements in path are not currently visible.

	return (treeState != null) ? treeState.getRowForPath(path) : -1;
    
protected intgetRowHeight()

	return (tree == null) ? -1 : tree.getRowHeight();
    
protected intgetRowX(int row, int depth)
Returns the location, along the x-axis, to render a particular row at. The return value does not include any Insets specified on the JTree. This does not check for the validity of the row or depth, it is assumed to be correct and will not throw an Exception if the row or depth doesn't match that of the tree.

param
row Row to return x location for
param
depth Depth of the row
return
amount to indent the given row.
since
1.5

        return totalChildIndent * (depth + depthOffset);
    
protected javax.swing.tree.TreeSelectionModelgetSelectionModel()

	return treeSelectionModel;
    
protected booleangetShowsRootHandles()

	return (tree != null) ? tree.getShowsRootHandles() : false;
    
protected intgetVerticalLegBuffer()
The vertical element of legs between nodes starts at the bottom of the parent node by default. This method makes the leg start below that.

	return 0;
    
protected voidhandleExpandControlClick(javax.swing.tree.TreePath path, int mouseX, int mouseY)
Messaged when the user clicks the particular row, this invokes toggleExpandState.

	toggleExpandState(path);
    
protected voidinstallComponents()
Intalls the subcomponents of the tree, which is the renderer pane.

	if ((rendererPane = createCellRendererPane()) != null) {
	    tree.add( rendererPane );
	}
    
protected voidinstallDefaults()

	if(tree.getBackground() == null ||
	   tree.getBackground() instanceof UIResource) {
	    tree.setBackground(UIManager.getColor("Tree.background"));
	} 
	if(getHashColor() == null || getHashColor() instanceof UIResource) {
	    setHashColor(UIManager.getColor("Tree.hash"));
	}
	if (tree.getFont() == null || tree.getFont() instanceof UIResource)
	    tree.setFont( UIManager.getFont("Tree.font") );
        // JTree's original row height is 16.  To correctly display the
        // contents on Linux we should have set it to 18, Windows 19 and
        // Solaris 20.  As these values vary so much it's too hard to
        // be backward compatable and try to update the row height, we're
        // therefor NOT going to adjust the row height based on font.  If the
        // developer changes the font, it's there responsibility to update
        // the row height.

	setExpandedIcon( (Icon)UIManager.get( "Tree.expandedIcon" ) );
	setCollapsedIcon( (Icon)UIManager.get( "Tree.collapsedIcon" ) );

	setLeftChildIndent(((Integer)UIManager.get("Tree.leftChildIndent")).
			   intValue());
	setRightChildIndent(((Integer)UIManager.get("Tree.rightChildIndent")).
			   intValue());

	LookAndFeel.installProperty(tree, "rowHeight",
				    UIManager.get("Tree.rowHeight"));

        largeModel = (tree.isLargeModel() && tree.getRowHeight() > 0);

	Object scrollsOnExpand = UIManager.get("Tree.scrollsOnExpand");
	if (scrollsOnExpand != null) {
	    LookAndFeel.installProperty(tree, "scrollsOnExpand", scrollsOnExpand);
	}

	paintLines = UIManager.getBoolean("Tree.paintLines");
	lineTypeDashed = UIManager.getBoolean("Tree.lineTypeDashed");
	
 	Long l = (Long)UIManager.get("Tree.timeFactor");
 	timeFactor = (l!=null) ? l.longValue() : 1000L;
        
        Object showsRootHandles = UIManager.get("Tree.showsRootHandles");
        if (showsRootHandles != null) {
            LookAndFeel.installProperty(tree, 
                    JTree.SHOWS_ROOT_HANDLES_PROPERTY, showsRootHandles);
        }
    
protected voidinstallKeyboardActions()

	InputMap km = getInputMap(JComponent.
				  WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

	SwingUtilities.replaceUIInputMap(tree, JComponent.
					 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
					 km);
	km = getInputMap(JComponent.WHEN_FOCUSED);
	SwingUtilities.replaceUIInputMap(tree, JComponent.WHEN_FOCUSED, km);

        LazyActionMap.installLazyActionMap(tree, BasicTreeUI.class,
                                           "Tree.actionMap");
    
protected voidinstallListeners()

        if ( (propertyChangeListener = createPropertyChangeListener())
	     != null ) {
	    tree.addPropertyChangeListener(propertyChangeListener);
	}
        if (!DRAG_FIX) {
            tree.addMouseListener(defaultDragRecognizer);
            tree.addMouseMotionListener(defaultDragRecognizer);
        }
        if ( (mouseListener = createMouseListener()) != null ) {
	    tree.addMouseListener(mouseListener);
	    if (mouseListener instanceof MouseMotionListener) {
		tree.addMouseMotionListener((MouseMotionListener)mouseListener);
	    }
	}
        if ((focusListener = createFocusListener()) != null ) {
	    tree.addFocusListener(focusListener);
	}
        if ((keyListener = createKeyListener()) != null) {
	    tree.addKeyListener(keyListener);
	}
	if((treeExpansionListener = createTreeExpansionListener()) != null) {
	    tree.addTreeExpansionListener(treeExpansionListener);
	}
	if((treeModelListener = createTreeModelListener()) != null &&
	   treeModel != null) {
	    treeModel.addTreeModelListener(treeModelListener);
	}
	if((selectionModelPropertyChangeListener =
	    createSelectionModelPropertyChangeListener()) != null &&
	   treeSelectionModel != null) {
	    treeSelectionModel.addPropertyChangeListener
		(selectionModelPropertyChangeListener);
	}
	if((treeSelectionListener = createTreeSelectionListener()) != null &&
	   treeSelectionModel != null) {
	    treeSelectionModel.addTreeSelectionListener(treeSelectionListener);
	}

	TransferHandler th = tree.getTransferHandler();
	if (th == null || th instanceof UIResource) {
	    tree.setTransferHandler(defaultTransferHandler);
	}
	DropTarget dropTarget = tree.getDropTarget();
	if (dropTarget instanceof UIResource) {
            if (defaultDropTargetListener == null) {
                defaultDropTargetListener = new TreeDropTargetListener();
            }
	    try {
		dropTarget.addDropTargetListener(defaultDropTargetListener);
	    } catch (TooManyListenersException tmle) {
		// should not happen... swing drop target is multicast
	    }
	}
        LookAndFeel.installProperty(tree, "opaque", Boolean.TRUE);
    
public voidinstallUI(javax.swing.JComponent c)

        if ( c == null ) {
	    throw new NullPointerException( "null component passed to BasicTreeUI.installUI()" );
        }

	tree = (JTree)c;

	prepareForUIInstall();

	// Boilerplate install block
	installDefaults();
	installKeyboardActions();
	installComponents();
	installListeners();

	completeUIInstall();
    
protected booleanisEditable()

	return (tree != null) ? tree.isEditable() : false;
    
public booleanisEditing(javax.swing.JTree tree)
Returns true if the tree is being edited. The item that is being edited can be returned by getEditingPath().

	return (editingComponent != null);
    
protected booleanisLargeModel()

	return largeModel;
    
protected booleanisLeaf(int row)

return
true if the node at row is a leaf.

	TreePath          path = getPathForRow(tree, row);

	if(path != null)
	    return treeModel.isLeaf(path.getLastPathComponent());
	// Have to return something here...
	return true;
    
protected booleanisLocationInExpandControl(javax.swing.tree.TreePath path, int mouseX, int mouseY)
Returns true if mouseX and mouseY fall in the area of row that is used to expand/collapse the node and the node at row does not represent a leaf.

	if(path != null && !treeModel.isLeaf(path.getLastPathComponent())){
	    int                     boxWidth;
	    Insets                  i = tree.getInsets();

	    if(getExpandedIcon() != null)
		boxWidth = getExpandedIcon().getIconWidth();
	    else
		boxWidth = 8;

	    int boxLeftX = getRowX(tree.getRowForPath(path),
                   path.getPathCount() - 1) - getRightChildIndent() -
                   boxWidth / 2;

	    if (leftToRight) {
                boxLeftX += i.left;
	    }
	    else {
	        boxLeftX = i.left + lastWidth - 1 - 
		            ((path.getPathCount() - 2 + depthOffset) *
			     totalChildIndent) - getLeftChildIndent() -
		            boxWidth / 2;
	    }
	    int boxRightX = boxLeftX + boxWidth;

	    return mouseX >= boxLeftX && mouseX <= boxRightX;
	}
	return false;
    
protected booleanisMultiSelectEvent(java.awt.event.MouseEvent event)
Returning true signifies a mouse event on the node should select from the anchor point.

	return (SwingUtilities.isLeftMouseButton(event) &&
		event.isShiftDown());
    
protected booleanisRootVisible()

	return (tree != null) ? tree.isRootVisible() : false;
    
protected booleanisToggleEvent(java.awt.event.MouseEvent event)
Returning true indicates the row under the mouse should be toggled based on the event. This is invoked after checkForClickInExpandControl, implying the location is not in the expand (toggle) control

	if(!SwingUtilities.isLeftMouseButton(event)) {
	    return false;
	}
	int           clickCount = tree.getToggleClickCount();

	if(clickCount <= 0) {
	    return false;
	}
	return ((event.getClickCount() % clickCount) == 0);
    
protected booleanisToggleSelectionEvent(java.awt.event.MouseEvent event)
Returning true signifies a mouse event on the node should toggle the selection of only the row under mouse.

	return (SwingUtilities.isLeftMouseButton(event) &&
		event.isControlDown());
    
static voidloadActionMap(javax.swing.plaf.basic.LazyActionMap map)

	map.put(new Actions(Actions.SELECT_PREVIOUS));
        map.put(new Actions(Actions.SELECT_PREVIOUS_CHANGE_LEAD));
	map.put(new Actions(Actions.SELECT_PREVIOUS_EXTEND_SELECTION));

	map.put(new Actions(Actions.SELECT_NEXT));
	map.put(new Actions(Actions.SELECT_NEXT_CHANGE_LEAD));
	map.put(new Actions(Actions.SELECT_NEXT_EXTEND_SELECTION));

	map.put(new Actions(Actions.SELECT_CHILD));
	map.put(new Actions(Actions.SELECT_CHILD_CHANGE_LEAD));

	map.put(new Actions(Actions.SELECT_PARENT));
	map.put(new Actions(Actions.SELECT_PARENT_CHANGE_LEAD));

	map.put(new Actions(Actions.SCROLL_UP_CHANGE_SELECTION));
	map.put(new Actions(Actions.SCROLL_UP_CHANGE_LEAD));
	map.put(new Actions(Actions.SCROLL_UP_EXTEND_SELECTION));

	map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_SELECTION));
	map.put(new Actions(Actions.SCROLL_DOWN_EXTEND_SELECTION));
	map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_LEAD));

	map.put(new Actions(Actions.SELECT_FIRST));
	map.put(new Actions(Actions.SELECT_FIRST_CHANGE_LEAD));
	map.put(new Actions(Actions.SELECT_FIRST_EXTEND_SELECTION));

	map.put(new Actions(Actions.SELECT_LAST));
	map.put(new Actions(Actions.SELECT_LAST_CHANGE_LEAD));
	map.put(new Actions(Actions.SELECT_LAST_EXTEND_SELECTION));

	map.put(new Actions(Actions.TOGGLE));

	map.put(new Actions(Actions.CANCEL_EDITING));

	map.put(new Actions(Actions.START_EDITING));

	map.put(new Actions(Actions.SELECT_ALL));

	map.put(new Actions(Actions.CLEAR_SELECTION));

	map.put(new Actions(Actions.SCROLL_LEFT));
	map.put(new Actions(Actions.SCROLL_RIGHT));

	map.put(new Actions(Actions.SCROLL_LEFT_EXTEND_SELECTION));
	map.put(new Actions(Actions.SCROLL_RIGHT_EXTEND_SELECTION));

	map.put(new Actions(Actions.SCROLL_RIGHT_CHANGE_LEAD));
	map.put(new Actions(Actions.SCROLL_LEFT_CHANGE_LEAD));

        map.put(new Actions(Actions.EXPAND));
        map.put(new Actions(Actions.COLLAPSE));
        map.put(new Actions(Actions.MOVE_SELECTION_TO_PARENT));

        map.put(new Actions(Actions.ADD_TO_SELECTION));
        map.put(new Actions(Actions.TOGGLE_AND_ANCHOR));
        map.put(new Actions(Actions.EXTEND_TO));
        map.put(new Actions(Actions.MOVE_SELECTION_TO));

        map.put(TransferHandler.getCutAction());
        map.put(TransferHandler.getCopyAction());
        map.put(TransferHandler.getPasteAction());
    
public voidpaint(java.awt.Graphics g, javax.swing.JComponent c)

	if (tree != c) {
	    throw new InternalError("incorrect component");
	}

	// Should never happen if installed for a UI
	if(treeState == null) {
	    return;
	}

	// Update the lastWidth if necessary.
	// This should really come from a ComponentListener installed on
	// the JTree, but for the time being it is here.
	int              width = tree.getWidth();

	if (width != lastWidth) {
	    lastWidth = width;
	    if (!leftToRight) {
		// For RTL when the size changes, we have to refresh the
		// cache as the X position is based off the width.
		redoTheLayout();
		updateSize();
	    }
	}

	Rectangle        paintBounds = g.getClipBounds();
	Insets           insets = tree.getInsets();

	if(insets == null)
	    insets = EMPTY_INSETS;

	TreePath         initialPath = getClosestPathForLocation
	                               (tree, 0, paintBounds.y);
	Enumeration      paintingEnumerator = treeState.getVisiblePathsFrom
	                                      (initialPath);
	int              row = treeState.getRowForPath(initialPath);
	int              endY = paintBounds.y + paintBounds.height;

	drawingCache.clear();

	if(initialPath != null && paintingEnumerator != null) {
	    TreePath   parentPath = initialPath;

	    // Draw the lines, knobs, and rows

	    // Find each parent and have them draw a line to their last child
	    parentPath = parentPath.getParentPath();
	    while(parentPath != null) {
		paintVerticalPartOfLeg(g, paintBounds, insets, parentPath);
		drawingCache.put(parentPath, Boolean.TRUE);
		parentPath = parentPath.getParentPath();
	    }

	    boolean         done = false;
	    // Information for the node being rendered.
	    boolean         isExpanded;
	    boolean         hasBeenExpanded;
	    boolean         isLeaf;
	    Rectangle       boundsBuffer = new Rectangle();
	    Rectangle       bounds;
	    TreePath        path;
	    boolean         rootVisible = isRootVisible();

	    while(!done && paintingEnumerator.hasMoreElements()) {
		path = (TreePath)paintingEnumerator.nextElement();
		if(path != null) {
		    isLeaf = treeModel.isLeaf(path.getLastPathComponent());
		    if(isLeaf)
			isExpanded = hasBeenExpanded = false;
		    else {
			isExpanded = treeState.getExpandedState(path);
			hasBeenExpanded = tree.hasBeenExpanded(path);
		    }
		    bounds = treeState.getBounds(path, boundsBuffer);
		    if(bounds == null)
			// This will only happen if the model changes out
			// from under us (usually in another thread).
			// Swing isn't multithreaded, but I'll put this
			// check in anyway.
			return;
		    bounds.x += insets.left;
		    bounds.y += insets.top;
		    // See if the vertical line to the parent has been drawn.
		    parentPath = path.getParentPath();
		    if(parentPath != null) {
			if(drawingCache.get(parentPath) == null) {
			    paintVerticalPartOfLeg(g, paintBounds,
						   insets, parentPath);
			    drawingCache.put(parentPath, Boolean.TRUE);
			}
			paintHorizontalPartOfLeg(g, paintBounds, insets,
						 bounds, path, row,
						 isExpanded,
						 hasBeenExpanded, isLeaf);
		    }
		    else if(rootVisible && row == 0) {
			paintHorizontalPartOfLeg(g, paintBounds, insets,
						 bounds, path, row,
						 isExpanded,
						 hasBeenExpanded, isLeaf);
		    }
		    if(shouldPaintExpandControl(path, row, isExpanded,
						hasBeenExpanded, isLeaf)) {
			paintExpandControl(g, paintBounds, insets, bounds,
					   path, row, isExpanded,
					   hasBeenExpanded, isLeaf);
		    }
                    //This is the quick fix for bug 4259260.  Somewhere we
                    //are out by 4 pixels in the RTL layout.  Its probably
                    //due to built in right-side padding in some icons.  Rather
                    //than ferret out problem at the source, this compensates. 
            	    if (!leftToRight) {
                        bounds.x +=4;
                    }
		    paintRow(g, paintBounds, insets, bounds, path,
				 row, isExpanded, hasBeenExpanded, isLeaf);
		    if((bounds.y + bounds.height) >= endY)
			done = true;
		}
		else {
		    done = true;
		}
		row++;
	    }
	}
	// Empty out the renderer pane, allowing renderers to be gc'ed.
	rendererPane.removeAll();
    
protected voidpaintExpandControl(java.awt.Graphics g, java.awt.Rectangle clipBounds, java.awt.Insets insets, java.awt.Rectangle bounds, javax.swing.tree.TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf)
Paints the expand (toggle) part of a row. The receiver should NOT modify clipBounds, or insets.

	Object       value = path.getLastPathComponent();

	// Draw icons if not a leaf and either hasn't been loaded,
	// or the model child count is > 0.
	if (!isLeaf && (!hasBeenExpanded ||
			treeModel.getChildCount(value) > 0)) {
	    int middleXOfKnob;
	    if (leftToRight) {
	        middleXOfKnob = bounds.x - (getRightChildIndent() - 1);
	    }
	    else {
	        middleXOfKnob = bounds.x + bounds.width + getRightChildIndent();
	    }
	    int middleYOfKnob = bounds.y + (bounds.height / 2);

	    if (isExpanded) {
		Icon expandedIcon = getExpandedIcon();
		if(expandedIcon != null)
		  drawCentered(tree, g, expandedIcon, middleXOfKnob,
			       middleYOfKnob );
	    }
	    else {
		Icon collapsedIcon = getCollapsedIcon();
		if(collapsedIcon != null)
		  drawCentered(tree, g, collapsedIcon, middleXOfKnob,
			       middleYOfKnob);
	    }
	}
    
protected voidpaintHorizontalLine(java.awt.Graphics g, javax.swing.JComponent c, int y, int left, int right)
Paints a horizontal line.

	if (lineTypeDashed) {
	    drawDashedHorizontalLine(g, y, left, right);
	} else {
	    g.drawLine(left, y, right, y);
	}
    
protected voidpaintHorizontalPartOfLeg(java.awt.Graphics g, java.awt.Rectangle clipBounds, java.awt.Insets insets, java.awt.Rectangle bounds, javax.swing.tree.TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf)
Paints the horizontal part of the leg. The receiver should NOT modify clipBounds, or insets.

NOTE: parentRow can be -1 if the root is not visible.

	if (!paintLines) {
	    return;
	}

        // Don't paint the legs for the root'ish node if the
        int depth = path.getPathCount() - 1;
	if((depth == 0 || (depth == 1 && !isRootVisible())) &&
	   !getShowsRootHandles()) {
	    return;
        }

	int clipLeft = clipBounds.x;
	int clipRight = clipBounds.x + (clipBounds.width - 1);
	int clipTop = clipBounds.y;
	int clipBottom = clipBounds.y + (clipBounds.height - 1);
	int lineY = bounds.y + bounds.height / 2;
	// Offset leftX from parents indent.
	if (leftToRight) {
	    int leftX = bounds.x - getRightChildIndent();
	    int nodeX = bounds.x - getHorizontalLegBuffer();
	
	    if(lineY >= clipTop && lineY <= clipBottom && nodeX >= clipLeft &&
	                                                 leftX <= clipRight ) {
	        leftX = Math.max(Math.max(insets.left, leftX), clipLeft);
		nodeX = Math.min(Math.max(insets.left, nodeX), clipRight);

                if (leftX != nodeX) {
                    g.setColor(getHashColor());
                    paintHorizontalLine(g, tree, lineY, leftX, nodeX);
                }
	    }
	}
	else {
	    int leftX = bounds.x + bounds.width + getRightChildIndent();
	    int nodeX = bounds.x + bounds.width + 
	                                  getHorizontalLegBuffer() - 1;

	    if(lineY >= clipTop && lineY <= clipBottom &&
	       leftX >= clipLeft && nodeX <= clipRight) {
	        leftX = Math.min(leftX, clipRight);
		nodeX = Math.max(nodeX, clipLeft);

		g.setColor(getHashColor());
		paintHorizontalLine(g, tree, lineY, nodeX, leftX);
	    }
	}
    
protected voidpaintRow(java.awt.Graphics g, java.awt.Rectangle clipBounds, java.awt.Insets insets, java.awt.Rectangle bounds, javax.swing.tree.TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf)
Paints the renderer part of a row. The receiver should NOT modify clipBounds, or insets.

	// Don't paint the renderer if editing this row.
	if(editingComponent != null && editingRow == row)
	    return;

	int leadIndex;

	if(tree.hasFocus()) {
	    leadIndex = getLeadSelectionRow();
	}
	else
	    leadIndex = -1;

	Component component;

	component = currentCellRenderer.getTreeCellRendererComponent
	              (tree, path.getLastPathComponent(),
		       tree.isRowSelected(row), isExpanded, isLeaf, row,
		       (leadIndex == row));
	
	rendererPane.paintComponent(g, component, tree, bounds.x, bounds.y,
				    bounds.width, bounds.height, true);	
    
protected voidpaintVerticalLine(java.awt.Graphics g, javax.swing.JComponent c, int x, int top, int bottom)
Paints a vertical line.

	if (lineTypeDashed) {
	    drawDashedVerticalLine(g, x, top, bottom);
	} else {
	    g.drawLine(x, top, x, bottom);
	}
    
protected voidpaintVerticalPartOfLeg(java.awt.Graphics g, java.awt.Rectangle clipBounds, java.awt.Insets insets, javax.swing.tree.TreePath path)
Paints the vertical part of the leg. The receiver should NOT modify clipBounds, insets.

	if (!paintLines) {
	    return;
	}

        int depth = path.getPathCount() - 1;
	if (depth == 0 && !getShowsRootHandles() && !isRootVisible()) {
	    return;
        }
	int lineX = getRowX(-1, depth + 1);
	if (leftToRight) {
            lineX = lineX - getRightChildIndent() + insets.left;
	}
	else {
	    lineX = lastWidth - getRowX(-1, depth) - 9;
	}
	int clipLeft = clipBounds.x;
	int clipRight = clipBounds.x + (clipBounds.width - 1);

	if (lineX >= clipLeft && lineX <= clipRight) {
	    int clipTop = clipBounds.y;
	    int clipBottom = clipBounds.y + clipBounds.height;
	    Rectangle parentBounds = getPathBounds(tree, path);
	    Rectangle lastChildBounds = getPathBounds(tree,
						     getLastChildPath(path));

	    if(lastChildBounds == null)
		// This shouldn't happen, but if the model is modified
		// in another thread it is possible for this to happen.
		// Swing isn't multithreaded, but I'll add this check in
		// anyway.
		return;

	    int       top;

	    if(parentBounds == null) {
		top = Math.max(insets.top + getVerticalLegBuffer(),
			       clipTop);
	    }
	    else
		top = Math.max(parentBounds.y + parentBounds.height +
			       getVerticalLegBuffer(), clipTop);
	    if(depth == 0 && !isRootVisible()) {
		TreeModel      model = getModel();

		if(model != null) {
		    Object        root = model.getRoot();

		    if(model.getChildCount(root) > 0) {
			parentBounds = getPathBounds(tree, path.
				  pathByAddingChild(model.getChild(root, 0)));
			if(parentBounds != null)
			    top = Math.max(insets.top + getVerticalLegBuffer(),
					   parentBounds.y +
					   parentBounds.height / 2);
		    }
		}
	    }

	    int bottom = Math.min(lastChildBounds.y +
				  (lastChildBounds.height / 2), clipBottom);

            if (top <= bottom) {
                g.setColor(getHashColor());
                paintVerticalLine(g, tree, lineX, top, bottom);
            }
	}
    
protected voidpathWasCollapsed(javax.swing.tree.TreePath path)
Messaged from the VisibleTreeNode after it has collapsed.

	if(tree != null) {
	    tree.fireTreeCollapsed(path);
	}
    
protected voidpathWasExpanded(javax.swing.tree.TreePath path)
Messaged from the VisibleTreeNode after it has been expanded.

	if(tree != null) {
	    tree.fireTreeExpanded(path);
	}
    
protected voidprepareForUIInstall()
Invoked after the tree instance variable has been set, but before any defaults/listeners have been installed.

	drawingCache = new Hashtable<TreePath,Boolean>(7);

	// Data member initializations
	leftToRight = BasicGraphicsUtils.isLeftToRight(tree);
	lastWidth = tree.getWidth();
	stopEditingInCompleteEditing = true;
	lastSelectedRow = -1;
	leadRow = -1;
	preferredSize = new Dimension();

	largeModel = tree.isLargeModel();
	if(getRowHeight() <= 0)
	    largeModel = false;
	setModel(tree.getModel());
    
protected voidprepareForUIUninstall()

    
private voidredoTheLayout()
Recomputes the right margin, and invalidates any tree states

	if (treeState != null) {
	    treeState.invalidateSizes();
	}
    
private voidrepaintPath(javax.swing.tree.TreePath path)
Invokes repaint on the JTree for the passed in TreePath, path.

	if (path != null) {
	    Rectangle bounds = getPathBounds(tree, path);
	    if (bounds != null) {
		tree.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
	    }
	}
    
protected voidselectPathForEvent(javax.swing.tree.TreePath path, java.awt.event.MouseEvent event)
Messaged to update the selection based on a MouseEvent over a particular row. If the event is a toggle selection event, the row is either selected, or deselected. If the event identifies a multi selection event, the selection is updated from the anchor point. Otherwise the row is selected, and if the event specified a toggle event the row is expanded/collapsed.

	/* Adjust from the anchor point. */
	if(isMultiSelectEvent(event)) {
	    TreePath    anchor = getAnchorSelectionPath();
	    int         anchorRow = (anchor == null) ? -1 :
		                    getRowForPath(tree, anchor);

	    if(anchorRow == -1 || tree.getSelectionModel().
                      getSelectionMode() == TreeSelectionModel.
                      SINGLE_TREE_SELECTION) {
		tree.setSelectionPath(path);
	    }
	    else {
		int          row = getRowForPath(tree, path);
		TreePath     lastAnchorPath = anchor;

                if (isToggleSelectionEvent(event)) {
                    if (tree.isRowSelected(anchorRow)) {
                        tree.addSelectionInterval(anchorRow, row);
                    } else {
                        tree.removeSelectionInterval(anchorRow, row);
                        tree.addSelectionInterval(row, row);
                    }
                } else if(row < anchorRow) {
		    tree.setSelectionInterval(row, anchorRow);
                } else {
		    tree.setSelectionInterval(anchorRow, row);
                }
		lastSelectedRow = row;
		setAnchorSelectionPath(lastAnchorPath);
		setLeadSelectionPath(path);
	    }
	}

        // Should this event toggle the selection of this row?
        /* Control toggles just this node. */
        else if(isToggleSelectionEvent(event)) {
            if(tree.isPathSelected(path))
                tree.removeSelectionPath(path);
            else
                tree.addSelectionPath(path);
            lastSelectedRow = getRowForPath(tree, path);
            setAnchorSelectionPath(path);
            setLeadSelectionPath(path);
        }

	/* Otherwise set the selection to just this interval. */
	else if(SwingUtilities.isLeftMouseButton(event)) {
	    tree.setSelectionPath(path);
	    if(isToggleEvent(event)) {
		toggleExpandState(path);
	    }
	}
    
private voidsetAnchorSelectionPath(javax.swing.tree.TreePath newPath)

	ignoreLAChange = true;
	try {
	    tree.setAnchorSelectionPath(newPath);
	} finally{ 
	    ignoreLAChange = false;
	}
    
protected voidsetCellEditor(javax.swing.tree.TreeCellEditor editor)
Sets the cell editor.

	updateCellEditor();
    
protected voidsetCellRenderer(javax.swing.tree.TreeCellRenderer tcr)
Sets the TreeCellRenderer to tcr. This invokes updateRenderer.

	completeEditing();
	updateRenderer();
	if(treeState != null) {
	    treeState.invalidateSizes();
	    updateSize();
	}
    
public voidsetCollapsedIcon(javax.swing.Icon newG)

	collapsedIcon = newG;
    
protected voidsetEditable(boolean newValue)
Configures the receiver to allow, or not allow, editing.

	updateCellEditor();
    
public voidsetExpandedIcon(javax.swing.Icon newG)

	expandedIcon = newG;
    
protected voidsetHashColor(java.awt.Color color)

        hashColor = color;
    
protected voidsetLargeModel(boolean largeModel)
Updates the componentListener, if necessary.

	if(getRowHeight() < 1)
	    largeModel = false;
	if(this.largeModel != largeModel) {
	    completeEditing();
	    this.largeModel = largeModel;
	    treeState = createLayoutCache();
	    configureLayoutCache();
	    updateLayoutCacheExpandedNodes();
	    updateSize();
	}
    
private voidsetLeadSelectionPath(javax.swing.tree.TreePath newPath)

	setLeadSelectionPath(newPath, false);
    
private voidsetLeadSelectionPath(javax.swing.tree.TreePath newPath, boolean repaint)

	Rectangle       bounds = repaint ?
	                    getPathBounds(tree, getLeadSelectionPath()) : null;

	ignoreLAChange = true;
	try {
	    tree.setLeadSelectionPath(newPath);
	} finally {
	    ignoreLAChange = false;
	}
	leadRow = getRowForPath(tree, newPath);

	if(repaint) {
	    if(bounds != null)
		tree.repaint(bounds);
	    bounds = getPathBounds(tree, newPath);
	    if(bounds != null)
		tree.repaint(bounds);
	}
    
public voidsetLeftChildIndent(int newAmount)

	leftChildIndent = newAmount;
	totalChildIndent = leftChildIndent + rightChildIndent;
	if(treeState != null)
	    treeState.invalidateSizes();
	updateSize();
    
protected voidsetModel(javax.swing.tree.TreeModel model)
Sets the TreeModel.

	completeEditing();
	if(treeModel != null && treeModelListener != null)
	    treeModel.removeTreeModelListener(treeModelListener);
	treeModel = model;
	if(treeModel != null) {
	    if(treeModelListener != null)
		treeModel.addTreeModelListener(treeModelListener);
	}
	if(treeState != null) {
	    treeState.setModel(model);
	    updateLayoutCacheExpandedNodes();
	    updateSize();
	}
    
public voidsetPreferredMinSize(java.awt.Dimension newSize)
Sets the preferred minimum size.

	preferredMinSize = newSize;
    
public voidsetRightChildIndent(int newAmount)

	rightChildIndent = newAmount;
	totalChildIndent = leftChildIndent + rightChildIndent;
	if(treeState != null)
	    treeState.invalidateSizes();
	updateSize();
    
protected voidsetRootVisible(boolean newValue)
Sets the root to being visible.

	completeEditing();
	updateDepthOffset();
	if(treeState != null) {
	    treeState.setRootVisible(newValue);
	    treeState.invalidateSizes();
	    updateSize();
	}
    
protected voidsetRowHeight(int rowHeight)
Sets the row height, this is forwarded to the treeState.

	completeEditing();
	if(treeState != null) {
	    setLargeModel(tree.isLargeModel());
	    treeState.setRowHeight(rowHeight);
	    updateSize();
	}
    
protected voidsetSelectionModel(javax.swing.tree.TreeSelectionModel newLSM)
Resets the selection model. The appropriate listener are installed on the model.

	completeEditing();
	if(selectionModelPropertyChangeListener != null &&
	   treeSelectionModel != null)
	    treeSelectionModel.removePropertyChangeListener
		              (selectionModelPropertyChangeListener);
	if(treeSelectionListener != null && treeSelectionModel != null)
	    treeSelectionModel.removeTreeSelectionListener
		               (treeSelectionListener);
	treeSelectionModel = newLSM;
	if(treeSelectionModel != null) {
	    if(selectionModelPropertyChangeListener != null)
		treeSelectionModel.addPropertyChangeListener
		              (selectionModelPropertyChangeListener);
	    if(treeSelectionListener != null)
		treeSelectionModel.addTreeSelectionListener
		                   (treeSelectionListener);
	    if(treeState != null)
		treeState.setSelectionModel(treeSelectionModel);
	}
	else if(treeState != null)
	    treeState.setSelectionModel(null);
	if(tree != null)
	    tree.repaint();
    
protected voidsetShowsRootHandles(boolean newValue)
Determines whether the node handles are to be displayed.

	completeEditing();
	updateDepthOffset();
	if(treeState != null) {
	    treeState.invalidateSizes();
	    updateSize();
	}
    
protected booleanshouldPaintExpandControl(javax.swing.tree.TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf)
Returns true if the expand (toggle) control should be drawn for the specified row.

	if(isLeaf)
	    return false;

	int              depth = path.getPathCount() - 1;

	if((depth == 0 || (depth == 1 && !isRootVisible())) &&
	   !getShowsRootHandles())
	    return false;
	return true;
    
protected booleanstartEditing(javax.swing.tree.TreePath path, java.awt.event.MouseEvent event)
Will start editing for node if there is a cellEditor and shouldSelectCell returns true.

This assumes that path is valid and visible.

        if (isEditing(tree) && tree.getInvokesStopCellEditing() &&
                               !stopEditing(tree)) {
            return false;
        }
	completeEditing();
	if(cellEditor != null && tree.isPathEditable(path)) {
	    int           row = getRowForPath(tree, path);

	    if(cellEditor.isCellEditable(event)) {
                editingComponent = cellEditor.getTreeCellEditorComponent
		      (tree, path.getLastPathComponent(),
		       tree.isPathSelected(path), tree.isExpanded(path),
		       treeModel.isLeaf(path.getLastPathComponent()), row);

		Rectangle           nodeBounds = getPathBounds(tree, path);

		editingRow = row;

		Dimension editorSize = editingComponent.getPreferredSize();

		// Only allow odd heights if explicitly set.
		if(editorSize.height != nodeBounds.height &&
		   getRowHeight() > 0)
		    editorSize.height = getRowHeight();

		if(editorSize.width != nodeBounds.width ||
		   editorSize.height != nodeBounds.height) {
		    // Editor wants different width or height, invalidate 
		    // treeState and relayout.
		    editorHasDifferentSize = true;
		    treeState.invalidatePathBounds(path);
		    updateSize();
		}
		else
		    editorHasDifferentSize = false;
		tree.add(editingComponent);
		editingComponent.setBounds(nodeBounds.x, nodeBounds.y,
					   editorSize.width,
					   editorSize.height);
		editingPath = path;
		editingComponent.validate();

		Rectangle              visRect = tree.getVisibleRect();

		tree.paintImmediately(nodeBounds.x, nodeBounds.y,
				      visRect.width + visRect.x - nodeBounds.x,
				      editorSize.height);
		if(cellEditor.shouldSelectCell(event)) {
		    stopEditingInCompleteEditing = false;
		    try {
			tree.setSelectionRow(row);
		    } catch (Exception e) {
			System.err.println("Editing exception: " + e);
		    }
		    stopEditingInCompleteEditing = true;
		}

		Component focusedComponent = BasicLookAndFeel.
		                 compositeRequestFocus(editingComponent);
		boolean selectAll = true;

		if(event != null && event instanceof MouseEvent) {
		    /* Find the component that will get forwarded all the
		       mouse events until mouseReleased. */
		    Point          componentPoint = SwingUtilities.convertPoint
			(tree, new Point(event.getX(), event.getY()),
			 editingComponent);

		    /* Create an instance of BasicTreeMouseListener to handle
		       passing the mouse/motion events to the necessary
		       component. */
		    // We really want similar behavior to getMouseEventTarget,
		    // but it is package private.
		    Component activeComponent = SwingUtilities.
			            getDeepestComponentAt(editingComponent,
				       componentPoint.x, componentPoint.y);
		    if (activeComponent != null) {
                        MouseInputHandler handler =
                            new MouseInputHandler(tree, activeComponent,
                                                  event, focusedComponent);

                        if (releaseEvent != null) {
                            handler.mouseReleased(releaseEvent);
                        }

                        selectAll = false;
		    }
		}
		if (selectAll && focusedComponent instanceof JTextField) {
		    ((JTextField)focusedComponent).selectAll();
		}
		return true;
	    }
	    else
		editingComponent = null;
	}
	return false;
    
public voidstartEditingAtPath(javax.swing.JTree tree, javax.swing.tree.TreePath path)
Selects the last item in path and tries to edit it. Editing will fail if the CellEditor won't allow it for the selected item.

	tree.scrollPathToVisible(path);
	if(path != null && tree.isVisible(path))
	    startEditing(path, null);
    
private booleanstartEditingOnRelease(javax.swing.tree.TreePath path, java.awt.event.MouseEvent event, java.awt.event.MouseEvent releaseEvent)

        this.releaseEvent = releaseEvent;
        try {
            return startEditing(path, event);
        } finally {
            this.releaseEvent = null;
        }
    
public booleanstopEditing(javax.swing.JTree tree)
Stops the current editing session. This has no effect if the tree isn't being edited. Returns true if the editor allows the editing session to stop.

	if(editingComponent != null && cellEditor.stopCellEditing()) {
	    completeEditing(false, false, true);
	    return true;
	}
	return false;
    
protected voidtoggleExpandState(javax.swing.tree.TreePath path)
Expands path if it is not expanded, or collapses row if it is expanded. If expanding a path and JTree scrolls on expand, ensureRowsAreVisible is invoked to scroll as many of the children to visible as possible (tries to scroll to last visible descendant of path).

	if(!tree.isExpanded(path)) {
	    int       row = getRowForPath(tree, path);

	    tree.expandPath(path);
	    updateSize();
	    if(row != -1) {
		if(tree.getScrollsOnExpand())
		    ensureRowsAreVisible(row, row + treeState.
					 getVisibleChildCount(path));
		else
		    ensureRowsAreVisible(row, row);
	    }
	}
	else {
	    tree.collapsePath(path);
	    updateSize();
	}
    
protected voiduninstallComponents()
Uninstalls the renderer pane.

	if(rendererPane != null) {
	    tree.remove(rendererPane);
	}
    
protected voiduninstallDefaults()

	if (tree.getTransferHandler() instanceof UIResource) {
	    tree.setTransferHandler(null);
	}
    
protected voiduninstallKeyboardActions()

	SwingUtilities.replaceUIActionMap(tree, null);
	SwingUtilities.replaceUIInputMap(tree, JComponent.
					 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
					 null);
	SwingUtilities.replaceUIInputMap(tree, JComponent.WHEN_FOCUSED, null);
    
protected voiduninstallListeners()

	if(componentListener != null) {
	    tree.removeComponentListener(componentListener);
	}
        if (propertyChangeListener != null) {
	    tree.removePropertyChangeListener(propertyChangeListener);
	}
        if (!DRAG_FIX) {
            tree.removeMouseListener(defaultDragRecognizer);
            tree.removeMouseMotionListener(defaultDragRecognizer);
        }
        if (mouseListener != null) {
	    tree.removeMouseListener(mouseListener);
	    if (mouseListener instanceof MouseMotionListener) {
		tree.removeMouseMotionListener((MouseMotionListener)mouseListener);
	    }
	}
        if (focusListener != null) {
	    tree.removeFocusListener(focusListener);
	}
        if (keyListener != null) {
	    tree.removeKeyListener(keyListener);
	}
	if(treeExpansionListener != null) {
	    tree.removeTreeExpansionListener(treeExpansionListener);
	}
	if(treeModel != null && treeModelListener != null) {
	    treeModel.removeTreeModelListener(treeModelListener);
	}
	if(selectionModelPropertyChangeListener != null &&
	   treeSelectionModel != null) {
	    treeSelectionModel.removePropertyChangeListener
		(selectionModelPropertyChangeListener);
	}
	if(treeSelectionListener != null && treeSelectionModel != null) {
	    treeSelectionModel.removeTreeSelectionListener
		               (treeSelectionListener);
	}
        handler = null;
    
public voiduninstallUI(javax.swing.JComponent c)

	completeEditing();

	prepareForUIUninstall();

	uninstallDefaults();
	uninstallListeners();
	uninstallKeyboardActions();
	uninstallComponents();

	completeUIUninstall();
    
protected voidupdateCachedPreferredSize()
Updates the preferredSize instance variable, which is returned from getPreferredSize().

For left to right orientations, the size is determined from the current AbstractLayoutCache. For RTL orientations, the preferred size becomes the width minus the minimum x position.

	if(treeState != null) {
	    Insets               i = tree.getInsets();

	    if(isLargeModel()) {
		Rectangle            visRect = tree.getVisibleRect();

		if(i != null) {
		    visRect.x -= i.left;
		    visRect.y -= i.top;
		}
		if (leftToRight) {
		    preferredSize.width = treeState.getPreferredWidth(visRect);
		}
		else {
		    if (getRowCount(tree) == 0) {
			preferredSize.width = 0;
		    }
		    else {
			preferredSize.width = lastWidth - getMinX(visRect);
		    }
		}
	    }
	    else if (leftToRight) {
		preferredSize.width = treeState.getPreferredWidth(null);
	    }
	    else {
		Rectangle tempRect = null;
		int rowCount = tree.getRowCount();
		int width = 0;
		for (int counter = 0; counter < rowCount; counter++) {
		    tempRect = treeState.getBounds
			       (treeState.getPathForRow(counter), tempRect);
		    if (tempRect != null) {
			width = Math.max(lastWidth - tempRect.x, width);
		    }
		}
		preferredSize.width = width;
	    }
	    preferredSize.height = treeState.getPreferredHeight();
	    if(i != null) {
		preferredSize.width += i.left + i.right;
		preferredSize.height += i.top + i.bottom;
	    }
	}
	validCachedPreferredSize = true;
    
protected voidupdateCellEditor()
Updates the cellEditor based on the editability of the JTree that we're contained in. If the tree is editable but doesn't have a cellEditor, a basic one will be used.

	TreeCellEditor        newEditor;

	completeEditing();
	if(tree == null)
	    newEditor = null;
	else {
	    if(tree.isEditable()) {
		newEditor = tree.getCellEditor();
		if(newEditor == null) {
		    newEditor = createDefaultCellEditor();
		    if(newEditor != null) {
			tree.setCellEditor(newEditor);
			createdCellEditor = true;
		    }
		}
	    }
	    else
		newEditor = null;
	}
	if(newEditor != cellEditor) {
	    if(cellEditor != null && cellEditorListener != null)
		cellEditor.removeCellEditorListener(cellEditorListener);
	    cellEditor = newEditor;
	    if(cellEditorListener == null)
		cellEditorListener = createCellEditorListener();
	    if(newEditor != null && cellEditorListener != null)
		newEditor.addCellEditorListener(cellEditorListener);
	    createdCellEditor = false;
	}
    
protected voidupdateDepthOffset()
Updates how much each depth should be offset by.

	if(isRootVisible()) {
	    if(getShowsRootHandles())
		depthOffset = 1;
	    else
		depthOffset = 0;
	}
	else if(!getShowsRootHandles())
	    depthOffset = -1;
	else
	    depthOffset = 0;
    
protected voidupdateExpandedDescendants(javax.swing.tree.TreePath path)
Updates the expanded state of all the descendants of path by getting the expanded descendants from the tree and forwarding to the tree state.

	completeEditing();
	if(treeState != null) {
	    treeState.setExpandedState(path, true);

	    Enumeration   descendants = tree.getExpandedDescendants(path);

	    if(descendants != null) {
		while(descendants.hasMoreElements()) {
		    path = (TreePath)descendants.nextElement();
		    treeState.setExpandedState(path, true);
		}
	    }
	    updateLeadRow();
	    updateSize();
	}
    
protected voidupdateLayoutCacheExpandedNodes()
Makes all the nodes that are expanded in JTree expanded in LayoutCache. This invokes updateExpandedDescendants with the root path.

	if(treeModel != null && treeModel.getRoot() != null)
	    updateExpandedDescendants(new TreePath(treeModel.getRoot()));
    
private voidupdateLeadRow()

	leadRow = getRowForPath(tree, getLeadSelectionPath());
    
protected voidupdateRenderer()
Messaged from the tree we're in when the renderer has changed.

	if(tree != null) {
	    TreeCellRenderer      newCellRenderer;

	    newCellRenderer = tree.getCellRenderer();
	    if(newCellRenderer == null) {
		tree.setCellRenderer(createDefaultCellRenderer());
		createdRenderer = true;
	    }
	    else {
		createdRenderer = false;
		currentCellRenderer = newCellRenderer;
		if(createdCellEditor) {
		    tree.setCellEditor(null);
		}
	    }
	}
	else {
	    createdRenderer = false;
	    currentCellRenderer = null;
	}
	updateCellEditor();
    
protected voidupdateSize()
Marks the cached size as being invalid, and messages the tree with treeDidChange.

	validCachedPreferredSize = false;
	tree.treeDidChange();