2005-07-16 00:30:23 +00:00
|
|
|
/* BasicTreeUI.java --
|
2005-09-23 21:31:04 +00:00
|
|
|
Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
02110-1301 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. */
|
2005-07-16 00:30:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
package javax.swing.plaf.basic;
|
|
|
|
|
|
|
|
import java.awt.Color;
|
|
|
|
import java.awt.Component;
|
|
|
|
import java.awt.Dimension;
|
|
|
|
import java.awt.Font;
|
|
|
|
import java.awt.FontMetrics;
|
|
|
|
import java.awt.Graphics;
|
2005-11-15 23:20:01 +00:00
|
|
|
import java.awt.Insets;
|
2005-07-16 00:30:23 +00:00
|
|
|
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;
|
2005-11-15 23:20:01 +00:00
|
|
|
import java.util.Enumeration;
|
2005-09-23 21:31:04 +00:00
|
|
|
import java.util.Hashtable;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
|
|
|
import javax.swing.AbstractAction;
|
|
|
|
import javax.swing.Action;
|
2005-09-23 21:31:04 +00:00
|
|
|
import javax.swing.ActionMap;
|
2005-07-16 00:30:23 +00:00
|
|
|
import javax.swing.CellRendererPane;
|
|
|
|
import javax.swing.Icon;
|
2005-09-23 21:31:04 +00:00
|
|
|
import javax.swing.InputMap;
|
2005-07-16 00:30:23 +00:00
|
|
|
import javax.swing.JComponent;
|
|
|
|
import javax.swing.JScrollBar;
|
|
|
|
import javax.swing.JScrollPane;
|
2005-09-23 21:31:04 +00:00
|
|
|
import javax.swing.JTextField;
|
2005-07-16 00:30:23 +00:00
|
|
|
import javax.swing.JTree;
|
2005-09-23 21:31:04 +00:00
|
|
|
import javax.swing.KeyStroke;
|
2005-11-15 23:20:01 +00:00
|
|
|
import javax.swing.LookAndFeel;
|
2005-07-16 00:30:23 +00:00
|
|
|
import javax.swing.SwingUtilities;
|
|
|
|
import javax.swing.Timer;
|
|
|
|
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;
|
2005-11-15 23:20:01 +00:00
|
|
|
import javax.swing.plaf.ActionMapUIResource;
|
2005-07-16 00:30:23 +00:00
|
|
|
import javax.swing.plaf.ComponentUI;
|
2005-09-23 21:31:04 +00:00
|
|
|
import javax.swing.plaf.InputMapUIResource;
|
2005-07-16 00:30:23 +00:00
|
|
|
import javax.swing.plaf.TreeUI;
|
2005-09-23 21:31:04 +00:00
|
|
|
import javax.swing.text.Caret;
|
2005-07-16 00:30:23 +00:00
|
|
|
import javax.swing.tree.AbstractLayoutCache;
|
|
|
|
import javax.swing.tree.DefaultTreeCellEditor;
|
|
|
|
import javax.swing.tree.DefaultTreeCellRenderer;
|
2005-09-23 21:31:04 +00:00
|
|
|
import javax.swing.tree.FixedHeightLayoutCache;
|
2005-07-16 00:30:23 +00:00
|
|
|
import javax.swing.tree.TreeCellEditor;
|
|
|
|
import javax.swing.tree.TreeCellRenderer;
|
|
|
|
import javax.swing.tree.TreeModel;
|
|
|
|
import javax.swing.tree.TreeNode;
|
|
|
|
import javax.swing.tree.TreePath;
|
2005-09-23 21:31:04 +00:00
|
|
|
import javax.swing.tree.TreeSelectionModel;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A delegate providing the user interface for <code>JTree</code> according to
|
|
|
|
* the Basic look and feel.
|
|
|
|
*
|
|
|
|
* @see javax.swing.JTree
|
2005-11-15 23:20:01 +00:00
|
|
|
*
|
2005-07-16 00:30:23 +00:00
|
|
|
* @author Lillian Angel (langel@redhat.com)
|
2005-11-15 23:20:01 +00:00
|
|
|
* @author Sascha Brawer (brawer@dandelis.ch)
|
2005-07-16 00:30:23 +00:00
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
public class BasicTreeUI extends TreeUI
|
2005-07-16 00:30:23 +00:00
|
|
|
{
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Collapse Icon for the tree. */
|
|
|
|
protected transient Icon collapsedIcon;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Expanded Icon for the tree. */
|
|
|
|
protected transient Icon expandedIcon;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Distance between left margin and where vertical dashes will be drawn. */
|
|
|
|
protected int leftChildIndent;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/**
|
|
|
|
* Distance between leftChildIndent and where cell contents will be drawn.
|
|
|
|
*/
|
|
|
|
protected int rightChildIndent;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/**
|
|
|
|
* Total fistance that will be indented. The sum of leftChildIndent and
|
|
|
|
* rightChildIndent .
|
|
|
|
*/
|
|
|
|
protected int totalChildIndent;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Index of the row that was last selected. */
|
|
|
|
protected int lastSelectedRow;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Component that we're going to be drawing onto. */
|
|
|
|
protected JTree tree;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Renderer that is being used to do the actual cell drawing. */
|
|
|
|
protected transient TreeCellRenderer currentCellRenderer;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/**
|
|
|
|
* Set to true if the renderer that is currently in the tree was created by
|
|
|
|
* this instance.
|
|
|
|
*/
|
|
|
|
protected boolean createdRenderer;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Editor for the tree. */
|
|
|
|
protected transient TreeCellEditor cellEditor;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/**
|
|
|
|
* Set to true if editor that is currently in the tree was created by this
|
|
|
|
* instance.
|
|
|
|
*/
|
|
|
|
protected boolean createdCellEditor;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/**
|
|
|
|
* Set to false when editing and shouldSelectCall() returns true meaning the
|
|
|
|
* node should be selected before editing, used in completeEditing.
|
|
|
|
*/
|
|
|
|
protected boolean stopEditingInCompleteEditing;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Used to paint the TreeCellRenderer. */
|
|
|
|
protected CellRendererPane rendererPane;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Size needed to completely display all the nodes. */
|
|
|
|
protected Dimension preferredSize;
|
2005-11-15 23:20:01 +00:00
|
|
|
|
|
|
|
/** Minimum size needed to completely display all the nodes. */
|
|
|
|
protected Dimension preferredMinSize;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Is the preferredSize valid? */
|
|
|
|
protected boolean validCachedPreferredSize;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Object responsible for handling sizing and expanded issues. */
|
|
|
|
protected AbstractLayoutCache treeState;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Used for minimizing the drawing of vertical lines. */
|
|
|
|
protected Hashtable drawingCache;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** Responsible for telling the TreeState the size needed for a node. */
|
|
|
|
protected AbstractLayoutCache.NodeDimensions nodeDimensions;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** 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;
|
2005-11-15 23:20:01 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/** The action listener for the editor's Timer. */
|
2005-11-15 23:20:01 +00:00
|
|
|
Timer editorTimer = new EditorUpdateTimer();
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/** The new value of the node after editing. */
|
2005-11-15 23:20:01 +00:00
|
|
|
Object newVal;
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/** The action bound to KeyStrokes. */
|
2005-11-15 23:20:01 +00:00
|
|
|
TreeAction action;
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/** Boolean to keep track of editing. */
|
2005-11-15 23:20:01 +00:00
|
|
|
boolean isEditing;
|
|
|
|
|
|
|
|
/** The current path of the visible nodes in the tree. */
|
|
|
|
TreePath currentVisiblePath;
|
|
|
|
|
|
|
|
/** The gap between the icon and text. */
|
|
|
|
int gap = 4;
|
2005-11-18 00:59:33 +00:00
|
|
|
|
|
|
|
/** Default row height, if none was set. */
|
|
|
|
int rowHeight = 20;
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/** Listeners */
|
|
|
|
private PropertyChangeListener propertyChangeListener;
|
|
|
|
private FocusListener focusListener;
|
|
|
|
private TreeSelectionListener treeSelectionListener;
|
2005-11-15 23:20:01 +00:00
|
|
|
private MouseListener mouseListener;
|
2005-09-23 21:31:04 +00:00
|
|
|
private KeyListener keyListener;
|
|
|
|
private PropertyChangeListener selectionModelPropertyChangeListener;
|
|
|
|
private ComponentListener componentListener;
|
2005-11-15 23:20:01 +00:00
|
|
|
CellEditorListener cellEditorListener;
|
2005-09-23 21:31:04 +00:00
|
|
|
private TreeExpansionListener treeExpansionListener;
|
|
|
|
private TreeModelListener treeModelListener;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new BasicTreeUI object.
|
|
|
|
*/
|
|
|
|
public BasicTreeUI()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
validCachedPreferredSize = false;
|
2005-09-23 21:31:04 +00:00
|
|
|
drawingCache = new Hashtable();
|
|
|
|
nodeDimensions = createNodeDimensions();
|
|
|
|
configureLayoutCache();
|
|
|
|
|
|
|
|
propertyChangeListener = createPropertyChangeListener();
|
|
|
|
focusListener = createFocusListener();
|
|
|
|
treeSelectionListener = createTreeSelectionListener();
|
2005-11-15 23:20:01 +00:00
|
|
|
mouseListener = createMouseListener();
|
2005-09-23 21:31:04 +00:00
|
|
|
keyListener = createKeyListener();
|
|
|
|
selectionModelPropertyChangeListener = createSelectionModelPropertyChangeListener();
|
|
|
|
componentListener = createComponentListener();
|
|
|
|
cellEditorListener = createCellEditorListener();
|
|
|
|
treeExpansionListener = createTreeExpansionListener();
|
|
|
|
treeModelListener = createTreeModelListener();
|
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
return UIManager.getColor("Tree.hash");
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the Hash color.
|
|
|
|
*
|
|
|
|
* @param color
|
|
|
|
* the <code>Color</code> to set the Hash to.
|
|
|
|
*/
|
|
|
|
protected void setHashColor(Color color)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
// FIXME: Putting something in the UIDefaults map is certainly wrong.
|
|
|
|
UIManager.put("Tree.hash", color);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
public int getLeftChildIndent()
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
if (rowHeight == 0)
|
|
|
|
rowHeight = this.rowHeight;
|
2005-09-23 21:31:04 +00:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
tree.setModel(model);
|
|
|
|
treeModel = tree.getModel();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
tree.setRootVisible(newValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the root is visible.
|
|
|
|
*
|
|
|
|
* @return true if the root is visible.
|
|
|
|
*/
|
|
|
|
protected boolean isRootVisible()
|
|
|
|
{
|
|
|
|
return tree.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;
|
|
|
|
createdCellEditor = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
Rectangle bounds = null;
|
|
|
|
int row = -1;
|
|
|
|
Object cell = null;
|
2005-09-23 21:31:04 +00:00
|
|
|
if (path != null)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
row = getRowForPath(tree, path);
|
|
|
|
cell = path.getLastPathComponent();
|
|
|
|
bounds = new Rectangle(0, row * getRowHeight(), 0, 0);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-18 00:59:33 +00:00
|
|
|
return nodeDimensions.getNodeDimensions(cell, row,
|
|
|
|
getLevel(cell),
|
|
|
|
tree.isExpanded(path),
|
|
|
|
bounds);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (treeModel != null && currentVisiblePath != null)
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
Object[] nodes = currentVisiblePath.getPath();
|
|
|
|
if (row < nodes.length)
|
|
|
|
return new TreePath(getPathToRoot(nodes[row], 0));
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
int row = 0;
|
|
|
|
Object dest = path.getLastPathComponent();
|
|
|
|
int rowCount = getRowCount(tree);
|
|
|
|
if (currentVisiblePath != null)
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
Object[] nodes = currentVisiblePath.getPath();
|
|
|
|
while (row < rowCount)
|
|
|
|
{
|
|
|
|
if (dest.equals(nodes[row]))
|
|
|
|
return row;
|
|
|
|
row++;
|
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
return -1;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (currentVisiblePath != null)
|
|
|
|
return currentVisiblePath.getPathCount();
|
|
|
|
return 0;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
int row = Math.round(y / getRowHeight());
|
|
|
|
TreePath path = getPathForRow(tree, row);
|
|
|
|
|
|
|
|
// no row is visible at this node
|
|
|
|
while (row > 0 && path == null)
|
|
|
|
{
|
|
|
|
--row;
|
|
|
|
path = getPathForRow(tree, row);
|
|
|
|
}
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
return isEditing;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
if (isEditing(tree))
|
|
|
|
completeEditing(true, false, false);
|
|
|
|
return !isEditing(tree);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Cancels the current editing session.
|
|
|
|
*
|
|
|
|
* @param tree
|
|
|
|
* is the tree to cancel the editing session on.
|
|
|
|
*/
|
|
|
|
public void cancelEditing(JTree tree)
|
|
|
|
{
|
|
|
|
if (isEditing(tree))
|
|
|
|
completeEditing(false, true, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
startEditing(path, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
return editingPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked after the tree instance variable has been set, but before any
|
|
|
|
* default/listeners have been installed.
|
|
|
|
*/
|
|
|
|
protected void prepareForUIInstall()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked from installUI after all the defaults/listeners have been
|
|
|
|
* installed.
|
|
|
|
*/
|
|
|
|
protected void completeUIInstall()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked from uninstallUI after all the defaults/listeners have been
|
|
|
|
* uninstalled.
|
|
|
|
*/
|
|
|
|
protected void completeUIUninstall()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Installs the subcomponents of the tree, which is the renderer pane.
|
|
|
|
*/
|
|
|
|
protected void installComponents()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
currentCellRenderer = createDefaultCellRenderer();
|
|
|
|
rendererPane = createCellRendererPane();
|
|
|
|
createdRenderer = true;
|
|
|
|
setCellRenderer(currentCellRenderer);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
return new NodeDimensionsHandler();
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
|
|
|
if (currentCellRenderer != null)
|
|
|
|
return new DefaultTreeCellEditor(tree,
|
|
|
|
(DefaultTreeCellRenderer) currentCellRenderer,
|
|
|
|
cellEditor);
|
|
|
|
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);
|
2005-11-15 23:20:01 +00:00
|
|
|
tree.removeMouseListener(mouseListener);
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.removeKeyListener(keyListener);
|
|
|
|
tree.removePropertyChangeListener(selectionModelPropertyChangeListener);
|
|
|
|
tree.removeComponentListener(componentListener);
|
|
|
|
tree.removeTreeExpansionListener(treeExpansionListener);
|
|
|
|
|
|
|
|
TreeCellEditor tce = tree.getCellEditor();
|
|
|
|
if (tce != null)
|
|
|
|
tce.removeCellEditorListener(cellEditorListener);
|
2005-11-15 23:20:01 +00:00
|
|
|
if (treeModel != null)
|
|
|
|
treeModel.removeTreeModelListener(treeModelListener);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Uninstall all keyboard actions.
|
|
|
|
*/
|
|
|
|
protected void uninstallKeyboardActions()
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
action = null;
|
|
|
|
tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).setParent(null);
|
|
|
|
tree.getActionMap().setParent(null);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Uninstall the rendererPane.
|
|
|
|
*/
|
|
|
|
protected void uninstallComponents()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
currentCellRenderer = null;
|
|
|
|
rendererPane = null;
|
|
|
|
createdRenderer = false;
|
|
|
|
setCellRenderer(currentCellRenderer);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
return getRowHeight() / 2;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
return rightChildIndent / 2;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make all the nodes that are expanded in JTree expanded in LayoutCache. This
|
2005-11-15 23:20:01 +00:00
|
|
|
* invokes updateExpandedDescendants with the root path.
|
2005-09-23 21:31:04 +00:00
|
|
|
*/
|
|
|
|
protected void updateLayoutCacheExpandedNodes()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (treeModel != null)
|
|
|
|
updateExpandedDescendants(new TreePath(treeModel.getRoot()));
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
Enumeration expanded = tree.getExpandedDescendants(path);
|
|
|
|
while (expanded.hasMoreElements())
|
|
|
|
treeState.setExpandedState(((TreePath) expanded.nextElement()), true);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
depthOffset += getVerticalLegBuffer();
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates the cellEditor based on editability of the JTree that we're
|
|
|
|
* contained in. If the tree is editable but doesn't have a cellEditor, a
|
|
|
|
* basic one will be used.
|
|
|
|
*/
|
|
|
|
protected void updateCellEditor()
|
|
|
|
{
|
|
|
|
if (tree.isEditable() && cellEditor == null)
|
|
|
|
setCellEditor(createDefaultCellEditor());
|
|
|
|
createdCellEditor = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Messaged from the tree we're in when the renderer has changed.
|
|
|
|
*/
|
|
|
|
protected void updateRenderer()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (tree != null)
|
|
|
|
{
|
|
|
|
if(tree.getCellRenderer() == null)
|
|
|
|
{
|
|
|
|
if(currentCellRenderer == null)
|
|
|
|
currentCellRenderer = createDefaultCellRenderer();
|
|
|
|
tree.setCellRenderer(currentCellRenderer);
|
|
|
|
}
|
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
preferredSize = null;
|
|
|
|
updateCachedPreferredSize();
|
|
|
|
tree.treeDidChange();
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates the <code>preferredSize</code> instance variable, which is
|
2005-11-15 23:20:01 +00:00
|
|
|
* returned from <code>getPreferredSize()</code>.
|
2005-09-23 21:31:04 +00:00
|
|
|
*/
|
|
|
|
protected void updateCachedPreferredSize()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
int maxWidth = 0;
|
|
|
|
boolean isLeaf = false;
|
|
|
|
if (currentVisiblePath != null)
|
|
|
|
{
|
|
|
|
Object[] path = currentVisiblePath.getPath();
|
|
|
|
for (int i = 0; i < path.length; i++)
|
|
|
|
{
|
|
|
|
TreePath curr = new TreePath(getPathToRoot(path[i], 0));
|
|
|
|
Rectangle bounds = getPathBounds(tree, curr);
|
|
|
|
if (treeModel != null)
|
|
|
|
isLeaf = treeModel.isLeaf(path[i]);
|
|
|
|
if (!isLeaf && hasControlIcons())
|
|
|
|
bounds.width += getCurrentControlIcon(curr).getIconWidth();
|
|
|
|
maxWidth = Math.max(maxWidth, bounds.x + bounds.width);
|
|
|
|
}
|
|
|
|
preferredSize = new Dimension(maxWidth, (getRowHeight() * path.length));
|
|
|
|
}
|
|
|
|
else preferredSize = new Dimension(0, 0);
|
|
|
|
validCachedPreferredSize = true;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Messaged from the VisibleTreeNode after it has been expanded.
|
|
|
|
*
|
|
|
|
* @param path
|
|
|
|
* is the path that has been expanded.
|
|
|
|
*/
|
|
|
|
protected void pathWasExpanded(TreePath path)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
validCachedPreferredSize = false;
|
|
|
|
tree.revalidate();
|
|
|
|
tree.repaint();
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Messaged from the VisibleTreeNode after it has collapsed
|
|
|
|
*/
|
|
|
|
protected void pathWasCollapsed(TreePath path)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
validCachedPreferredSize = false;
|
|
|
|
tree.revalidate();
|
|
|
|
tree.repaint();
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Install all defaults for the tree.
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
protected void installDefaults()
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
LookAndFeel.installColorsAndFont(tree, "Tree.background",
|
|
|
|
"Tree.foreground", "Tree.font");
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.setOpaque(true);
|
|
|
|
|
2005-11-15 23:20:01 +00:00
|
|
|
rightChildIndent = UIManager.getInt("Tree.rightChildIndent");
|
|
|
|
leftChildIndent = UIManager.getInt("Tree.leftChildIndent");
|
|
|
|
setRowHeight(UIManager.getInt("Tree.rowHeight"));
|
2005-11-18 00:59:33 +00:00
|
|
|
tree.setRowHeight(getRowHeight());
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.requestFocusInWindow(false);
|
2005-11-15 23:20:01 +00:00
|
|
|
tree.setScrollsOnExpand(UIManager.getBoolean("Tree.scrollsOnExpand"));
|
|
|
|
setExpandedIcon(UIManager.getIcon("Tree.expandedIcon"));
|
|
|
|
setCollapsedIcon(UIManager.getIcon("Tree.collapsedIcon"));
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Install all keyboard actions for this
|
|
|
|
*/
|
|
|
|
protected void installKeyboardActions()
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
InputMap focusInputMap = (InputMap) UIManager.get("Tree.focusInputMap");
|
2005-09-23 21:31:04 +00:00
|
|
|
InputMapUIResource parentInputMap = new InputMapUIResource();
|
2005-11-15 23:20:01 +00:00
|
|
|
ActionMap parentActionMap = new ActionMapUIResource();
|
2005-09-23 21:31:04 +00:00
|
|
|
action = new TreeAction();
|
|
|
|
Object keys[] = focusInputMap.allKeys();
|
|
|
|
|
|
|
|
for (int i = 0; i < keys.length; i++)
|
|
|
|
{
|
|
|
|
parentInputMap.put(
|
|
|
|
KeyStroke.getKeyStroke(
|
|
|
|
((KeyStroke) keys[i]).getKeyCode(),
|
|
|
|
convertModifiers(((KeyStroke) keys[i]).getModifiers())),
|
|
|
|
(String) focusInputMap.get((KeyStroke) keys[i]));
|
|
|
|
|
|
|
|
parentInputMap.put(
|
|
|
|
KeyStroke.getKeyStroke(
|
|
|
|
((KeyStroke) keys[i]).getKeyCode(),
|
|
|
|
((KeyStroke) keys[i]).getModifiers()),
|
|
|
|
(String) focusInputMap.get((KeyStroke) keys[i]));
|
|
|
|
|
|
|
|
parentActionMap.put(
|
|
|
|
(String) focusInputMap.get((KeyStroke) keys[i]),
|
|
|
|
new ActionListenerProxy(
|
|
|
|
action,
|
|
|
|
(String) focusInputMap.get((KeyStroke) keys[i])));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
parentInputMap.setParent(tree.getInputMap(
|
|
|
|
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).getParent());
|
|
|
|
parentActionMap.setParent(tree.getActionMap().getParent());
|
|
|
|
tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).setParent(
|
|
|
|
parentInputMap);
|
|
|
|
tree.getActionMap().setParent(parentActionMap);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts the modifiers.
|
|
|
|
*
|
|
|
|
* @param mod -
|
|
|
|
* modifier to convert
|
|
|
|
* @returns the new modifier
|
|
|
|
*/
|
|
|
|
private int convertModifiers(int mod)
|
|
|
|
{
|
|
|
|
if ((mod & KeyEvent.SHIFT_DOWN_MASK) != 0)
|
|
|
|
{
|
|
|
|
mod |= KeyEvent.SHIFT_MASK;
|
|
|
|
mod &= ~KeyEvent.SHIFT_DOWN_MASK;
|
|
|
|
}
|
|
|
|
if ((mod & KeyEvent.CTRL_DOWN_MASK) != 0)
|
|
|
|
{
|
|
|
|
mod |= KeyEvent.CTRL_MASK;
|
|
|
|
mod &= ~KeyEvent.CTRL_DOWN_MASK;
|
|
|
|
}
|
|
|
|
if ((mod & KeyEvent.META_DOWN_MASK) != 0)
|
|
|
|
{
|
|
|
|
mod |= KeyEvent.META_MASK;
|
|
|
|
mod &= ~KeyEvent.META_DOWN_MASK;
|
|
|
|
}
|
|
|
|
if ((mod & KeyEvent.ALT_DOWN_MASK) != 0)
|
|
|
|
{
|
|
|
|
mod |= KeyEvent.ALT_MASK;
|
|
|
|
mod &= ~KeyEvent.ALT_DOWN_MASK;
|
|
|
|
}
|
|
|
|
if ((mod & KeyEvent.ALT_GRAPH_DOWN_MASK) != 0)
|
|
|
|
{
|
|
|
|
mod |= KeyEvent.ALT_GRAPH_MASK;
|
|
|
|
mod &= ~KeyEvent.ALT_GRAPH_DOWN_MASK;
|
|
|
|
}
|
|
|
|
return mod;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Install all listeners for this
|
|
|
|
*/
|
|
|
|
protected void installListeners()
|
|
|
|
{
|
|
|
|
tree.addPropertyChangeListener(propertyChangeListener);
|
|
|
|
tree.addFocusListener(focusListener);
|
|
|
|
tree.addTreeSelectionListener(treeSelectionListener);
|
2005-11-15 23:20:01 +00:00
|
|
|
tree.addMouseListener(mouseListener);
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.addKeyListener(keyListener);
|
|
|
|
tree.addPropertyChangeListener(selectionModelPropertyChangeListener);
|
|
|
|
tree.addComponentListener(componentListener);
|
|
|
|
tree.addTreeExpansionListener(treeExpansionListener);
|
|
|
|
if (treeModel != null)
|
|
|
|
treeModel.addTreeModelListener(treeModelListener);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Install the UI for the component
|
|
|
|
*
|
|
|
|
* @param c
|
|
|
|
* the component to install UI for
|
|
|
|
*/
|
|
|
|
public void installUI(JComponent c)
|
|
|
|
{
|
|
|
|
tree = (JTree) c;
|
2005-11-15 23:20:01 +00:00
|
|
|
prepareForUIInstall();
|
|
|
|
super.installUI(c);
|
|
|
|
installDefaults();
|
2005-09-23 21:31:04 +00:00
|
|
|
|
2005-11-15 23:20:01 +00:00
|
|
|
installComponents();
|
|
|
|
installKeyboardActions();
|
|
|
|
installListeners();
|
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
setCellEditor(createDefaultCellEditor());
|
|
|
|
createdCellEditor = true;
|
|
|
|
isEditing = false;
|
2005-11-15 23:20:01 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
TreeModel mod = tree.getModel();
|
|
|
|
setModel(mod);
|
|
|
|
if (mod != null)
|
2005-11-15 23:20:01 +00:00
|
|
|
{
|
|
|
|
TreePath path = new TreePath(mod.getRoot());
|
|
|
|
if (!tree.isExpanded(path))
|
|
|
|
toggleExpandState(path);
|
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
treeSelectionModel = tree.getSelectionModel();
|
|
|
|
|
|
|
|
completeUIInstall();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Uninstall the defaults for the tree
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
protected void uninstallDefaults()
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
|
|
|
tree.setFont(null);
|
|
|
|
tree.setForeground(null);
|
|
|
|
tree.setBackground(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Uninstall the UI for the component
|
|
|
|
*
|
|
|
|
* @param c
|
|
|
|
* the component to uninstall UI for
|
|
|
|
*/
|
|
|
|
public void uninstallUI(JComponent c)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
prepareForUIUninstall();
|
|
|
|
uninstallDefaults();
|
2005-09-23 21:31:04 +00:00
|
|
|
uninstallKeyboardActions();
|
|
|
|
uninstallListeners();
|
|
|
|
tree = null;
|
2005-11-15 23:20:01 +00:00
|
|
|
uninstallComponents();
|
2005-09-23 21:31:04 +00:00
|
|
|
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;
|
2005-11-15 23:20:01 +00:00
|
|
|
if (currentVisiblePath == null)
|
|
|
|
updateCurrentVisiblePath();
|
2005-11-18 00:59:33 +00:00
|
|
|
|
|
|
|
Rectangle clip = g.getClipBounds();
|
|
|
|
Insets insets = tree.getInsets();
|
|
|
|
|
|
|
|
if (clip != null && treeModel != null && currentVisiblePath != null)
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
int startIndex = tree.getClosestRowForLocation(clip.x, clip.y);
|
|
|
|
int endIndex = tree.getClosestRowForLocation(clip.x + clip.width,
|
|
|
|
clip.y + clip.height);
|
|
|
|
|
|
|
|
paintVerticalPartOfLeg(g, clip, insets, currentVisiblePath);
|
|
|
|
for (int i = startIndex; i <= endIndex; i++)
|
|
|
|
{
|
|
|
|
Object curr = currentVisiblePath.getPathComponent(i);
|
|
|
|
boolean isLeaf = treeModel.isLeaf(curr);
|
|
|
|
TreePath path = new TreePath(getPathToRoot(curr, 0));
|
|
|
|
|
|
|
|
boolean isExpanded = tree.isExpanded(path);
|
|
|
|
Rectangle bounds = getPathBounds(tree, path);
|
|
|
|
paintHorizontalPartOfLeg(g, clip, insets, bounds, path, i,
|
|
|
|
isExpanded, false, isLeaf);
|
|
|
|
paintRow(g, clip, insets, bounds, path, i, isExpanded, false,
|
|
|
|
isLeaf);
|
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (beginRow < endRow)
|
|
|
|
{
|
|
|
|
int temp = endRow;
|
|
|
|
endRow = beginRow;
|
|
|
|
beginRow = temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = beginRow; i < endRow; i++)
|
|
|
|
{
|
|
|
|
TreePath path = getPathForRow(tree, i);
|
|
|
|
if (!tree.isVisible(path))
|
|
|
|
tree.makeVisible(path);
|
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the preferred minimum size.
|
|
|
|
*
|
|
|
|
* @param newSize
|
|
|
|
* is the new preferred minimum size.
|
|
|
|
*/
|
|
|
|
public void setPreferredMinSize(Dimension newSize)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
preferredMinSize = newSize;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the preferred minimum size.
|
|
|
|
*
|
|
|
|
* @returns the preferred minimum size.
|
|
|
|
*/
|
|
|
|
public Dimension getPreferredMinSize()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
return preferredMinSize;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// FIXME: checkConsistancy not implemented, c not used
|
|
|
|
if(!validCachedPreferredSize)
|
|
|
|
updateCachedPreferredSize();
|
|
|
|
return preferredSize;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
Dimension min = getPreferredMinSize();
|
|
|
|
if (min == null)
|
|
|
|
return new Dimension();
|
|
|
|
return min;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (c instanceof JTree)
|
|
|
|
return ((JTree) c).getPreferredSize();
|
|
|
|
return new Dimension();
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
|
|
|
completeEditing(false, true, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
if (messageStop)
|
|
|
|
{
|
|
|
|
getCellEditor().stopCellEditing();
|
|
|
|
stopEditingInCompleteEditing = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (messageCancel)
|
|
|
|
{
|
|
|
|
getCellEditor().cancelCellEditing();
|
|
|
|
stopEditingInCompleteEditing = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (messageTree)
|
2005-11-15 23:20:01 +00:00
|
|
|
treeModel.valueForPathChanged(tree.getLeadSelectionPath(), newVal);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
int x;
|
|
|
|
int y;
|
|
|
|
if (event == null)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
Rectangle bounds = getPathBounds(tree, path);
|
2005-09-23 21:31:04 +00:00
|
|
|
x = bounds.x;
|
|
|
|
y = bounds.y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x = event.getX();
|
|
|
|
y = event.getY();
|
|
|
|
}
|
|
|
|
|
|
|
|
updateCellEditor();
|
|
|
|
TreeCellEditor ed = getCellEditor();
|
|
|
|
if (ed != null && ed.shouldSelectCell(event) && ed.isCellEditable(event))
|
|
|
|
{
|
|
|
|
editingPath = path;
|
|
|
|
editingRow = tree.getRowForPath(editingPath);
|
2005-11-15 23:20:01 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
Object val = editingPath.getLastPathComponent();
|
|
|
|
cellEditor.addCellEditorListener(cellEditorListener);
|
|
|
|
stopEditingInCompleteEditing = false;
|
|
|
|
boolean expanded = tree.isExpanded(editingPath);
|
|
|
|
isEditing = true;
|
|
|
|
editingComponent = ed.getTreeCellEditorComponent(tree, val, true,
|
|
|
|
expanded,
|
|
|
|
isLeaf(editingRow),
|
|
|
|
editingRow);
|
|
|
|
editingComponent.getParent().setVisible(true);
|
|
|
|
editingComponent.getParent().validate();
|
|
|
|
tree.add(editingComponent.getParent());
|
|
|
|
editingComponent.getParent().validate();
|
2005-11-15 23:20:01 +00:00
|
|
|
validCachedPreferredSize = false;
|
|
|
|
tree.revalidate();
|
2005-09-23 21:31:04 +00:00
|
|
|
((JTextField) editingComponent).requestFocusInWindow(false);
|
|
|
|
editorTimer.start();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (isLocationInExpandControl(path, mouseX, mouseY))
|
|
|
|
toggleExpandState(path);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
boolean cntlClick = false;
|
|
|
|
int row = getRowForPath(tree, path);
|
|
|
|
|
|
|
|
if (!isLeaf(row))
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
Rectangle bounds = getPathBounds(tree, path);
|
2005-11-15 23:20:01 +00:00
|
|
|
|
|
|
|
if (hasControlIcons() && (mouseX < bounds.x)
|
|
|
|
&& (mouseX > (bounds.x - getCurrentControlIcon(path).getIconWidth() - gap)))
|
|
|
|
cntlClick = true;
|
|
|
|
}
|
|
|
|
return cntlClick;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
toggleExpandState(path);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (tree.isExpanded(path))
|
|
|
|
tree.collapsePath(path);
|
|
|
|
else
|
|
|
|
tree.expandPath(path);
|
|
|
|
updateCurrentVisiblePath();
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
return (tree.getSelectionModel().getSelectionMode() ==
|
|
|
|
TreeSelectionModel.SINGLE_TREE_SELECTION);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
return (tree.getSelectionModel().getSelectionMode() ==
|
|
|
|
TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
return true;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (isToggleSelectionEvent(event))
|
|
|
|
{
|
|
|
|
if (tree.isPathSelected(path))
|
|
|
|
tree.removeSelectionPath(path);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tree.addSelectionPath(path);
|
|
|
|
tree.setAnchorSelectionPath(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (isMultiSelectEvent(event))
|
|
|
|
{
|
|
|
|
TreePath anchor = tree.getAnchorSelectionPath();
|
|
|
|
if (anchor != null)
|
|
|
|
{
|
|
|
|
int aRow = getRowForPath(tree, anchor);
|
|
|
|
tree.addSelectionInterval(aRow, getRowForPath(tree, path));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
tree.addSelectionPath(path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
tree.addSelectionPath(path);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
TreePath pathForRow = getPathForRow(tree, row);
|
|
|
|
if (pathForRow == null)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
Object node = pathForRow.getLastPathComponent();
|
2005-11-15 23:20:01 +00:00
|
|
|
return treeModel.isLeaf(node);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class implements the actions that we want to happen when specific keys
|
|
|
|
* are pressed for the JTree. The actionPerformed method is called when a key
|
|
|
|
* that has been registered for the JTree is received.
|
|
|
|
*/
|
|
|
|
class TreeAction
|
|
|
|
extends AbstractAction
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* What to do when this action is called.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* the ActionEvent that caused this action.
|
|
|
|
*/
|
|
|
|
public void actionPerformed(ActionEvent e)
|
|
|
|
{
|
|
|
|
TreePath lead = tree.getLeadSelectionPath();
|
|
|
|
|
|
|
|
if (e.getActionCommand().equals("selectPreviousChangeLead")
|
|
|
|
|| e.getActionCommand().equals("selectPreviousExtendSelection")
|
|
|
|
|| e.getActionCommand().equals("selectPrevious")
|
|
|
|
|| e.getActionCommand().equals("selectNext")
|
|
|
|
|| e.getActionCommand().equals("selectNextExtendSelection")
|
|
|
|
|| e.getActionCommand().equals("selectNextChangeLead"))
|
|
|
|
(new TreeIncrementAction(0, "")).actionPerformed(e);
|
|
|
|
else if (e.getActionCommand().equals("selectParent")
|
|
|
|
|| e.getActionCommand().equals("selectChild"))
|
|
|
|
(new TreeTraverseAction(0, "")).actionPerformed(e);
|
|
|
|
else if (e.getActionCommand().equals("selectAll"))
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
TreePath[] paths = new TreePath[tree.getVisibleRowCount()];
|
2005-09-23 21:31:04 +00:00
|
|
|
|
2005-11-15 23:20:01 +00:00
|
|
|
Object curr = getNextVisibleNode(treeModel.getRoot());
|
2005-09-23 21:31:04 +00:00
|
|
|
int i = 0;
|
|
|
|
while (curr != null && i < paths.length)
|
|
|
|
{
|
|
|
|
paths[i] = new TreePath(getPathToRoot(curr, 0));
|
|
|
|
i++;
|
|
|
|
}
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.addSelectionPaths(paths);
|
|
|
|
}
|
|
|
|
else if (e.getActionCommand().equals("startEditing"))
|
|
|
|
tree.startEditingAtPath(lead);
|
|
|
|
else if (e.getActionCommand().equals("toggle"))
|
|
|
|
{
|
|
|
|
if (tree.isEditing())
|
|
|
|
tree.stopEditing();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Object last = lead.getLastPathComponent();
|
|
|
|
TreePath path = new TreePath(getPathToRoot(last, 0));
|
2005-11-15 23:20:01 +00:00
|
|
|
if (!treeModel.isLeaf(last))
|
|
|
|
toggleExpandState(path);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (e.getActionCommand().equals("clearSelection"))
|
|
|
|
tree.clearSelection();
|
|
|
|
|
|
|
|
if (tree.isEditing() && !e.getActionCommand().equals("startEditing"))
|
|
|
|
tree.cancelEditing();
|
|
|
|
|
|
|
|
tree.scrollPathToVisible(lead);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class is used to mimic the behaviour of the JDK when registering
|
|
|
|
* keyboard actions. It is the same as the private class used in JComponent
|
|
|
|
* for the same reason. This class receives an action event and dispatches it
|
|
|
|
* to the true receiver after altering the actionCommand property of the
|
|
|
|
* event.
|
|
|
|
*/
|
|
|
|
private static class ActionListenerProxy
|
|
|
|
extends AbstractAction
|
|
|
|
{
|
|
|
|
ActionListener target;
|
|
|
|
|
|
|
|
String bindingCommandName;
|
|
|
|
|
|
|
|
public ActionListenerProxy(ActionListener li, String cmd)
|
|
|
|
{
|
|
|
|
target = li;
|
|
|
|
bindingCommandName = cmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void actionPerformed(ActionEvent e)
|
|
|
|
{
|
|
|
|
ActionEvent derivedEvent = new ActionEvent(e.getSource(), e.getID(),
|
|
|
|
bindingCommandName,
|
|
|
|
e.getModifiers());
|
|
|
|
|
|
|
|
target.actionPerformed(derivedEvent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The timer that updates the editor component.
|
|
|
|
*/
|
|
|
|
private class EditorUpdateTimer
|
|
|
|
extends Timer
|
|
|
|
implements ActionListener
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Creates a new EditorUpdateTimer object with a default delay of 0.3
|
|
|
|
* seconds.
|
|
|
|
*/
|
|
|
|
public EditorUpdateTimer()
|
|
|
|
{
|
|
|
|
super(300, null);
|
|
|
|
addActionListener(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Lets the caret blink and repaints the table.
|
|
|
|
*/
|
|
|
|
public void actionPerformed(ActionEvent ev)
|
|
|
|
{
|
|
|
|
Caret c = ((JTextField) editingComponent).getCaret();
|
|
|
|
if (c != null)
|
|
|
|
c.setVisible(!c.isVisible());
|
|
|
|
tree.repaint();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates the blink delay according to the current caret.
|
|
|
|
*/
|
|
|
|
public void update()
|
|
|
|
{
|
|
|
|
stop();
|
|
|
|
Caret c = ((JTextField) editingComponent).getCaret();
|
|
|
|
if (c != null)
|
|
|
|
{
|
|
|
|
setDelay(c.getBlinkRate());
|
|
|
|
if (((JTextField) editingComponent).isEditable())
|
|
|
|
start();
|
|
|
|
else
|
|
|
|
c.setVisible(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates the preferred size when scrolling, if necessary.
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
public class ComponentHandler extends ComponentAdapter
|
2005-09-23 21:31:04 +00:00
|
|
|
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()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// Nothing to do here.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when the component's position changes.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* the event that occurs when moving the component
|
|
|
|
*/
|
|
|
|
public void componentMoved(ComponentEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2005-11-18 00:59:33 +00:00
|
|
|
* Creates, if necessary, and starts a Timer to check if needed to resize the
|
2005-09-23 21:31:04 +00:00
|
|
|
* bounds
|
|
|
|
*/
|
|
|
|
protected void startTimer()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Listener responsible for getting cell editing events and updating the tree
|
|
|
|
* accordingly.
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
public class CellEditorHandler implements CellEditorListener
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
|
|
|
public CellEditorHandler()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// Nothing to do here.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
editingPath = null;
|
2005-07-16 00:30:23 +00:00
|
|
|
editingRow = -1;
|
2005-09-23 21:31:04 +00:00
|
|
|
stopEditingInCompleteEditing = false;
|
|
|
|
if (editingComponent != null)
|
|
|
|
{
|
|
|
|
tree.remove(editingComponent.getParent());
|
|
|
|
editingComponent = null;
|
|
|
|
}
|
|
|
|
if (cellEditor != null)
|
|
|
|
{
|
|
|
|
newVal = ((JTextField) getCellEditor().getCellEditorValue()).getText();
|
|
|
|
completeEditing(false, false, true);
|
|
|
|
if (cellEditor instanceof DefaultTreeCellEditor)
|
|
|
|
tree.removeTreeSelectionListener((DefaultTreeCellEditor) cellEditor);
|
|
|
|
cellEditor.removeCellEditorListener(cellEditorListener);
|
|
|
|
setCellEditor(null);
|
|
|
|
createdCellEditor = false;
|
|
|
|
}
|
|
|
|
isEditing = false;
|
|
|
|
tree.requestFocusInWindow(false);
|
|
|
|
editorTimer.stop();
|
2005-11-15 23:20:01 +00:00
|
|
|
validCachedPreferredSize = false;
|
|
|
|
tree.revalidate();
|
|
|
|
tree.repaint();
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
editingPath = null;
|
|
|
|
editingRow = -1;
|
|
|
|
stopEditingInCompleteEditing = false;
|
|
|
|
if (editingComponent != null)
|
|
|
|
tree.remove(editingComponent.getParent());
|
|
|
|
editingComponent = null;
|
|
|
|
if (cellEditor != null)
|
|
|
|
{
|
|
|
|
if (cellEditor instanceof DefaultTreeCellEditor)
|
|
|
|
tree.removeTreeSelectionListener((DefaultTreeCellEditor) cellEditor);
|
|
|
|
cellEditor.removeCellEditorListener(cellEditorListener);
|
|
|
|
setCellEditor(null);
|
|
|
|
createdCellEditor = false;
|
|
|
|
}
|
|
|
|
tree.requestFocusInWindow(false);
|
|
|
|
editorTimer.stop();
|
|
|
|
isEditing = false;
|
2005-11-15 23:20:01 +00:00
|
|
|
validCachedPreferredSize = false;
|
|
|
|
tree.revalidate();
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.repaint();
|
|
|
|
}
|
|
|
|
}// CellEditorHandler
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Repaints the lead selection row when focus is lost/grained.
|
|
|
|
*/
|
|
|
|
public class FocusHandler
|
|
|
|
implements FocusListener
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
|
|
|
public FocusHandler()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// Nothing to do here.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// Nothing to do here.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when a key has been pressed.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* the key pressed
|
|
|
|
*/
|
|
|
|
public void keyPressed(KeyEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when a key has been released
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* the key released
|
|
|
|
*/
|
|
|
|
public void keyReleased(KeyEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* MouseListener is responsible for updating the selection based on mouse
|
|
|
|
* events.
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
public class MouseHandler extends MouseAdapter implements MouseMotionListener
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
|
|
|
public MouseHandler()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// Nothing to do here.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when a mouse button has been pressed on a component.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* is the mouse event that occured
|
|
|
|
*/
|
|
|
|
public void mousePressed(MouseEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
Point click = e.getPoint();
|
|
|
|
TreePath path = getClosestPathForLocation(tree, click.x, click.y);
|
|
|
|
|
|
|
|
if (path != null)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
Rectangle bounds = getPathBounds(tree, path);
|
2005-11-15 23:20:01 +00:00
|
|
|
int row = getRowForPath(tree, path);
|
|
|
|
boolean cntlClick = isLocationInExpandControl(path, click.x, click.y);
|
|
|
|
|
|
|
|
boolean isLeaf = isLeaf(row);
|
|
|
|
|
|
|
|
TreeCellRenderer tcr = getCellRenderer();
|
|
|
|
Icon icon;
|
|
|
|
if (isLeaf)
|
|
|
|
icon = UIManager.getIcon("Tree.leafIcon");
|
|
|
|
else if (tree.isExpanded(path))
|
|
|
|
icon = UIManager.getIcon("Tree.openIcon");
|
|
|
|
else
|
|
|
|
icon = UIManager.getIcon("Tree.closedIcon");
|
|
|
|
|
|
|
|
if (tcr instanceof DefaultTreeCellRenderer)
|
|
|
|
{
|
|
|
|
Icon tmp = ((DefaultTreeCellRenderer) tcr).getIcon();
|
|
|
|
if (tmp != null)
|
|
|
|
icon = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add gap*2 for the space before and after the text
|
|
|
|
if (icon != null)
|
|
|
|
bounds.width += icon.getIconWidth() + gap*2;
|
|
|
|
|
|
|
|
boolean inBounds = bounds.contains(click.x, click.y);
|
|
|
|
if ((inBounds || cntlClick) && tree.isVisible(path))
|
|
|
|
{
|
|
|
|
if (inBounds)
|
|
|
|
{
|
|
|
|
selectPath(tree, path);
|
|
|
|
if (e.getClickCount() == 2 && !isLeaf(row))
|
|
|
|
toggleExpandState(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cntlClick)
|
|
|
|
{
|
|
|
|
handleExpandControlClick(path, click.x, click.y);
|
|
|
|
if (cellEditor != null)
|
|
|
|
cellEditor.cancelCellEditing();
|
2005-11-18 00:59:33 +00:00
|
|
|
tree.scrollPathToVisible(path);
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
|
|
|
else if (tree.isEditable())
|
|
|
|
startEditing(path, e);
|
|
|
|
}
|
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when a mouse button has been released on a component.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* is the mouse event that occured
|
|
|
|
*/
|
|
|
|
public void mouseReleased(MouseEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* MouseInputHandler handles passing all mouse events, including mouse motion
|
|
|
|
* events, until the mouse is released to the destination it is constructed
|
|
|
|
* with.
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
public class MouseInputHandler implements MouseInputListener
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
|
|
|
/** 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 e
|
|
|
|
* is the event received
|
|
|
|
*/
|
|
|
|
public MouseInputHandler(Component source, Component destination,
|
|
|
|
MouseEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
this.source = source;
|
|
|
|
this.destination = destination;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when a mouse button has been pressed on a component.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* mouse event that occured
|
|
|
|
*/
|
|
|
|
public void mousePressed(MouseEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when a mouse button has been released on a component.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* mouse event that occured
|
|
|
|
*/
|
|
|
|
public void mouseReleased(MouseEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when the mouse enters a component.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* mouse event that occured
|
|
|
|
*/
|
|
|
|
public void mouseEntered(MouseEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when the mouse exits a component.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* mouse event that occured
|
|
|
|
*/
|
|
|
|
public void mouseExited(MouseEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes event from the source
|
|
|
|
*/
|
|
|
|
protected void removeFromSource()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// Nothing to do here.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2005-11-18 00:59:33 +00:00
|
|
|
* Returns, by reference in bounds, the size and x origin to place value at.
|
|
|
|
* The calling method is responsible for determining the Y location.
|
|
|
|
* If bounds is null, a newly created Rectangle should be returned,
|
|
|
|
* otherwise the value should be placed in bounds and returned.
|
2005-09-23 21:31:04 +00:00
|
|
|
*
|
2005-11-18 00:59:33 +00:00
|
|
|
* @param cell
|
2005-09-23 21:31:04 +00:00
|
|
|
* 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
|
|
|
|
*/
|
2005-11-18 00:59:33 +00:00
|
|
|
public Rectangle getNodeDimensions(Object cell, int row, int depth,
|
2005-09-23 21:31:04 +00:00
|
|
|
boolean expanded, Rectangle size)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
if (size == null || cell == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
String s = cell.toString();
|
|
|
|
Font f = tree.getFont();
|
|
|
|
FontMetrics fm = tree.getToolkit().getFontMetrics(f);
|
|
|
|
|
|
|
|
if (s != null)
|
|
|
|
{
|
|
|
|
size.x = getRowX(row, depth);
|
|
|
|
size.width = SwingUtilities.computeStringWidth(fm, s);
|
|
|
|
size.height = fm.getHeight();
|
|
|
|
}
|
|
|
|
return size;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the amount to indent the given row
|
|
|
|
*
|
|
|
|
* @return amount to indent the given row.
|
|
|
|
*/
|
|
|
|
protected int getRowX(int row, int depth)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
if (row == 0)
|
|
|
|
return 0;
|
|
|
|
return depth * rightChildIndent;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
}// NodeDimensionsHandler
|
|
|
|
|
|
|
|
/**
|
|
|
|
* PropertyChangeListener for the tree. Updates the appropriate varaible, or
|
|
|
|
* TreeState, based on what changes.
|
|
|
|
*/
|
|
|
|
public class PropertyChangeHandler
|
|
|
|
implements PropertyChangeListener
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
|
|
|
public PropertyChangeHandler()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// Nothing to do here.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if ((event.getPropertyName()).equals("rootVisible"))
|
|
|
|
{
|
|
|
|
validCachedPreferredSize = false;
|
|
|
|
updateCurrentVisiblePath();
|
|
|
|
tree.revalidate();
|
|
|
|
tree.repaint();
|
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// Nothing to do here.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: What should be done here, if anything?
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* ActionListener that invokes cancelEditing when action performed.
|
|
|
|
*/
|
|
|
|
public class TreeCancelEditingAction
|
|
|
|
extends AbstractAction
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
public TreeCancelEditingAction(String name)
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when an action occurs.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* event that occured
|
|
|
|
*/
|
|
|
|
public void actionPerformed(ActionEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the action is enabled.
|
|
|
|
*
|
|
|
|
* @return true if the action is enabled, false otherwise
|
|
|
|
*/
|
|
|
|
public boolean isEnabled()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-07-16 00:30:23 +00:00
|
|
|
return false;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates the TreeState in response to nodes expanding/collapsing.
|
|
|
|
*/
|
|
|
|
public class TreeExpansionHandler
|
|
|
|
implements TreeExpansionListener
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
|
|
|
public TreeExpansionHandler()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// Nothing to do here.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called whenever an item in the tree has been expanded.
|
|
|
|
*
|
|
|
|
* @param event
|
|
|
|
* is the event that occured
|
|
|
|
*/
|
|
|
|
public void treeExpanded(TreeExpansionEvent event)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
validCachedPreferredSize = false;
|
|
|
|
updateCurrentVisiblePath();
|
|
|
|
tree.revalidate();
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.repaint();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called whenever an item in the tree has been collapsed.
|
|
|
|
*
|
|
|
|
* @param event
|
|
|
|
* is the event that occured
|
|
|
|
*/
|
|
|
|
public void treeCollapsed(TreeExpansionEvent event)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
validCachedPreferredSize = false;
|
|
|
|
updateCurrentVisiblePath();
|
|
|
|
tree.revalidate();
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.repaint();
|
|
|
|
}
|
|
|
|
}// 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when an action occurs.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* is the event that occured
|
|
|
|
*/
|
|
|
|
public void actionPerformed(ActionEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the action is enabled.
|
|
|
|
*
|
|
|
|
* @return true if the action is enabled.
|
|
|
|
*/
|
|
|
|
public boolean isEnabled()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly
|
2005-07-16 00:30:23 +00:00
|
|
|
return false;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when an action occurs.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* is the event that occured
|
|
|
|
*/
|
|
|
|
public void actionPerformed(ActionEvent e)
|
|
|
|
{
|
|
|
|
Object last = tree.getLeadSelectionPath().getLastPathComponent();
|
|
|
|
|
|
|
|
if (e.getActionCommand().equals("selectPreviousChangeLead"))
|
|
|
|
{
|
|
|
|
Object prev = getPreviousVisibleNode(last);
|
2005-11-15 23:20:01 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
if (prev != null)
|
2005-07-16 00:30:23 +00:00
|
|
|
{
|
2005-09-23 21:31:04 +00:00
|
|
|
TreePath newPath = new TreePath(getPathToRoot(prev, 0));
|
2005-11-15 23:20:01 +00:00
|
|
|
selectPath(tree, newPath);
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.setLeadSelectionPath(newPath);
|
2005-07-16 00:30:23 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
else if (e.getActionCommand().equals("selectPreviousExtendSelection"))
|
|
|
|
{
|
|
|
|
Object prev = getPreviousVisibleNode(last);
|
|
|
|
if (prev != null)
|
2005-07-16 00:30:23 +00:00
|
|
|
{
|
2005-09-23 21:31:04 +00:00
|
|
|
TreePath newPath = new TreePath(getPathToRoot(prev, 0));
|
|
|
|
tree.addSelectionPath(newPath);
|
|
|
|
tree.setLeadSelectionPath(newPath);
|
2005-07-16 00:30:23 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
else if (e.getActionCommand().equals("selectPrevious"))
|
|
|
|
{
|
|
|
|
Object prev = getPreviousVisibleNode(last);
|
2005-11-15 23:20:01 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
if (prev != null)
|
2005-07-16 00:30:23 +00:00
|
|
|
{
|
2005-09-23 21:31:04 +00:00
|
|
|
TreePath newPath = new TreePath(getPathToRoot(prev, 0));
|
2005-11-15 23:20:01 +00:00
|
|
|
selectPath(tree, newPath);
|
2005-07-16 00:30:23 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
else if (e.getActionCommand().equals("selectNext"))
|
|
|
|
{
|
|
|
|
Object next = getNextVisibleNode(last);
|
2005-11-15 23:20:01 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
if (next != null)
|
2005-07-16 00:30:23 +00:00
|
|
|
{
|
2005-09-23 21:31:04 +00:00
|
|
|
TreePath newPath = new TreePath(getPathToRoot(next, 0));
|
|
|
|
selectPath(tree, newPath);
|
2005-07-16 00:30:23 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
else if (e.getActionCommand().equals("selectNextExtendSelection"))
|
|
|
|
{
|
|
|
|
Object next = getNextVisibleNode(last);
|
|
|
|
if (next != null)
|
2005-07-16 00:30:23 +00:00
|
|
|
{
|
2005-09-23 21:31:04 +00:00
|
|
|
TreePath newPath = new TreePath(getPathToRoot(next, 0));
|
|
|
|
tree.addSelectionPath(newPath);
|
|
|
|
tree.setLeadSelectionPath(newPath);
|
2005-07-16 00:30:23 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
else if (e.getActionCommand().equals("selectNextChangeLead"))
|
|
|
|
{
|
|
|
|
Object next = getNextVisibleNode(last);
|
|
|
|
if (next != null)
|
|
|
|
{
|
|
|
|
TreePath newPath = new TreePath(getPathToRoot(next, 0));
|
|
|
|
selectPath(tree, newPath);
|
|
|
|
tree.setLeadSelectionPath(newPath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the action is enabled.
|
|
|
|
*
|
|
|
|
* @return true if the action is enabled.
|
|
|
|
*/
|
|
|
|
public boolean isEnabled()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly
|
2005-09-23 21:31:04 +00:00
|
|
|
return false;
|
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Forwards all TreeModel events to the TreeState.
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
public class TreeModelHandler implements TreeModelListener
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
|
|
|
public TreeModelHandler()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// Nothing to do here.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
validCachedPreferredSize = false;
|
|
|
|
updateCurrentVisiblePath();
|
|
|
|
tree.revalidate();
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.repaint();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
validCachedPreferredSize = false;
|
|
|
|
updateCurrentVisiblePath();
|
|
|
|
tree.revalidate();
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.repaint();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
validCachedPreferredSize = false;
|
|
|
|
updateCurrentVisiblePath();
|
|
|
|
tree.revalidate();
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.repaint();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (e.getPath().length == 1
|
|
|
|
&& !e.getPath()[0].equals(treeModel.getRoot()))
|
|
|
|
tree.expandPath(new TreePath(treeModel.getRoot()));
|
|
|
|
updateCurrentVisiblePath();
|
|
|
|
validCachedPreferredSize = false;
|
|
|
|
tree.revalidate();
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.repaint();
|
|
|
|
}
|
|
|
|
}// TreeModelHandler
|
|
|
|
|
|
|
|
/**
|
|
|
|
* TreePageAction handles page up and page down events.
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
public class TreePageAction extends AbstractAction
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
|
|
|
/** 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
this.direction = direction;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when an action occurs.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* is the event that occured
|
|
|
|
*/
|
|
|
|
public void actionPerformed(ActionEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
public class TreeSelectionHandler implements TreeSelectionListener
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
|
|
|
public TreeSelectionHandler()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// Nothing to do here.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
if (tree.isEditing())
|
|
|
|
tree.cancelEditing();
|
|
|
|
}
|
|
|
|
}// TreeSelectionHandler
|
|
|
|
|
|
|
|
/**
|
|
|
|
* For the first selected row expandedness will be toggled.
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
public class TreeToggleAction extends AbstractAction
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* @param name
|
|
|
|
* is the name of <code>Action</code> field
|
|
|
|
*/
|
|
|
|
public TreeToggleAction(String name)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// Nothing to do here.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when an action occurs.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* the event that occured
|
|
|
|
*/
|
|
|
|
public void actionPerformed(ActionEvent e)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly.
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
public class TreeTraverseAction extends AbstractAction
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
this.direction = direction;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when an action occurs.
|
|
|
|
*
|
|
|
|
* @param e
|
|
|
|
* the event that occured
|
|
|
|
*/
|
|
|
|
public void actionPerformed(ActionEvent e)
|
|
|
|
{
|
|
|
|
Object last = tree.getLeadSelectionPath().getLastPathComponent();
|
|
|
|
|
|
|
|
if (e.getActionCommand().equals("selectParent"))
|
|
|
|
{
|
|
|
|
TreePath path = new TreePath(getPathToRoot(last, 0));
|
2005-11-15 23:20:01 +00:00
|
|
|
Object p = getParent(treeModel.getRoot(), last);
|
2005-09-23 21:31:04 +00:00
|
|
|
|
2005-11-15 23:20:01 +00:00
|
|
|
if (!treeModel.isLeaf(last))
|
|
|
|
toggleExpandState(path);
|
2005-09-23 21:31:04 +00:00
|
|
|
else if (p != null)
|
|
|
|
selectPath(tree, new TreePath(getPathToRoot(p, 0)));
|
|
|
|
}
|
|
|
|
else if (e.getActionCommand().equals("selectChild"))
|
|
|
|
{
|
|
|
|
TreePath path = new TreePath(getPathToRoot(last, 0));
|
|
|
|
|
2005-11-15 23:20:01 +00:00
|
|
|
if (!treeModel.isLeaf(last))
|
|
|
|
toggleExpandState(path);
|
2005-09-23 21:31:04 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Object next = getNextVisibleNode(last);
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
if (next != null)
|
|
|
|
selectPath(tree, new TreePath(getPathToRoot(next, 0)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the action is enabled.
|
|
|
|
*
|
|
|
|
* @return true if the action is enabled, false otherwise
|
|
|
|
*/
|
|
|
|
public boolean isEnabled()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
// TODO: Implement this properly
|
2005-09-23 21:31:04 +00:00
|
|
|
return false;
|
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
/**
|
2005-11-15 23:20:01 +00:00
|
|
|
* Returns true if the LookAndFeel implements the control icons. Package
|
2005-09-23 21:31:04 +00:00
|
|
|
* private for use in inner classes.
|
|
|
|
*
|
2005-11-15 23:20:01 +00:00
|
|
|
* @returns true if there are control icons
|
2005-09-23 21:31:04 +00:00
|
|
|
*/
|
|
|
|
boolean hasControlIcons()
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (expandedIcon != null || collapsedIcon != null)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns control icon. It is null if the LookAndFeel does not implements the
|
|
|
|
* control icons. Package private for use in inner classes.
|
|
|
|
*
|
|
|
|
* @return control icon if it exists.
|
|
|
|
*/
|
|
|
|
Icon getCurrentControlIcon(TreePath path)
|
|
|
|
{
|
|
|
|
if (tree.isExpanded(path))
|
|
|
|
return expandedIcon;
|
|
|
|
return collapsedIcon;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the parent of the current node
|
|
|
|
*
|
|
|
|
* @param root
|
|
|
|
* is the root of the tree
|
|
|
|
* @param node
|
|
|
|
* is the current node
|
|
|
|
* @return is the parent of the current node
|
|
|
|
*/
|
|
|
|
Object getParent(Object root, Object node)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (root == null || node == null ||
|
|
|
|
root.equals(node))
|
2005-09-23 21:31:04 +00:00
|
|
|
return null;
|
2005-11-15 23:20:01 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
if (node instanceof TreeNode)
|
|
|
|
return ((TreeNode) node).getParent();
|
|
|
|
return findNode(root, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recursively checks the tree for the specified node, starting at the root.
|
|
|
|
*
|
|
|
|
* @param root
|
|
|
|
* is starting node to start searching at.
|
|
|
|
* @param node
|
|
|
|
* is the node to search for
|
|
|
|
* @return the parent node of node
|
|
|
|
*/
|
|
|
|
private Object findNode(Object root, Object node)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (!treeModel.isLeaf(root) && !root.equals(node))
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
int size = treeModel.getChildCount(root);
|
|
|
|
for (int j = 0; j < size; j++)
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
Object child = treeModel.getChild(root, j);
|
|
|
|
if (node.equals(child))
|
|
|
|
return root;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-11-15 23:20:01 +00:00
|
|
|
Object n = findNode(child, node);
|
|
|
|
if (n != null)
|
|
|
|
return n;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
return null;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/**
|
|
|
|
* Get previous visible node in the tree. Package private for use in inner
|
|
|
|
* classes.
|
|
|
|
*
|
2005-11-15 23:20:01 +00:00
|
|
|
* @param node -
|
2005-09-23 21:31:04 +00:00
|
|
|
* current node
|
|
|
|
* @return the next visible node in the JTree. Return null if there are no
|
|
|
|
* more.
|
|
|
|
*/
|
|
|
|
Object getPreviousVisibleNode(Object node)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (currentVisiblePath != null)
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
Object[] nodes = currentVisiblePath.getPath();
|
|
|
|
int i = 0;
|
|
|
|
while (i < nodes.length && !node.equals(nodes[i]))
|
|
|
|
i++;
|
|
|
|
// return the next node
|
|
|
|
if (i-1 >= 0)
|
|
|
|
return nodes[i-1];
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
return null;
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the next node in the tree Package private for use in inner classes.
|
|
|
|
*
|
2005-11-15 23:20:01 +00:00
|
|
|
* @param curr -
|
2005-09-23 21:31:04 +00:00
|
|
|
* current node
|
|
|
|
* @return the next node in the tree
|
|
|
|
*/
|
|
|
|
Object getNextNode(Object curr)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (!treeModel.isLeaf(curr) && treeModel.getChildCount(curr) > 0)
|
|
|
|
return treeModel.getChild(curr, 0);
|
2005-09-23 21:31:04 +00:00
|
|
|
|
|
|
|
Object node = curr;
|
|
|
|
Object sibling = null;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
sibling = getNextSibling(node);
|
2005-11-15 23:20:01 +00:00
|
|
|
node = getParent(treeModel.getRoot(), node);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
while (sibling == null && node != null);
|
2005-11-15 23:20:01 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
return sibling;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the previous node in the tree Package private for use in inner
|
|
|
|
* classes.
|
|
|
|
*
|
2005-11-15 23:20:01 +00:00
|
|
|
* @param node
|
2005-09-23 21:31:04 +00:00
|
|
|
* current node
|
|
|
|
* @return the previous node in the tree
|
|
|
|
*/
|
|
|
|
Object getPreviousNode(Object node)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
Object parent = getParent(treeModel.getRoot(), node);
|
2005-09-23 21:31:04 +00:00
|
|
|
if (parent == null)
|
|
|
|
return null;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
Object sibling = getPreviousSibling(node);
|
|
|
|
|
|
|
|
if (sibling == null)
|
|
|
|
return parent;
|
|
|
|
|
|
|
|
int size = 0;
|
2005-11-15 23:20:01 +00:00
|
|
|
if (!treeModel.isLeaf(sibling))
|
|
|
|
size = treeModel.getChildCount(sibling);
|
2005-09-23 21:31:04 +00:00
|
|
|
while (size > 0)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
sibling = treeModel.getChild(sibling, size - 1);
|
|
|
|
if (!treeModel.isLeaf(sibling))
|
|
|
|
size = treeModel.getChildCount(sibling);
|
2005-09-23 21:31:04 +00:00
|
|
|
else
|
|
|
|
size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sibling;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the next sibling in the tree Package private for use in inner
|
|
|
|
* classes.
|
|
|
|
*
|
2005-11-15 23:20:01 +00:00
|
|
|
* @param node -
|
2005-09-23 21:31:04 +00:00
|
|
|
* current node
|
|
|
|
* @return the next sibling in the tree
|
|
|
|
*/
|
|
|
|
Object getNextSibling(Object node)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
Object parent = getParent(treeModel.getRoot(), node);
|
2005-09-23 21:31:04 +00:00
|
|
|
if (parent == null)
|
|
|
|
return null;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-11-15 23:20:01 +00:00
|
|
|
int index = treeModel.getIndexOfChild(parent, node) + 1;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
int size = 0;
|
2005-11-15 23:20:01 +00:00
|
|
|
if (!treeModel.isLeaf(parent))
|
|
|
|
size = treeModel.getChildCount(parent);
|
2005-09-23 21:31:04 +00:00
|
|
|
if (index == 0 || index >= size)
|
|
|
|
return null;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-11-15 23:20:01 +00:00
|
|
|
return treeModel.getChild(parent, index);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
/**
|
|
|
|
* Returns the previous sibling in the tree Package private for use in inner
|
|
|
|
* classes.
|
|
|
|
*
|
2005-11-15 23:20:01 +00:00
|
|
|
* @param node -
|
2005-09-23 21:31:04 +00:00
|
|
|
* current node
|
|
|
|
* @return the previous sibling in the tree
|
|
|
|
*/
|
|
|
|
Object getPreviousSibling(Object node)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
Object parent = getParent(treeModel.getRoot(), node);
|
2005-09-23 21:31:04 +00:00
|
|
|
if (parent == null)
|
|
|
|
return null;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-11-15 23:20:01 +00:00
|
|
|
int index = treeModel.getIndexOfChild(parent, node) - 1;
|
2005-07-16 00:30:23 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
int size = 0;
|
2005-11-15 23:20:01 +00:00
|
|
|
if (!treeModel.isLeaf(parent))
|
|
|
|
size = treeModel.getChildCount(parent);
|
2005-09-23 21:31:04 +00:00
|
|
|
if (index < 0 || index >= size)
|
2005-07-16 00:30:23 +00:00
|
|
|
return null;
|
|
|
|
|
2005-11-15 23:20:01 +00:00
|
|
|
return treeModel.getChild(parent, index);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Selects the specified path in the tree depending on modes. Package private
|
|
|
|
* for use in inner classes.
|
|
|
|
*
|
|
|
|
* @param tree
|
|
|
|
* is the tree we are selecting the path in
|
|
|
|
* @param path
|
|
|
|
* is the path we are selecting
|
|
|
|
*/
|
|
|
|
void selectPath(JTree tree, TreePath path)
|
|
|
|
{
|
|
|
|
if (path != null)
|
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
if (tree.getSelectionModel().getSelectionMode() ==
|
|
|
|
TreeSelectionModel.SINGLE_TREE_SELECTION)
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
tree.getSelectionModel().clearSelection();
|
2005-09-23 21:31:04 +00:00
|
|
|
tree.addSelectionPath(path);
|
|
|
|
tree.setLeadSelectionPath(path);
|
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
else if (tree.getSelectionModel().getSelectionMode() ==
|
|
|
|
TreeSelectionModel.CONTIGUOUS_TREE_SELECTION)
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tree.addSelectionPath(path);
|
|
|
|
tree.setLeadSelectionPath(path);
|
2005-11-15 23:20:01 +00:00
|
|
|
tree.getSelectionModel().setSelectionMode
|
|
|
|
(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the path from node to the root. Package private for use in inner
|
|
|
|
* classes.
|
|
|
|
*
|
|
|
|
* @param node
|
|
|
|
* the node to get the path to
|
|
|
|
* @param depth
|
|
|
|
* the depth of the tree to return a path for
|
|
|
|
* @return an array of tree nodes that represent the path to node.
|
|
|
|
*/
|
|
|
|
Object[] getPathToRoot(Object node, int depth)
|
|
|
|
{
|
|
|
|
if (node == null)
|
|
|
|
{
|
|
|
|
if (depth == 0)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
return new Object[depth];
|
|
|
|
}
|
|
|
|
|
2005-11-15 23:20:01 +00:00
|
|
|
Object[] path = getPathToRoot(getParent(treeModel.getRoot(), node), depth + 1);
|
2005-09-23 21:31:04 +00:00
|
|
|
path[path.length - depth - 1] = node;
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the level of the node in the tree.
|
|
|
|
*
|
2005-11-15 23:20:01 +00:00
|
|
|
* @param node -
|
2005-09-23 21:31:04 +00:00
|
|
|
* current node
|
|
|
|
* @return the number of the level
|
|
|
|
*/
|
|
|
|
int getLevel(Object node)
|
|
|
|
{
|
|
|
|
int count = -1;
|
2005-11-18 00:59:33 +00:00
|
|
|
|
2005-09-23 21:31:04 +00:00
|
|
|
Object current = node;
|
|
|
|
|
2005-11-18 00:59:33 +00:00
|
|
|
if (treeModel != null)
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
Object root = treeModel.getRoot();
|
|
|
|
if (!tree.isRootVisible() && tree.isExpanded(new TreePath(root)))
|
|
|
|
count--;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
current = getParent(root, current);
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
while (current != null);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Draws a vertical line using the given graphic context
|
|
|
|
*
|
|
|
|
* @param g
|
|
|
|
* is the graphic context
|
|
|
|
* @param c
|
|
|
|
* is the component the new line will belong to
|
|
|
|
* @param x
|
|
|
|
* is the horizonal position
|
|
|
|
* @param top
|
|
|
|
* specifies the top of the line
|
|
|
|
* @param bottom
|
|
|
|
* specifies the bottom of the line
|
|
|
|
*/
|
|
|
|
protected void paintVerticalLine(Graphics g, JComponent c, int x, int top,
|
|
|
|
int bottom)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
// FIXME: Check if drawing a dashed line or not.
|
|
|
|
g.setColor(getHashColor());
|
2005-09-23 21:31:04 +00:00
|
|
|
g.drawLine(x, top, x, bottom);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Draws a horizontal line using the given graphic context
|
|
|
|
*
|
|
|
|
* @param g
|
|
|
|
* is the graphic context
|
|
|
|
* @param c
|
|
|
|
* is the component the new line will belong to
|
|
|
|
* @param y
|
|
|
|
* is the vertical position
|
|
|
|
* @param left
|
|
|
|
* specifies the left point of the line
|
|
|
|
* @param right
|
|
|
|
* specifies the right point of the line
|
|
|
|
*/
|
|
|
|
protected void paintHorizontalLine(Graphics g, JComponent c, int y, int left,
|
|
|
|
int right)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
// FIXME: Check if drawing a dashed line or not.
|
|
|
|
g.setColor(getHashColor());
|
2005-09-23 21:31:04 +00:00
|
|
|
g.drawLine(left, y, right, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Draws an icon at around a specific position
|
|
|
|
*
|
|
|
|
* @param c
|
|
|
|
* is the component the new line will belong to
|
|
|
|
* @param g
|
|
|
|
* is the graphic context
|
|
|
|
* @param icon
|
|
|
|
* is the icon which will be drawn
|
|
|
|
* @param x
|
|
|
|
* is the center position in x-direction
|
|
|
|
* @param y
|
2005-11-18 00:59:33 +00:00
|
|
|
* is the center position in y-direction
|
2005-09-23 21:31:04 +00:00
|
|
|
*/
|
2005-11-15 23:20:01 +00:00
|
|
|
protected void drawCentered(Component c, Graphics g, Icon icon, int x, int y)
|
2005-09-23 21:31:04 +00:00
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
x -= icon.getIconWidth() / 2;
|
|
|
|
y -= icon.getIconHeight() / 2;
|
|
|
|
|
|
|
|
if (x < 0)
|
|
|
|
x = 0;
|
|
|
|
if (y < 0)
|
|
|
|
y = 0;
|
|
|
|
|
|
|
|
icon.paintIcon(c, g, x, y);
|
2005-09-23 21:31:04 +00:00
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Draws a dashed horizontal line.
|
|
|
|
*
|
|
|
|
* @param g - the graphics configuration.
|
|
|
|
* @param y - the y location to start drawing at
|
|
|
|
* @param x1 - the x location to start drawing at
|
|
|
|
* @param x2 - the x location to finish drawing at
|
|
|
|
*/
|
|
|
|
protected void drawDashedHorizontalLine(Graphics g, int y, int x1, int x2)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
g.setColor(getHashColor());
|
2005-11-15 23:20:01 +00:00
|
|
|
for (int i = x1; i < x2; i += 2)
|
|
|
|
g.drawLine(i, y, i + 1, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Draws a dashed vertical line.
|
|
|
|
*
|
|
|
|
* @param g - the graphics configuration.
|
|
|
|
* @param x - the x location to start drawing at
|
|
|
|
* @param y1 - the y location to start drawing at
|
|
|
|
* @param y2 - the y location to finish drawing at
|
|
|
|
*/
|
|
|
|
protected void drawDashedVerticalLine(Graphics g, int x, int y1, int y2)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
g.setColor(getHashColor());
|
2005-11-15 23:20:01 +00:00
|
|
|
for (int i = y1; i < y2; i += 2)
|
|
|
|
g.drawLine(x, i, x, i + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Paints the expand (toggle) part of a row. The receiver should NOT modify
|
|
|
|
* clipBounds, or insets.
|
|
|
|
*
|
|
|
|
* @param g - the graphics configuration
|
|
|
|
* @param clipBounds -
|
|
|
|
* @param insets -
|
|
|
|
* @param bounds - bounds of expand control
|
|
|
|
* @param path - path to draw control for
|
|
|
|
* @param row - row to draw control for
|
|
|
|
* @param isExpanded - is the row expanded
|
|
|
|
* @param hasBeenExpanded - has the row already been expanded
|
|
|
|
* @param isLeaf - is the path a leaf
|
|
|
|
*/
|
|
|
|
protected void paintExpandControl(Graphics g, Rectangle clipBounds,
|
|
|
|
Insets insets, Rectangle bounds,
|
|
|
|
TreePath path, int row,
|
|
|
|
boolean isExpanded, boolean hasBeenExpanded,
|
|
|
|
boolean isLeaf)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
if (shouldPaintExpandControl(path, row, isExpanded, hasBeenExpanded, isLeaf))
|
|
|
|
{
|
|
|
|
Icon icon = getCurrentControlIcon(path);
|
|
|
|
int iconW = icon.getIconWidth();
|
|
|
|
int x = bounds.x - rightChildIndent + iconW/2;
|
|
|
|
if (x + iconW > bounds.x)
|
|
|
|
x = bounds.x - rightChildIndent - gap;
|
|
|
|
icon.paintIcon(tree, g, x, bounds.y + bounds.height/2 - icon.getIconHeight()/2);
|
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Paints the horizontal part of the leg. The receiver should NOT modify
|
|
|
|
* clipBounds, or insets.
|
|
|
|
* NOTE: parentRow can be -1 if the root is not visible.
|
|
|
|
*
|
|
|
|
* @param g - the graphics configuration
|
|
|
|
* @param clipBounds -
|
|
|
|
* @param insets -
|
2005-11-18 00:59:33 +00:00
|
|
|
* @param bounds - bounds of the cell
|
|
|
|
* @param path - path to draw leg for
|
|
|
|
* @param row - row to start drawing at
|
2005-11-15 23:20:01 +00:00
|
|
|
* @param isExpanded - is the row expanded
|
|
|
|
* @param hasBeenExpanded - has the row already been expanded
|
|
|
|
* @param isLeaf - is the path a leaf
|
|
|
|
*/
|
|
|
|
protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds,
|
|
|
|
Insets insets, Rectangle bounds,
|
|
|
|
TreePath path, int row,
|
|
|
|
boolean isExpanded, boolean hasBeenExpanded,
|
|
|
|
boolean isLeaf)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
if (row != 0)
|
|
|
|
paintHorizontalLine(g, tree, bounds.y + bounds.height/2, bounds.x - gap - 2,
|
|
|
|
bounds.x);
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Paints the vertical part of the leg. The receiver should NOT modify
|
|
|
|
* clipBounds, insets.
|
|
|
|
*
|
|
|
|
* @param g - the graphics configuration.
|
|
|
|
* @param clipBounds -
|
|
|
|
* @param insets -
|
|
|
|
* @param path - the path to draw the vertical part for.
|
|
|
|
*/
|
|
|
|
protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds,
|
|
|
|
Insets insets, TreePath path)
|
|
|
|
{
|
2005-11-18 00:59:33 +00:00
|
|
|
int max = tree.getVisibleRowCount();
|
|
|
|
for (int i = 0; i < max; i++)
|
|
|
|
{
|
|
|
|
Object curr = path.getPathComponent(i);
|
|
|
|
TreePath currPath = new TreePath(getPathToRoot(curr, 0));
|
|
|
|
int numChild = treeModel.getChildCount(curr);
|
|
|
|
if (numChild > 0 && tree.isExpanded(currPath))
|
|
|
|
{
|
|
|
|
Rectangle bounds = getPathBounds(tree, currPath);
|
|
|
|
Rectangle lastChildBounds = getPathBounds(tree,
|
|
|
|
new TreePath(getPathToRoot(
|
|
|
|
treeModel.getChild(curr, numChild - 1),
|
|
|
|
0)));
|
|
|
|
paintVerticalLine(g, tree, bounds.x + gap + 2, bounds.y +
|
|
|
|
bounds.height - 2, lastChildBounds.y +
|
|
|
|
lastChildBounds.height/2);
|
|
|
|
}
|
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Paints the renderer part of a row. The receiver should NOT modify clipBounds,
|
|
|
|
* or insets.
|
|
|
|
*
|
|
|
|
* @param g - the graphics configuration
|
|
|
|
* @param clipBounds -
|
|
|
|
* @param insets -
|
|
|
|
* @param bounds - bounds of expand control
|
|
|
|
* @param path - path to draw control for
|
|
|
|
* @param row - row to draw control for
|
|
|
|
* @param isExpanded - is the row expanded
|
|
|
|
* @param hasBeenExpanded - has the row already been expanded
|
|
|
|
* @param isLeaf - is the path a leaf
|
|
|
|
*/
|
|
|
|
protected void paintRow(Graphics g, Rectangle clipBounds,
|
|
|
|
Insets insets, Rectangle bounds,
|
|
|
|
TreePath path, int row,
|
|
|
|
boolean isExpanded, boolean hasBeenExpanded,
|
|
|
|
boolean isLeaf)
|
|
|
|
{
|
|
|
|
boolean selected = tree.isPathSelected(path);
|
|
|
|
boolean hasIcons = false;
|
|
|
|
Object node = path.getLastPathComponent();
|
|
|
|
|
|
|
|
if (tree.isVisible(path))
|
|
|
|
{
|
|
|
|
if (!validCachedPreferredSize)
|
|
|
|
updateCachedPreferredSize();
|
2005-11-18 00:59:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
paintExpandControl(g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf);
|
|
|
|
|
|
|
|
if (row != 0)
|
|
|
|
bounds.x += gap;
|
2005-11-15 23:20:01 +00:00
|
|
|
bounds.width = preferredSize.width + bounds.x;
|
|
|
|
|
|
|
|
if (editingComponent != null && editingPath != null && isEditing(tree)
|
|
|
|
&& node.equals(editingPath.getLastPathComponent()))
|
|
|
|
{
|
|
|
|
rendererPane.paintComponent(g, editingComponent.getParent(), null,
|
|
|
|
bounds);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TreeCellRenderer dtcr = tree.getCellRenderer();
|
|
|
|
if (dtcr == null)
|
|
|
|
dtcr = createDefaultCellRenderer();
|
|
|
|
|
|
|
|
Component c = dtcr.getTreeCellRendererComponent(tree, node,
|
|
|
|
selected, isExpanded, isLeaf, row, tree.hasFocus());
|
|
|
|
rendererPane.paintComponent(g, c, c.getParent(), bounds);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepares for the UI to uninstall.
|
|
|
|
*/
|
|
|
|
protected void prepareForUIUninstall()
|
|
|
|
{
|
|
|
|
// TODO: Implement this properly.
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the expand (toggle) control should be drawn for the
|
|
|
|
* specified row.
|
|
|
|
*
|
|
|
|
* @param path - current path to check for.
|
|
|
|
* @param row - current row to check for.
|
|
|
|
* @param isExpanded - true if the path is expanded
|
|
|
|
* @param hasBeenExpanded - true if the path has been expanded already
|
|
|
|
* @param isLeaf - true if the row is a lead
|
|
|
|
*/
|
|
|
|
protected boolean shouldPaintExpandControl(TreePath path, int row,
|
|
|
|
boolean isExpanded,
|
|
|
|
boolean hasBeenExpanded,
|
|
|
|
boolean isLeaf)
|
|
|
|
{
|
|
|
|
Object node = path.getLastPathComponent();
|
2005-11-18 00:59:33 +00:00
|
|
|
return (!isLeaf && getLevel(node) != 0 && hasControlIcons());
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates the cached current TreePath of all visible
|
|
|
|
* nodes in the tree.
|
|
|
|
*/
|
|
|
|
void updateCurrentVisiblePath()
|
|
|
|
{
|
|
|
|
if (treeModel == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Object next = treeModel.getRoot();
|
2005-11-18 00:59:33 +00:00
|
|
|
TreePath rootPath = new TreePath(next);
|
|
|
|
Rectangle bounds = getPathBounds(tree, rootPath);
|
2005-11-15 23:20:01 +00:00
|
|
|
|
|
|
|
// If root is not a valid size to be visible, or is
|
|
|
|
// not visible and the tree is expanded, then the next node acts
|
|
|
|
// as the root
|
2005-11-18 00:59:33 +00:00
|
|
|
if ((bounds.width == 0 && bounds.height == 0) || (!isRootVisible()
|
2005-11-15 23:20:01 +00:00
|
|
|
&& tree.isExpanded(new TreePath(next))))
|
2005-11-18 00:59:33 +00:00
|
|
|
{
|
2005-11-15 23:20:01 +00:00
|
|
|
next = getNextNode(next);
|
2005-11-18 00:59:33 +00:00
|
|
|
rootPath = new TreePath(next);
|
|
|
|
}
|
2005-11-15 23:20:01 +00:00
|
|
|
|
|
|
|
Object root = next;
|
|
|
|
TreePath current = null;
|
|
|
|
while (next != null)
|
|
|
|
{
|
|
|
|
if (current == null)
|
2005-11-18 00:59:33 +00:00
|
|
|
current = rootPath;
|
|
|
|
else
|
|
|
|
current = current.pathByAddingChild(next);
|
|
|
|
|
2005-11-15 23:20:01 +00:00
|
|
|
do
|
|
|
|
{
|
|
|
|
TreePath path = new TreePath(getPathToRoot(next, 0));
|
|
|
|
if ((tree.isVisible(path) && tree.isExpanded(path))
|
|
|
|
|| treeModel.isLeaf(next))
|
|
|
|
next = getNextNode(next);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Object pNext = next;
|
|
|
|
next = getNextSibling(pNext);
|
|
|
|
// if no next sibling, check parent's next sibling.
|
|
|
|
if (next == null)
|
|
|
|
{
|
|
|
|
Object parent = getParent(root, pNext);
|
|
|
|
while (next == null && parent != null)
|
|
|
|
{
|
|
|
|
next = getNextSibling(parent);
|
|
|
|
if (next == null)
|
2005-11-18 00:59:33 +00:00
|
|
|
parent = getParent(root, parent);
|
2005-11-15 23:20:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (next != null &&
|
|
|
|
!tree.isVisible(new TreePath(getPathToRoot(next, 0))));
|
|
|
|
}
|
|
|
|
|
|
|
|
currentVisiblePath = current;
|
2005-11-18 00:59:33 +00:00
|
|
|
tree.setVisibleRowCount(getRowCount(tree));
|
2005-11-15 23:20:01 +00:00
|
|
|
|
|
|
|
if (tree.getSelectionModel() != null && tree.getSelectionCount() == 0 &&
|
|
|
|
currentVisiblePath != null)
|
|
|
|
selectPath(tree, new TreePath(getPathToRoot(currentVisiblePath.
|
|
|
|
getPathComponent(0), 0)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get next visible node in the currentVisiblePath. Package private for use in
|
|
|
|
* inner classes.
|
|
|
|
*
|
|
|
|
* @param node
|
|
|
|
* current node
|
|
|
|
* @return the next visible node in the JTree. Return null if there are no
|
|
|
|
* more.
|
|
|
|
*/
|
|
|
|
Object getNextVisibleNode(Object node)
|
|
|
|
{
|
|
|
|
if (currentVisiblePath != null)
|
|
|
|
{
|
|
|
|
Object[] nodes = currentVisiblePath.getPath();
|
|
|
|
int i = 0;
|
|
|
|
while (i < nodes.length && !node.equals(nodes[i]))
|
|
|
|
i++;
|
|
|
|
// return the next node
|
|
|
|
if (i+1 < nodes.length)
|
|
|
|
return nodes[i+1];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2005-09-23 21:31:04 +00:00
|
|
|
} // BasicTreeUI
|