/*
* 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 JTree
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 JComponent
for which we need a UI delegate
* for.
* @return the ComponentUI
for c.
*/
public static ComponentUI createUI(JComponent c)
{
return new BasicTreeUI();
}
/**
* Returns the Hash color.
*
* @return the Color
of the Hash.
*/
protected Color getHashColor()
{
return UIManager.getLookAndFeelDefaults().getColor("Tree.hash");
}
/**
* Sets the Hash color.
*
* @param the Color
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 tcr
. This invokes
* updateRenderer
.
*
* @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 TreeCellEditor
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
* path
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 parent
*
* @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
* treeDidChange
.
*/
protected void updateSize()
{
// FIXME: not implemented
}
/**
* 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.
*/
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
* getInvokesStopCellEditing
, 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 mouseX
and mouseY
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 mouseX
and mouseY
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 mouseX
and mouseY
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 row
is a leaf.
*
* @param row is the row we are concerned with.
* @return true if the node at row
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 Action
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