/*
 * BasicTreeUI.java -- Copyright (C) 2002, 2004 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Classpath.
 * 
 * GNU Classpath is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2, or (at your option) any later version.
 * 
 * GNU Classpath is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * GNU Classpath; see the file COPYING. If not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 * 
 * Linking this library statically or dynamically with other modules is making a
 * combined work based on this library. Thus, the terms and conditions of the
 * GNU General Public License cover the whole combination.
 * 
 * As a special exception, the copyright holders of this library give you
 * permission to link this library with independent modules to produce an
 * executable, regardless of the license terms of these independent modules, and
 * to copy and distribute the resulting executable under terms of your choice,
 * provided that you also meet, for each linked independent module, the terms
 * and conditions of the license of that module. An independent module is a
 * module which is not derived from or based on this library. If you modify this
 * library, you may extend this exception to your version of the library, but
 * you are not obligated to do so. If you do not wish to do so, delete this
 * exception statement from your version.
 */

package javax.swing.plaf.basic;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.CellRendererPane;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.Timer;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.MouseInputListener;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.TreeUI;
import javax.swing.tree.AbstractLayoutCache;
import javax.swing.tree.FixedHeightLayoutCache;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellEditor;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeSelectionModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import java.util.Enumeration;
import java.util.Hashtable;

/**
 * A delegate providing the user interface for <code>JTree</code> according to
 * the Basic look and feel.
 * 
 * @see javax.swing.JTree
 * @author Sascha Brawer (brawer@dandelis.ch)
 * @author Lillian Angel (langel@redhat.com)
 */
public class BasicTreeUI
		extends TreeUI
{

	/** Collapse Icon for the tree. */
	protected transient Icon collapsedIcon;

	/** Expanded Icon for the tree. */
	protected transient Icon expandedIcon;

	/** Distance between left margin and where vertical dashes will be drawn. */
	protected int leftChildIndent;

	/**
	 * Distance between leftChildIndent and where cell contents will be drawn.
	 */
	protected int rightChildIndent;

	/**
	 * Total fistance that will be indented. The sum of leftChildIndent and
	 * rightChildIndent .
	 */
	protected int totalChildIndent;

	/** Minimum preferred size. */
	protected Dimension preferredMinsize;

	/** Index of the row that was last selected. */
	protected int lastSelectedRow;

	/** Component that we're going to be drawing onto. */
	protected JTree tree;

	/** Renderer that is being used to do the actual cell drawing. */
	protected transient TreeCellRenderer currentCellRenderer;

	/**
	 * Set to true if the renderer that is currently in the tree was created by
	 * this instance.
	 */
	protected boolean createdRenderer;

	/** Editor for the tree. */
	protected transient TreeCellEditor cellEditor;

	/**
	 * Set to true if editor that is currently in the tree was created by this
	 * instance.
	 */
	protected boolean createdCellEditor;

	/**
	 * Set to false when editing and shouldSelectCall() returns true meaning the
	 * node should be selected before editing, used in completeEditing.
	 */
	protected boolean stopEditingInCompleteEditing;

	/** Used to paint the TreeCellRenderer. */
	protected CellRendererPane rendererPane;

	/** Size needed to completely display all the nodes. */
	protected Dimension preferredSize;

	/** Is the preferredSize valid? */
	protected boolean validCachedPreferredSize;

	/** Object responsible for handling sizing and expanded issues. */
	protected AbstractLayoutCache treeState;

	/** Used for minimizing the drawing of vertical lines. */
	protected Hashtable drawingCache;

	/**
	 * 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 boolean largeModel;

	/** Responsible for telling the TreeState the size needed for a node. */
	protected AbstractLayoutCache.NodeDimensions nodeDimensions;

	/** Used to determine what to display. */
	protected TreeModel treeModel;

	/** Model maintaining the selection. */
	protected TreeSelectionModel treeSelectionModel;

	/**
	 * 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.
	 */
	protected int depthOffset;

	/**
	 * When editing, this will be the Component that is doing the actual
	 * editing.
	 */
	protected Component editingComponent;

	/** Path that is being edited. */
	protected TreePath editingPath;

	/**
	 * Row that is being edited. Should only be referenced if editingComponent
	 * is null.
	 */
	protected int editingRow;

	/** Set to true if the editor has a different size than the renderer. */
	protected boolean editorHasDifferentSize;

	/** Listeners */
	private PropertyChangeListener propertyChangeListener;
	private FocusListener focusListener;
	private TreeSelectionListener treeSelectionListener;
	private MouseInputListener mouseInputListener;
	private KeyListener keyListener;
	private PropertyChangeListener selectionModelPropertyChangeListener;
	private ComponentListener componentListener;
	private CellEditorListener cellEditorListener;
	private TreeExpansionListener treeExpansionListener;
	private TreeModelListener treeModelListener;

	/**
	 * Creates a new BasicTreeUI object.
	 */
	public BasicTreeUI()
	{
		drawingCache = new Hashtable();
		cellEditor = createDefaultCellEditor();
		currentCellRenderer = createDefaultCellRenderer();
		nodeDimensions = createNodeDimensions();
		rendererPane = createCellRendererPane();
		configureLayoutCache();

		propertyChangeListener = createPropertyChangeListener();
		focusListener = createFocusListener();
		treeSelectionListener = createTreeSelectionListener();
		mouseInputListener = new MouseInputHandler(null, null, null);
		keyListener = createKeyListener();
		selectionModelPropertyChangeListener = createSelectionModelPropertyChangeListener();
		componentListener = createComponentListener();
		cellEditorListener = createCellEditorListener();
		treeExpansionListener = createTreeExpansionListener();
		treeModelListener = createTreeModelListener();

		createdRenderer = true;
		createdCellEditor = true;
		editingRow = -1;
		lastSelectedRow = -1;
	}

	/**
	 * Returns an instance of the UI delegate for the specified component.
	 * 
	 * @param c the <code>JComponent</code> for which we need a UI delegate
	 *        for.
	 * @return the <code>ComponentUI</code> for c.
	 */
	public static ComponentUI createUI(JComponent c)
	{
		return new BasicTreeUI();
	}

	/**
	 * Returns the Hash color.
	 * 
	 * @return the <code>Color</code> of the Hash.
	 */
	protected Color getHashColor()
	{
		return UIManager.getLookAndFeelDefaults().getColor("Tree.hash");
	}

	/**
	 * Sets the Hash color.
	 * 
	 * @param the <code>Color</code> to set the Hash to.
	 */
	protected void setHashColor(Color color)
	{
		// FIXME: not implemented

	}

	/**
	 * Sets the left child's indent value.
	 * 
	 * @param newAmount is the new indent value for the left child.
	 */
	public void setLeftChildIndent(int newAmount)
	{
		leftChildIndent = newAmount;
	}

	/**
	 * Returns the indent value for the left child.
	 * 
	 * @return the indent value for the left child.
	 */
	public int getLeftChildIndent(int newAmount)
	{
		return leftChildIndent;
	}

	/**
	 * Sets the right child's indent value.
	 * 
	 * @param newAmount is the new indent value for the right child.
	 */
	public void setRightChildIndent(int newAmount)
	{
		rightChildIndent = newAmount;
	}

	/**
	 * Returns the indent value for the right child.
	 * 
	 * @return the indent value for the right child.
	 */
	public int getRightChildIndent(int newAmount)
	{
		return rightChildIndent;
	}

	/**
	 * Sets the expanded icon.
	 * 
	 * @param newG is the new expanded icon.
	 */
	public void setExpandedIcon(Icon newG)
	{
		expandedIcon = newG;
	}

	/**
	 * Returns the current expanded icon.
	 * 
	 * @return the current expanded icon.
	 */
	public Icon getExpandedIcon()
	{
		return expandedIcon;
	}

	/**
	 * Sets the collapsed icon.
	 * 
	 * @param newG is the new collapsed icon.
	 */
	public void setCollapsedIcon(Icon newG)
	{
		collapsedIcon = newG;
	}

	/**
	 * Returns the current collapsed icon.
	 * 
	 * @return the current collapsed icon.
	 */
	public Icon getCollapsedIcon()
	{
		return collapsedIcon;
	}

	/**
	 * Updates the componentListener, if necessary.
	 * 
	 * @param largeModel sets this.largeModel to it.
	 */
	protected void setLargeModel(boolean largeModel)
	{
		if (largeModel != this.largeModel)
		{
			tree.removeComponentListener(componentListener);
			this.largeModel = largeModel;
			tree.addComponentListener(componentListener);
		}
	}

	/**
	 * Returns true if largeModel is set
	 * 
	 * @return true if largeModel is set, otherwise false.
	 */
	protected boolean isLargeModel()
	{
		return largeModel;
	}

	/**
	 * Sets the row height.
	 * 
	 * @param rowHeight is the height to set this.rowHeight to.
	 */
	protected void setRowHeight(int rowHeight)
	{
		treeState.setRowHeight(rowHeight);
	}

	/**
	 * Returns the current row height.
	 * 
	 * @return current row height.
	 */
	protected int getRowHeight()
	{
		return treeState.getRowHeight();
	}

	/**
	 * Sets the TreeCellRenderer to <code>tcr</code>. This invokes
	 * <code>updateRenderer</code>.
	 * 
	 * @param tcr is the new TreeCellRenderer.
	 */
	protected void setCellRenderer(TreeCellRenderer tcr)
	{
		currentCellRenderer = tcr;
		updateRenderer();
	}

	/**
	 * Return currentCellRenderer, which will either be the trees renderer, or
	 * defaultCellRenderer, which ever was not null.
	 * 
	 * @return the current Cell Renderer
	 */
	protected TreeCellRenderer getCellRenderer()
	{
		if (currentCellRenderer != null)
			return currentCellRenderer;

		return createDefaultCellRenderer();
	}

	/**
	 * Sets the tree's model.
	 * 
	 * @param model to set the treeModel to.
	 */
	protected void setModel(TreeModel model)
	{
		treeState.setModel(model);
		treeModel = model;
	}

	/**
	 * Returns the tree's model
	 * 
	 * @return treeModel
	 */
	protected TreeModel getModel()
	{
		return treeModel;
	}

	/**
	 * Sets the root to being visible.
	 * 
	 * @param newValue sets the visibility of the root
	 */
	protected void setRootVisible(boolean newValue)
	{
		treeState.setRootVisible(newValue);
	}

	/**
	 * Returns true if the root is visible.
	 * 
	 * @return true if the root is visible.
	 */
	protected boolean isRootVisible()
	{
		return treeState.isRootVisible();
	}

	/**
	 * Determines whether the node handles are to be displayed.
	 * 
	 * @param newValue sets whether or not node handles should be displayed.
	 */
	protected void setShowsRootHandles(boolean newValue)
	{
		tree.setShowsRootHandles(newValue);
	}

	/**
	 * Returns true if the node handles are to be displayed.
	 * 
	 * @return true if the node handles are to be displayed.
	 */
	protected boolean getShowsRootHandles()
	{
		return tree.getShowsRootHandles();
	}

	/**
	 * Sets the cell editor.
	 * 
	 * @param editor to set the cellEditor to.
	 */
	protected void setCellEditor(TreeCellEditor editor)
	{
		cellEditor = editor;
	}

	/**
	 * Returns the <code>TreeCellEditor</code> for this tree.
	 * 
	 * @return the cellEditor for this tree.
	 */
	protected TreeCellEditor getCellEditor()
	{
		return cellEditor;
	}

	/**
	 * Configures the receiver to allow, or not allow, editing.
	 * 
	 * @param newValue sets the receiver to allow editing if true.
	 */
	protected void setEditable(boolean newValue)
	{
		tree.setEditable(newValue);
	}

	/**
	 * Returns true if the receiver allows editing.
	 * 
	 * @return true if the receiver allows editing.
	 */
	protected boolean isEditable()
	{
		return tree.isEditable();
	}

	/**
	 * Resets the selection model. The appropriate listeners are installed on
	 * the model.
	 * 
	 * @param newLSM resets the selection model.
	 */
	protected void setSelectionModel(TreeSelectionModel newLSM)
	{
		if (newLSM != null)
		{
			treeSelectionModel = newLSM;
			tree.setSelectionModel(treeSelectionModel);
		}
	}

	/**
	 * Returns the current selection model.
	 * 
	 * @return the current selection model.
	 */
	protected TreeSelectionModel getSelectionModel()
	{
		return treeSelectionModel;
	}

	/**
	 * Returns the Rectangle enclosing the label portion that the last item in
	 * path will be drawn to. Will return null if any component in path is
	 * currently valid.
	 * 
	 * @param tree is the current tree the path will be drawn to.
	 * @param path is the current path the tree to draw to.
	 * @return the Rectangle enclosing the label portion that the last item in
	 *         the path will be drawn to.
	 */
	public Rectangle getPathBounds(JTree tree, TreePath path)
	{
		// FIXME: not implemented
		return null;
	}

	/**
	 * Returns the path for passed in row. If row is not visible null is
	 * returned.
	 * 
	 * @param tree is the current tree to return path for.
	 * @param row is the row number of the row to return.
	 * @return the path for passed in row. If row is not visible null is
	 *         returned.
	 */
	public TreePath getPathForRow(JTree tree, int row)
	{
		// FIXME: check visibility when expand/collapse is implemented
		DefaultMutableTreeNode pathForRow = ((DefaultMutableTreeNode) (tree
				.getModel()).getRoot());
		for (int i = 0; i < row; i++)
		{
			if (pathForRow != null)
				pathForRow = pathForRow.getNextNode();
		}
		if (pathForRow == null)
			return null;
		return new TreePath(pathForRow.getPath());
	}

	/**
	 * Returns the row that the last item identified in path is visible at. Will
	 * return -1 if any of the elments in the path are not currently visible.
	 * 
	 * @param tree is the current tree to return the row for.
	 * @param path is the path used to find the row.
	 * @return the row that the last item identified in path is visible at. Will
	 *         return -1 if any of the elments in the path are not currently
	 *         visible.
	 */
	public int getRowForPath(JTree tree, TreePath path)
	{
		// FIXME: check visibility
		// right now, just returns last element because
		// expand/collapse is not implemented
		return path.getPathCount() - 1;
	}

	/**
	 * Returns the number of rows that are being displayed.
	 * 
	 * @param tree is the current tree to return the number of rows for.
	 * @return the number of rows being displayed.
	 */
	public int getRowCount(JTree tree)
	{
		return treeState.getRowCount();
	}

	/**
	 * 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.
	 * 
	 * @param tree the tree to search for the closest path
	 * @param x is the x coordinate of the location to search
	 * @param y is the y coordinate of the location to search
	 * @return the tree path closes to x,y.
	 */
	public TreePath getClosestPathForLocation(JTree tree, int x, int y)
	{
		return treeState.getPathClosestTo(x, y);
	}

	/**
	 * Returns true if the tree is being edited. The item that is being edited
	 * can be returned by getEditingPath().
	 * 
	 * @param tree is the tree to check for editing.
	 * @return true if the tree is being edited.
	 */
	public boolean isEditing(JTree tree)
	{
		// FIXME: not implemented
		return false;
	}

	/**
	 * Stops the current editing session. This has no effect if the tree is not
	 * being edited. Returns true if the editor allows the editing session to
	 * stop.
	 * 
	 * @param tree is the tree to stop the editing on
	 * @return true if the editor allows the editing session to stop.
	 */
	public boolean stopEditing(JTree tree)
	{
		// FIXME: not implemented
		return false;
	}

	/**
	 * Cancels the current editing session.
	 * 
	 * @param tree is the tree to cancel the editing session on.
	 */
	public void cancelEditing(JTree tree)
	{
		// FIXME: not implemented
	}

	/**
	 * 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.
	 * 
	 * @param tree is the tree to edit on.
	 * @param path is the path in tree to edit on.
	 */
	public void startEditingAtPath(JTree tree, TreePath path)
	{
		// FIXME: not implemented
	}

	/**
	 * Returns the path to the element that is being editted.
	 * 
	 * @param tree is the tree to get the editing path from.
	 * @return the path that is being edited.
	 */
	public TreePath getEditingPath(JTree tree)
	{
		// FIXME: not implemented
		return null;
	}

	/**
	 * Invoked after the tree instance variable has been set, but before any
	 * default/listeners have been installed.
	 */
	protected void prepareForUIInstall()
	{
		// FIXME: not implemented
	}

	/**
	 * Invoked from installUI after all the defaults/listeners have been
	 * installed.
	 */
	protected void completeUIInstall()
	{
		// FIXME: not implemented
	}

	/**
	 * Invoked from uninstallUI after all the defaults/listeners have been
	 * uninstalled.
	 */
	protected void completeUIUninstall()
	{
		// FIXME: not implemented
	}

	/**
	 * Installs the subcomponents of the tree, which is the renderer pane.
	 */
	protected void installComponents()
	{
		// FIXME: not implemented
	}

	/**
	 * Creates an instance of NodeDimensions that is able to determine the size
	 * of a given node in the tree.
	 * 
	 * @return the NodeDimensions of a given node in the tree
	 */
	protected AbstractLayoutCache.NodeDimensions createNodeDimensions()
	{
		// FIXME: not implemented
		return null;
	}

	/**
	 * Creates a listener that is reponsible for the updates the UI based on how
	 * the tree changes.
	 * 
	 * @return the PropertyChangeListener that is reposnsible for the updates
	 */
	protected PropertyChangeListener createPropertyChangeListener()
	{
		return new PropertyChangeHandler();
	}

	/**
	 * Creates the listener responsible for updating the selection based on
	 * mouse events.
	 * 
	 * @return the MouseListener responsible for updating.
	 */
	protected MouseListener createMouseListener()
	{
		return new MouseHandler();
	}

	/**
	 * Creates the listener that is responsible for updating the display when
	 * focus is lost/grained.
	 * 
	 * @return the FocusListener responsible for updating.
	 */
	protected FocusListener createFocusListener()
	{
		return new FocusHandler();
	}

	/**
	 * Creates the listener reponsible for getting key events from the tree.
	 * 
	 * @return the KeyListener responsible for getting key events.
	 */
	protected KeyListener createKeyListener()
	{
		return new KeyHandler();
	}

	/**
	 * Creates the listener responsible for getting property change events from
	 * the selection model.
	 * 
	 * @returns the PropertyChangeListener reponsible for getting property
	 *          change events from the selection model.
	 */
	protected PropertyChangeListener createSelectionModelPropertyChangeListener()
	{
		return new SelectionModelPropertyChangeHandler();
	}

	/**
	 * Creates the listener that updates the display based on selection change
	 * methods.
	 * 
	 * @return the TreeSelectionListener responsible for updating.
	 */
	protected TreeSelectionListener createTreeSelectionListener()
	{
		return new TreeSelectionHandler();
	}

	/**
	 * Creates a listener to handle events from the current editor
	 * 
	 * @return the CellEditorListener that handles events from the current
	 *         editor
	 */
	protected CellEditorListener createCellEditorListener()
	{
		return new CellEditorHandler();
	}

	/**
	 * Creates and returns a new ComponentHandler. This is used for the large
	 * model to mark the validCachedPreferredSize as invalid when the component
	 * moves.
	 * 
	 * @return a new ComponentHandler.
	 */
	protected ComponentListener createComponentListener()
	{
		return new ComponentHandler();
	}

	/**
	 * Creates and returns the object responsible for updating the treestate
	 * when a nodes expanded state changes.
	 * 
	 * @return the TreeExpansionListener responsible for updating the treestate
	 */
	protected TreeExpansionListener createTreeExpansionListener()
	{
		return new TreeExpansionHandler();
	}

	/**
	 * Creates the object responsible for managing what is expanded, as well as
	 * the size of nodes.
	 * 
	 * @return the object responsible for managing what is expanded.
	 */
	protected AbstractLayoutCache createLayoutCache()
	{
		return new FixedHeightLayoutCache();
	}

	/**
	 * Returns the renderer pane that renderer components are placed in.
	 * 
	 * @return the rendererpane that render components are placed in.
	 */
	protected CellRendererPane createCellRendererPane()
	{
		return new CellRendererPane();
	}

	/**
	 * Creates a default cell editor.
	 * 
	 * @return the default cell editor.
	 */
	protected TreeCellEditor createDefaultCellEditor()
	{
		return new DefaultTreeCellEditor(tree,
				(DefaultTreeCellRenderer) createDefaultCellRenderer(),
				cellEditor);
	}

	/**
	 * Returns the default cell renderer that is used to do the stamping of each
	 * node.
	 * 
	 * @return the default cell renderer that is used to do the stamping of each
	 *         node.
	 */
	protected TreeCellRenderer createDefaultCellRenderer()
	{
		return new DefaultTreeCellRenderer();
	}

	/**
	 * Returns a listener that can update the tree when the model changes.
	 * 
	 * @return a listener that can update the tree when the model changes.
	 */
	protected TreeModelListener createTreeModelListener()
	{
		return new TreeModelHandler();
	}

	/**
	 * Uninstall all registered listeners
	 */
	protected void uninstallListeners()
	{
		tree.removePropertyChangeListener(propertyChangeListener);
		tree.removeFocusListener(focusListener);
		tree.removeTreeSelectionListener(treeSelectionListener);
		tree.removeMouseListener(mouseInputListener);
		tree.removeKeyListener(keyListener);
		tree.removePropertyChangeListener(selectionModelPropertyChangeListener);
		tree.removeComponentListener(componentListener);
		tree.getCellEditor().removeCellEditorListener(cellEditorListener);
		tree.removeTreeExpansionListener(treeExpansionListener);
		tree.getModel().removeTreeModelListener(treeModelListener);
	}

	/**
	 * Uninstall all keyboard actions.
	 */
	protected void uninstallKeyboardActions()
	{
	}

	/**
	 * Uninstall the rendererPane.
	 */
	protected void uninstallComponents()
	{
		// FIXME: not implemented
	}

	/**
	 * 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 the vertical leg buffer
	 */
	protected int getVerticalLegBuffer()
	{
		// FIXME: not implemented
		return 0;
	}

	/**
	 * 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 the horizontal leg buffer
	 */
	protected int getHorizontalLegBuffer()
	{
		// FIXME: not implemented
		return 0;
	}

	/**
	 * Make all the nodes that are expanded in JTree expanded in LayoutCache.
	 * This invokes update ExpandedDescendants with the root path.
	 */
	protected void updateLayoutCacheExpandedNodes()
	{
		// FIXME: not implemented
	}

	/**
	 * Updates the expanded state of all the descendants of the
	 * <code>path</code> by getting the expanded descendants from the tree and
	 * forwarding to the tree state.
	 * 
	 * @param path the path used to update the expanded states
	 */
	protected void updateExpandedDescendants(TreePath path)
	{
		// FIXME: not implemented
	}

	/**
	 * Returns a path to the last child of <code>parent</code>
	 * 
	 * @param parent is the topmost path to specified
	 * @return a path to the last child of parent
	 */
	protected TreePath getLastChildPath(TreePath parent)
	{
		return ((TreePath) parent.getLastPathComponent());
	}

	/**
	 * Updates how much each depth should be offset by.
	 */
	protected void updateDepthOffset()
	{
		// FIXME: not implemented
	}

	/**
	 * Updates the cellEditor based on editability of the JTree that we're
	 * contained in. Ig the tree is editable but doesn't have a cellEditor, a
	 * basic one will be used.
	 */
	protected void updateCellEditor()
	{
		// FIXME: not implemented
	}

	/**
	 * Messaged from the tree we're in when the renderer has changed.
	 */
	protected void updateRenderer()
	{
		// FIXME: not implemented
	}

	/**
	 * Resets the treeState instance based on the tree we're providing the look
	 * and feel for.
	 */
	protected void configureLayoutCache()
	{
		treeState = createLayoutCache();
	}

	/**
	 * Marks the cached size as being invalid, and messages the tree with
	 * <code>treeDidChange</code>.
	 */
	protected void updateSize()
	{
		// FIXME: not implemented
	}

	/**
	 * Updates the <code>preferredSize</code> instance variable, which is
	 * returned from <code>getPreferredSize()</code>. 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.
	 */
	protected void updateCachedPreferredSize()
	{
		// FIXME: not implemented
	}

	/**
	 * Messaged from the VisibleTreeNode after it has been expanded.
	 * 
	 * @param path is the path that has been expanded.
	 */
	protected void pathWasExpanded(TreePath path)
	{
		// FIXME: not implemented
	}

	/**
	 * Messaged from the VisibleTreeNode after it has collapsed
	 */
	protected void pathWasCollapsed(TreePath path)
	{
		// FIXME: not implemented
	}

	/**
	 * Install all defaults for the tree.
	 * 
	 * @param tree is the JTree to install defaults for
	 */
	protected void installDefaults(JTree tree)
	{
		UIDefaults defaults = UIManager.getLookAndFeelDefaults();

		tree.setFont(defaults.getFont("Tree.font"));
		tree.setForeground(defaults.getColor("Tree.foreground"));
		tree.setBackground(defaults.getColor("Tree.background"));
		tree.setOpaque(true);

		rightChildIndent = defaults.getInt("Tree.rightChildIndent");
		leftChildIndent = defaults.getInt("Tree.leftChildIndent");
		setRowHeight(defaults.getInt("Tree.rowHeight"));
	}

	/**
	 * Install all keyboard actions for this
	 */
	protected void installKeyboardActions()
	{
	}

	/**
	 * Install all listeners for this
	 */
	protected void installListeners()
	{
		tree.addPropertyChangeListener(propertyChangeListener);
		tree.addFocusListener(focusListener);
		tree.addTreeSelectionListener(treeSelectionListener);
		tree.addMouseListener(mouseInputListener);
		tree.addKeyListener(keyListener);
		tree.addPropertyChangeListener(selectionModelPropertyChangeListener);
		tree.addComponentListener(componentListener);
		cellEditor.addCellEditorListener(cellEditorListener);
		tree.addTreeExpansionListener(treeExpansionListener);
		treeModel.addTreeModelListener(treeModelListener);
	}

	/**
	 * Install the UI for the component
	 * 
	 * @param c the component to install UI for
	 */
	public void installUI(JComponent c)
	{
		super.installUI(c);
		installDefaults((JTree) c);
		tree = (JTree) c;
		setModel(tree.getModel());
		treeSelectionModel = tree.getSelectionModel();
		installListeners();
		installKeyboardActions();
		completeUIInstall();
	}

	/**
	 * Uninstall the defaults for the tree
	 * 
	 * @param tree to uninstall defaults for
	 */
	protected void uninstallDefaults(JTree tree)
	{
		UIDefaults defaults = UIManager.getLookAndFeelDefaults();
		tree.setFont(null);
		tree.setForeground(null);
		tree.setBackground(null);
		tree.setCellRenderer(null);
	}

	/**
	 * Uninstall the UI for the component
	 * 
	 * @param c the component to uninstall UI for
	 */
	public void uninstallUI(JComponent c)
	{
		uninstallDefaults((JTree) c);
		uninstallKeyboardActions();
		uninstallListeners();
		tree = null;
		completeUIUninstall();
	}

	/**
	 *     Paints the specified component appropriate for the look and feel.
	 *     This method is invoked from the ComponentUI.update method when the 
	 *     specified component is being painted. Subclasses should override 
	 *     this method and use the specified Graphics object to render the 
	 *     content of the component.
	 *     
	 *     @param g the Graphics context in which to paint
	 *     @param c the component being painted; this argument is often 
	 *     ignored, but might be used if the UI object is stateless and 
	 *     shared by multiple components
	 */
	public void paint(Graphics g, JComponent c)
	{
		JTree tree = (JTree) c;
		TreeModel mod = tree.getModel();
		g.translate(10, 10);
		paintRecursive(g, 0, 0, 0, 0, tree, mod, mod.getRoot());
		g.translate(-10, -10);
	}

	/**
	 * Ensures that the rows identified by beginRow through endRow are visible.
	 * 
	 * @param beginRow is the first row
	 * @param endRow is the last row
	 */
	protected void ensureRowsAreVisible(int beginRow, int endRow)
	{
		// FIXME: not implemented
	}

	/**
	 * Sets the preferred minimum size.
	 * 
	 * @param newSize is the new preferred minimum size.
	 */
	public void setPreferredMinSize(Dimension newSize)
	{
		// FIXME: not implemented
	}

	/**
	 * Gets the preferred minimum size.
	 * 
	 * @returns the preferred minimum size.
	 */
	public Dimension getPreferredMinSize()
	{
		// FIXME: not implemented
		return null;
	}

	/**
	 * Returns the preferred size to properly display the tree, this is a cover
	 * method for getPreferredSize(c, false).
	 * 
	 * @param c the component whose preferred size is being queried; this
	 *        argument is often ignored but might be used if the UI object is
	 *        stateless and shared by multiple components
	 * @return the preferred size
	 */
	public Dimension getPreferredSize(JComponent c)
	{
		return getPreferredSize(c, false);
	}

	/**
	 * Returns the preferred size to represent the tree in c. If
	 * checkConsistancy is true, checkConsistancy is messaged first.
	 * 
	 * @param c the component whose preferred size is being queried.
	 * @param checkConsistancy if true must check consistancy
	 * @return the preferred size
	 */
	public Dimension getPreferredSize(JComponent c, boolean checkConsistancy)
	{
		// FIXME: not implemented
		return new Dimension(200, 900);
	}

	/**
	 * Returns the minimum size for this component. Which will be the min
	 * preferred size or (0,0).
	 * 
	 * @param c the component whose min size is being queried.
	 * @returns the preferred size or null
	 */
	public Dimension getMinimumSize(JComponent c)
	{
		// FIXME: not implemented
		return new Dimension(200, 900);
	}

	/**
	 * Returns the maximum size for the component, which will be the preferred
	 * size if the instance is currently in JTree or (0,0).
	 * 
	 * @param c the component whose preferred size is being queried
	 * @return the max size or null
	 */
	public Dimension getMaximumSize(JComponent c)
	{
		// FIXME: not implemented
		return new Dimension(200, 900);
	}

	/**
	 * Messages to stop the editing session. If the UI the receiver is providing
	 * the look and feel for returns true from
	 * <code>getInvokesStopCellEditing</code>, stopCellEditing will be
	 * invoked on the current editor. Then completeEditing will be messaged with
	 * false, true, false to cancel any lingering editing.
	 */
	protected void completeEditing()
	{
		// FIXME: not implemented
	}

	/**
	 * 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.
	 * 
	 * @param messageStop message to stop editing
	 * @param messageCancel message to cancel editing
	 * @param messageTree message to treeModel
	 */
	protected void completeEditing(boolean messageStop, boolean messageCancel,
			boolean messageTree)
	{
		// FIXME: not implemented
	}

	/**
	 * Will start editing for node if there is a cellEditor and shouldSelectCall
	 * returns true. This assumes that path is valid and visible.
	 * 
	 * @param path is the path to start editing
	 * @param event is the MouseEvent performed on the path
	 * @return true if successful
	 */
	protected boolean startEditing(TreePath path, MouseEvent event)
	{
		// FIXME: not implemented
		return false;
	}

	/**
	 * If the <code>mouseX</code> and <code>mouseY</code> are in the expand
	 * or collapse region of the row, this will toggle the row.
	 * 
	 * @param path the path we are concerned with
	 * @param mouseX is the cursor's x position
	 * @param mouseY is the cursor's y position
	 */
	protected void checkForClickInExpandControl(TreePath path, int mouseX,
			int mouseY)
	{
		// FIXME: not implemented
	}

	/**
	 * Returns true if the <code>mouseX</code> and <code>mouseY</code> fall
	 * in the area of row that is used to expand/collpse the node and the node
	 * at row does not represent a leaf.
	 * 
	 * @param path the path we are concerned with
	 * @param mouseX is the cursor's x position
	 * @param mouseY is the cursor's y position
	 * @return true if the <code>mouseX</code> and <code>mouseY</code> fall
	 *         in the area of row that is used to expand/collpse the node and
	 *         the node at row does not represent a leaf.
	 */
	protected boolean isLocationInExpandControl(TreePath path, int mouseX,
			int mouseY)
	{
		// FIXME: not implemented
		return false;
	}

	/**
	 * Messaged when the user clicks the particular row, this invokes
	 * toggleExpandState.
	 * 
	 * @param path the path we are concerned with
	 * @param mouseX is the cursor's x position
	 * @param mouseY is the cursor's y position
	 */
	protected void handleExpandControlClick(TreePath path, int mouseX,
			int mouseY)
	{
		// FIXME: not implemented
	}

	/**
	 * Expands path if it is not expanded, or collapses row if it is expanded.
	 * If expanding a path and JTree scroll 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).
	 * 
	 * @param path the path we are concerned with
	 */
	protected void toggleExpandState(TreePath path)
	{
		// FIXME: not implemented
	}

	/**
	 * Returning true signifies a mouse event on the node should toggle the
	 * selection of only the row under the mouse.
	 * 
	 * @param event is the MouseEvent performed on the row.
	 * @return true signifies a mouse event on the node should toggle the
	 *         selection of only the row under the mouse.
	 */
	protected boolean isToggleSelectionEvent(MouseEvent event)
	{
		// FIXME: not implemented
		return false;
	}

	/**
	 * Returning true signifies a mouse event on the node should select from the
	 * anchor point.
	 * 
	 * @param event is the MouseEvent performed on the node.
	 * @return true signifies a mouse event on the node should select from the
	 *         anchor point.
	 */
	protected boolean isMultiSelectEvent(MouseEvent event)
	{
		// FIXME: not implemented
		return false;
	}

	/**
	 * 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.
	 * 
	 * @param event is the MouseEvent performed on the row.
	 * @return true indicates the row under the mouse should be toggled based on
	 *         the event.
	 */
	protected boolean isToggleEvent(MouseEvent event)
	{
		// FIXME: not implemented
		return false;
	}

	/**
	 * Messaged to update the selection based on a MouseEvent over a particular
	 * row. If the even 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 even specified a toggle event the row is
	 * expanded/collapsed.
	 * 
	 * @param path is the path selected for an event
	 * @param event is the MouseEvent performed on the path.
	 */
	protected void selectPathForEvent(TreePath path, MouseEvent event)
	{
		// FIXME: not implemented
	}

	/**
	 * Returns true if the node at <code>row</code> is a leaf.
	 * 
	 * @param row is the row we are concerned with.
	 * @return true if the node at <code>row</code> is a leaf.
	 */
	protected boolean isLeaf(int row)
	{
		return false;
		// FIXME: not implemented
	}

	/* * INTERNAL CLASSES * */

	/**
	 * Updates the preferred size when scrolling, if necessary.
	 */
	public class ComponentHandler
			extends ComponentAdapter
			implements ActionListener
	{
		/**
		 * Timer used when inside a scrollpane and the scrollbar is adjusting
		 */
		protected Timer timer;

		/** ScrollBar that is being adjusted */
		protected JScrollBar scrollBar;

		/**
		 * Constructor
		 */
		public ComponentHandler()
		{
		}

		/**
		 * Invoked when the component's position changes.
		 * 
		 * @param e the event that occurs when moving the component
		 */
		public void componentMoved(ComponentEvent e)
		{
		}

		/**
		 * Creats, if necessary, and starts a Timer to check if needed to resize
		 * the bounds
		 */
		protected void startTimer()
		{
		}

		/**
		 * Returns the JScrollPane housing the JTree, or null if one isn't
		 * found.
		 * 
		 * @return JScrollPane housing the JTree, or null if one isn't found.
		 */
		protected JScrollPane getScrollPane()
		{
			return null;
		}

		/**
		 * Public as a result of Timer. If the scrollBar is null, or not
		 * adjusting, this stops the timer and updates the sizing.
		 * 
		 * @param ae is the action performed
		 */
		public void actionPerformed(ActionEvent ae)
		{
		}
	}// ComponentHandler

	/**
	 * Listener responsible for getting cell editing events and updating the
	 * tree accordingly.
	 */
	public class CellEditorHandler
			implements CellEditorListener
	{
		/**
		 * Constructor
		 */
		public CellEditorHandler()
		{
		}

		/**
		 * Messaged when editing has stopped in the tree. Tells the listeners
		 * editing has stopped.
		 * 
		 * @param e is the notification event
		 */
		public void editingStopped(ChangeEvent e)
		{
		}

		/**
		 * Messaged when editing has been canceled in the tree. This tells the
		 * listeners the editor has canceled editing.
		 * 
		 * @param e is the notification event
		 */
		public void editingCanceled(ChangeEvent e)
		{
		}
	}// CellEditorHandler

	/**
	 * Repaints the lead selection row when focus is lost/grained.
	 */
	public class FocusHandler
			implements FocusListener
	{
		/**
		 * Constructor
		 */
		public FocusHandler()
		{
		}

		/**
		 * Invoked when focus is activated on the tree we're in, redraws the
		 * lead row. Invoked when a component gains the keyboard focus.
		 * 
		 * @param e is the focus event that is activated
		 */
		public void focusGained(FocusEvent e)
		{
		}

		/**
		 * Invoked when focus is deactivated on the tree we're in, redraws the
		 * lead row. Invoked when a component loses the keyboard focus.
		 * 
		 * @param e is the focus event that is deactivated
		 */
		public void focusLost(FocusEvent e)
		{
		}
	}// FocusHandler

	/**
	 * This is used to get multiple key down events to appropriately genereate
	 * events.
	 */
	public class KeyHandler
			extends KeyAdapter
	{
		/** Key code that is being generated for. */
		protected Action repeatKeyAction;

		/** Set to true while keyPressed is active */
		protected boolean isKeyDown;

		/**
		 * Constructor
		 */
		public KeyHandler()
		{
		}

		/**
		 * Invoked when a key has been typed. Moves the keyboard focus to the
		 * first element whose first letter matches the alphanumeric key pressed
		 * by the user. Subsequent same key presses move the keyboard focus to
		 * the next object that starts with the same letter.
		 * 
		 * @param e the key typed
		 */
		public void keyTyped(KeyEvent e)
		{
		}

		/**
		 * Invoked when a key has been pressed.
		 * 
		 * @param e the key pressed
		 */
		public void keyPressed(KeyEvent e)
		{
		}

		/**
		 * Invoked when a key has been released
		 * 
		 * @param e the key released
		 */
		public void keyReleased(KeyEvent e)
		{
		}
	}// KeyHandler

	/**
	 * MouseListener is responsible for updating the selevtion based on mouse
	 * events.
	 */
	public class MouseHandler
			extends MouseAdapter
			implements MouseMotionListener
	{
		/**
		 * Constructor
		 */
		public MouseHandler()
		{
		}

		/**
		 * Invoked when a mouse button has been pressed on a component.
		 * 
		 * @param e is the mouse event that occured
		 */
		public void mousePressed(MouseEvent e)
		{
		}

		/**
		 * Invoked when a mouse button is pressed on a component and then
		 * dragged. MOUSE_DRAGGED events will continue to be delivered to the
		 * component where the drag originated until the mouse button is
		 * released (regardless of whether the mouse position is within the
		 * bounds of the component).
		 * 
		 * @param e is the mouse event that occured
		 */
		public void mouseDragged(MouseEvent e)
		{
		}

		/**
		 * Invoked when the mouse button has been moved on a component (with no
		 * buttons no down).
		 * 
		 * @param e the mouse event that occured
		 */
		public void mouseMoved(MouseEvent e)
		{
		}

		/**
		 * Invoked when a mouse button has been released on a component.
		 * 
		 * @param e is the mouse event that occured
		 */
		public void mouseReleased(MouseEvent e)
		{
		}
	}// MouseHandler

	/**
	 * MouseInputHandler handles passing all mouse events, including mouse
	 * motion events, until the mouse is released to the destination it is
	 * constructed with.
	 */
	public class MouseInputHandler
			implements MouseInputListener
	{
		/** Source that events are coming from */
		protected Component source;

		/** Destination that receives all events. */
		protected Component destination;

		/**
		 * Constructor
		 * 
		 * @param source that events are coming from
		 * @param destination that receives all events
		 * @param event is the event received
		 */
		public MouseInputHandler(Component source, Component destination,
				MouseEvent e)
		{
		}

		/**
		 * Invoked when the mouse button has been clicked (pressed and released)
		 * on a component.
		 * 
		 * @param e mouse event that occured
		 */
		public void mouseClicked(MouseEvent e)
		{
			Point click = e.getPoint();
			int row = ((int) click.getY() / getRowHeight()) - 1;

			if (BasicTreeUI.this.tree.isRowSelected(row))
				BasicTreeUI.this.tree.removeSelectionRow(row);
			else if (BasicTreeUI.this.tree.getSelectionModel()
					.getSelectionMode() == treeSelectionModel.SINGLE_TREE_SELECTION)
				BasicTreeUI.this.tree.addSelectionRow(row);
			// FIXME: add in selection for more than 1 row, or an entire
			// path
		}

		/**
		 * Invoked when a mouse button has been pressed on a component.
		 * 
		 * @param e mouse event that occured
		 */
		public void mousePressed(MouseEvent e)
		{
		}

		/**
		 * Invoked when a mouse button has been released on a component.
		 * 
		 * @param e mouse event that occured
		 */
		public void mouseReleased(MouseEvent e)
		{
		}

		/**
		 * Invoked when the mouse enters a component.
		 * 
		 * @param e mouse event that occured
		 */
		public void mouseEntered(MouseEvent e)
		{
		}

		/**
		 * Invoked when the mouse exits a component.
		 * 
		 * @param e mouse event that occured
		 */
		public void mouseExited(MouseEvent e)
		{
		}

		/**
		 * Invoked when a mouse button is pressed on a component and then
		 * dragged. MOUSE_DRAGGED events will continue to be delivered to the
		 * component where the drag originated until the mouse button is
		 * released (regardless of whether the mouse position is within the
		 * bounds of the component).
		 * 
		 * @param e mouse event that occured
		 */
		public void mouseDragged(MouseEvent e)
		{
		}

		/**
		 * Invoked when the mouse cursor has been moved onto a component but no
		 * buttons have been pushed.
		 * 
		 * @param e mouse event that occured
		 */
		public void mouseMoved(MouseEvent e)
		{
		}

		/**
		 * Removes event from the source
		 */
		protected void removeFromSource()
		{
		}
	}// MouseInputHandler

	/**
	 * Class responsible for getting size of node, method is forwarded to
	 * BasicTreeUI method. X location does not include insets, that is handled
	 * in getPathBounds.
	 */
	public class NodeDimensionsHandler
			extends AbstractLayoutCache.NodeDimensions
	{
		/**
		 * Constructor
		 */
		public NodeDimensionsHandler()
		{
		}

		/**
		 * Responsible for getting the size of a particular node.
		 * 
		 * @param value the value to be represented
		 * @param row row being queried
		 * @param depth the depth of the row
		 * @param expanded true if row is expanded
		 * @param size a Rectangle containing the size needed to represent value
		 * @return containing the node dimensions, or null if node has no
		 *         dimension
		 */
		public Rectangle getNodeDimensions(Object value, int row, int depth,
				boolean expanded, Rectangle size)
		{
			return null;
		}

		/**
		 * Returns the amount to indent the given row
		 * 
		 * @return amount to indent the given row.
		 */
		protected int getRowX(int row, int depth)
		{
			return 0;
		}
	}// NodeDimensionsHandler

	/**
	 * PropertyChangeListener for the tree. Updates the appropriate varaible, or
	 * TreeState, based on what changes.
	 */
	public class PropertyChangeHandler
			implements PropertyChangeListener
	{

		/**
		 * Constructor
		 */
		public PropertyChangeHandler()
		{
		}

		/**
		 * This method gets called when a bound property is changed.
		 * 
		 * @param event A PropertyChangeEvent object describing the event source
		 *        and the property that has changed.
		 */
		public void propertyChange(PropertyChangeEvent event)
		{
		}
	}// PropertyChangeHandler

	/**
	 * Listener on the TreeSelectionModel, resets the row selection if any of
	 * the properties of the model change.
	 */
	public class SelectionModelPropertyChangeHandler
			implements PropertyChangeListener
	{

		/**
		 * Constructor
		 */
		public SelectionModelPropertyChangeHandler()
		{
		}

		/**
		 * This method gets called when a bound property is changed.
		 * 
		 * @param event A PropertyChangeEvent object describing the event source
		 *        and the property that has changed.
		 */
		public void propertyChange(PropertyChangeEvent event)
		{
		}
	}// SelectionModelPropertyChangeHandler

	/**
	 * ActionListener that invokes cancelEditing when action performed.
	 */
	public class TreeCancelEditingAction
			extends AbstractAction
	{

		/**
		 * Constructor
		 */
		public TreeCancelEditingAction()
		{
		}

		/**
		 * Invoked when an action occurs.
		 * 
		 * @param e event that occured
		 */
		public void actionPerformed(ActionEvent e)
		{
		}

		/**
		 * Returns true if the action is enabled.
		 * 
		 * @return true if the action is enabled, false otherwise
		 */
		public boolean isEnabled()
		{
			return false;
		}
	}// TreeCancelEditingAction

	/**
	 * Updates the TreeState in response to nodes expanding/collapsing.
	 */
	public class TreeExpansionHandler
			implements TreeExpansionListener
	{

		/**
		 * Constructor
		 */
		public TreeExpansionHandler()
		{
		}

		/**
		 * Called whenever an item in the tree has been expanded.
		 * 
		 * @param event is the event that occured
		 */
		public void treeExpanded(TreeExpansionEvent event)
		{
		}

		/**
		 * Called whenever an item in the tree has been collapsed.
		 * 
		 * @param event is the event that occured
		 */
		public void treeCollapsed(TreeExpansionEvent event)
		{
		}
	}// TreeExpansionHandler

	/**
	 * TreeHomeAction is used to handle end/home actions. Scrolls either the
	 * first or last cell to be visible based on direction.
	 */
	public class TreeHomeAction
			extends AbstractAction
	{

		/** direction is either home or end */
		protected int direction;

		/**
		 * Constructor
		 * 
		 * @param direction - it is home or end
		 * @param name is the name of the direction
		 */
		public TreeHomeAction(int direction, String name)
		{
		}

		/**
		 * Invoked when an action occurs.
		 * 
		 * @param e is the event that occured
		 */
		public void actionPerformed(ActionEvent e)
		{
		}

		/**
		 * Returns true if the action is enabled.
		 * 
		 * @return true if the action is enabled.
		 */
		public boolean isEnabled()
		{
			return false;
		}
	}// TreeHomeAction

	/**
	 * TreeIncrementAction is used to handle up/down actions. Selection is moved
	 * up or down based on direction.
	 */
	public class TreeIncrementAction
			extends AbstractAction
	{

		/** Specifies the direction to adjust the selection by. */
		protected int direction;

		/**
		 * Constructor
		 * 
		 * @param direction up or down
		 * @param name is the name of the direction
		 */
		public TreeIncrementAction(int direction, String name)
		{
		}

		/**
		 * Invoked when an action occurs.
		 * 
		 * @param e is the event that occured
		 */
		public void actionPerformed(ActionEvent e)
		{
		}

		/**
		 * Returns true if the action is enabled.
		 * 
		 * @return true if the action is enabled.
		 */
		public boolean isEnabled()
		{
			return false;
		}
	}// TreeIncrementAction

	/**
	 * Forwards all TreeModel events to the TreeState.
	 */
	public class TreeModelHandler
			implements TreeModelListener
	{
		/**
		 * Constructor
		 */
		public TreeModelHandler()
		{
		}

		/**
		 * Invoked after a node (or a set of siblings) has changed in some way.
		 * The node(s) have not changed locations in the tree or altered their
		 * children arrays, but other attributes have changed and may affect
		 * presentation. Example: the name of a file has changed, but it is in
		 * the same location in the file system. To indicate the root has
		 * changed, childIndices and children will be null. Use e.getPath() to
		 * get the parent of the changed node(s). e.getChildIndices() returns
		 * the index(es) of the changed node(s).
		 * 
		 * @param e is the event that occured
		 */
		public void treeNodesChanged(TreeModelEvent e)
		{
		}

		/**
		 * Invoked after nodes have been inserted into the tree. Use e.getPath()
		 * to get the parent of the new node(s). e.getChildIndices() returns the
		 * index(es) of the new node(s) in ascending order.
		 * 
		 * @param e is the event that occured
		 */
		public void treeNodesInserted(TreeModelEvent e)
		{
		}

		/**
		 * Invoked after nodes have been removed from the tree. Note that if a
		 * subtree is removed from the tree, this method may only be invoked
		 * once for the root of the removed subtree, not once for each
		 * individual set of siblings removed. Use e.getPath() to get the former
		 * parent of the deleted node(s). e.getChildIndices() returns, in
		 * ascending order, the index(es) the node(s) had before being deleted.
		 * 
		 * @param e is the event that occured
		 */
		public void treeNodesRemoved(TreeModelEvent e)
		{
		}

		/**
		 * Invoked after the tree has drastically changed structure from a given
		 * node down. If the path returned by e.getPath() is of length one and
		 * the first element does not identify the current root node the first
		 * element should become the new root of the tree. Use e.getPath() to
		 * get the path to the node. e.getChildIndices() returns null.
		 * 
		 * @param e is the event that occured
		 */
		public void treeStructureChanged(TreeModelEvent e)
		{
		}
	}// TreeModelHandler

	/**
	 * TreePageAction handles page up and page down events.
	 */
	public class TreePageAction
			extends AbstractAction
	{
		/** Specifies the direction to adjust the selection by. */
		protected int direction;

		/**
		 * Constructor
		 * 
		 * @param direction up or down
		 * @param name is the name of the direction
		 */
		public TreePageAction(int direction, String name)
		{
		}

		/**
		 * Invoked when an action occurs.
		 * 
		 * @param e is the event that occured
		 */
		public void actionPerformed(ActionEvent e)
		{
		}

		/**
		 * Returns true if the action is enabled.
		 * 
		 * @return true if the action is enabled.
		 */
		public boolean isEnabled()
		{
			return false;
		}
	}// TreePageAction

	/**
	 * Listens for changes in the selection model and updates the display
	 * accordingly.
	 */
	public class TreeSelectionHandler
			implements TreeSelectionListener
	{
		/**
		 * Constructor
		 */
		public TreeSelectionHandler()
		{
		}

		/**
		 * Messaged when the selection changes in the tree we're displaying for.
		 * Stops editing, messages super and displays the changed paths.
		 * 
		 * @param event the event that characterizes the change.
		 */
		public void valueChanged(TreeSelectionEvent event)
		{
		}
	}// TreeSelectionHandler

	/**
	 * For the first selected row expandedness will be toggled.
	 */
	public class TreeToggleAction
			extends AbstractAction
	{
		/**
		 * Constructor
		 * 
		 * @param name is the name of <code>Action</code> field
		 */
		public TreeToggleAction(String name)
		{
		}

		/**
		 * Invoked when an action occurs.
		 * 
		 * @param e the event that occured
		 */
		public void actionPerformed(ActionEvent e)
		{
		}

		/**
		 * Returns true if the action is enabled.
		 * 
		 * @return true if the action is enabled, false otherwise
		 */
		public boolean isEnabled()
		{
			return false;
		}
	} // TreeToggleAction

	/**
	 * TreeTraverseAction is the action used for left/right keys. Will toggle
	 * the expandedness of a node, as well as potentially incrementing the
	 * selection.
	 */
	public class TreeTraverseAction
			extends AbstractAction
	{
		/**
		 * Determines direction to traverse, 1 means expand, -1 means collapse.
		 */
		protected int direction;

		/**
		 * Constructor
		 * 
		 * @param direction to traverse
		 * @param name is the name of the direction
		 */
		public TreeTraverseAction(int direction, String name)
		{
		}

		/**
		 * Invoked when an action occurs.
		 * 
		 * @param e the event that occured
		 */
		public void actionPerformed(ActionEvent e)
		{
		}

		/**
		 * Returns true if the action is enabled.
		 * 
		 * @return true if the action is enabled, false otherwise
		 */
		public boolean isEnabled()
		{
			return false;
		}
	} // TreeTraverseAction

	/* * HELPER METHODS FOR PAINTING * */

	/**
	 * Paints a leaf in the tree
	 * 
	 * @param g the Graphics context in which to paint
	 * @param x the x location of the leaf
	 * @param y the y location of the leaf
	 * @param tree the tree to draw on
	 * @param leaf the object to draw
	 */
	private void paintLeaf(Graphics g, int x, int y, JTree tree, Object leaf)
	{
		TreePath tp = new TreePath(((DefaultMutableTreeNode) leaf).getPath());
		boolean selected = tree.isPathSelected(tp);

		Component c = tree.getCellRenderer().getTreeCellRendererComponent(tree,
				leaf, selected, false, true, 0, false);

		if (selected)
		{
			Component comp = tree.getCellRenderer()
					.getTreeCellRendererComponent(tree, leaf, true, false,
							true, 0, false);
			rendererPane.paintComponent(g, comp, tree, new Rectangle(x, y, 10,
					25));
		}

		g.translate(x, y);
		c.paint(g);
		g.translate(-x, -y);
	}

	/**
	 * Paints a non-leaf in the tree
	 * 
	 * @param g the Graphics context in which to paint
	 * @param x the x location of the non-leaf
	 * @param y the y location of the non-leaf
	 * @param tree the tree to draw on
	 * @param nonLeaf the object to draw
	 */
	private void paintNonLeaf(Graphics g, int x, int y, JTree tree,
			Object nonLeaf)
	{
		TreePath tp = new TreePath(((DefaultMutableTreeNode) nonLeaf).getPath());
		boolean selected = tree.isPathSelected(tp);

		Component c = tree.getCellRenderer().getTreeCellRendererComponent(tree,
				nonLeaf, selected, false, false, 0, false);

		if (selected)
		{
			Component comp = tree.getCellRenderer()
					.getTreeCellRendererComponent(tree, nonLeaf, true, false,
							true, 0, false);
			rendererPane.paintComponent(g, comp, tree, new Rectangle(x, y, 10,
					25));
		}
		g.translate(x, y);
		c.paint(g);
		g.translate(-x, -y);
	}

	/**
	 * Recursively paints all elements of the tree
	 * 
	 * @param g the Graphics context in which to paint
	 * @param indentation of the current object
	 * @param descent is the number of elements drawn
	 * @param childNumber is the index of the current child in the tree
	 * @param depth is the depth of the current object in the tree
	 * @param tree is the tree to draw to
	 * @param mod is the TreeModel we are using to draw
	 * @param curr is the current object to draw
	 * 
	 * @return int - current descent of the tree
	 */
	private int paintRecursive(Graphics g, int indentation, int descent,
			int childNumber, int depth, JTree tree, TreeModel mod, Object curr)
	{
		Rectangle clip = g.getClipBounds();
		if (indentation > clip.x + clip.width + rightChildIndent
				|| descent > clip.y + clip.height + getRowHeight())
			return descent;

		int halfHeight = getRowHeight() / 2;
		int halfWidth = rightChildIndent / 2;
		int y0 = descent + halfHeight;

		if (mod.isLeaf(curr))
		{
			paintLeaf(g, indentation, descent, tree, curr);
			descent += getRowHeight();
		} else
		{
			if (depth > 0 || tree.isRootVisible())
			{
				paintNonLeaf(g, indentation, descent, tree, curr);
				descent += getRowHeight();
				y0 += halfHeight;
			}
			int max = mod.getChildCount(curr);
			for (int i = 0; i < max; ++i)
			{
				g.setColor(getHashColor());
				g.drawLine(indentation + halfWidth, descent + halfHeight,
						indentation + rightChildIndent, descent + halfHeight);
				descent = paintRecursive(g, indentation + rightChildIndent,
						descent, i, depth + 1, tree, mod, mod.getChild(curr, i));
			}
		}

		int y1 = descent - halfHeight;
		if (y0 != y1)
		{
			g.setColor(getHashColor());
			g
					.drawLine(indentation + halfWidth, y0, indentation
							+ halfWidth, y1);
		}

		return descent;
	}

} // BasicTreeUI
