home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 1998 November
/
Chip_1998-11_cd.bin
/
tema
/
Cafe
/
jfc.bin
/
VisibleTreeNode.java
< prev
next >
Wrap
Text File
|
1998-02-26
|
17KB
|
602 lines
/*
* @(#)VisibleTreeNode.java 1.5 98/02/02
*
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
*/
package com.sun.java.swing.plaf.basic;
import com.sun.java.swing.tree.TreeModel;
import com.sun.java.swing.tree.DefaultMutableTreeNode;
import com.sun.java.swing.tree.MutableTreeNode;
import com.sun.java.swing.tree.TreePath;
import com.sun.java.swing.tree.TreeSelectionModel;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.util.Enumeration;
import java.util.Vector;
/**
* VisibleTreeNode is used by AbstractTreeUI to keep track of each of
* the nodes that have been expanded. This will also cache the preferred
* size of the value this represents.<p>
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*
* @version 1.5 02/02/98
* @author Scott Violet
*/
public class VisibleTreeNode extends DefaultMutableTreeNode {
/** AbstractTreeUI this was created for. */
protected AbstractTreeUI treeUI;
/** Preferred size needed to draw the user object. */
protected Dimension preferredSize;
/** Y location that the user object will be drawn at. */
protected int yOrigin;
/** Is this node currently expanded? */
protected boolean expanded;
/** Has this node been expanded at least once? */
protected boolean hasBeenExpanded;
/** Is this node in a tree? */
protected boolean isValid;
public static final Dimension EMPTY_SIZE = new Dimension(0, 0);
public VisibleTreeNode(AbstractTreeUI treeUI, Object value, int index) {
super(value);
this.treeUI = treeUI;
isValid = true;
updatePreferredSize(index);
}
/**
* Passes this message on to super and if the newParent is
* null, meaning we're no longer in the tree. markInvalid
* is messaged.
*/
public void setParent(MutableTreeNode newParent) {
super.setParent(newParent);
if(newParent == null)
markInvalid();
}
/**
* Marks this node and all its children as invalid.
*/
void markInvalid() {
isValid = false;
if(children != null) {
for(int counter = children.size() - 1; counter >= 0;
counter--)
((VisibleTreeNode)children.elementAt(counter))
.markInvalid();
}
}
/**
* Sets y origin the user object will be drawn at to
* <I>newYOrigin</I>.
*/
protected void setYOrigin(int newYOrigin) {
yOrigin = newYOrigin;
}
/**
* Returns the y origin the user object will be drawn at.
*/
public int getYOrigin() {
if(treeUI.isFixedRowHeight()) {
int aRow = getRow();
if(aRow == -1)
return -1;
return treeUI.getRowHeight() * aRow;
}
return yOrigin;
}
/**
* Shifts the y origin by <code>offset</code>.
*/
protected void shiftYOriginBy(int offset) {
yOrigin += offset;
}
public void updatePreferredSize() {
updatePreferredSize(-1);
}
/**
* Updates the preferred size by asking the current renderer
* for the Dimension needed to draw the user object this
* instance represents.
*/
protected void updatePreferredSize(int index) {
preferredSize = treeUI.getSizeOfNode(this, index);
if(preferredSize == null) {
preferredSize = EMPTY_SIZE;
treeUI.updateNodeSizes = true;
}
else if(preferredSize.height == 0) {
treeUI.updateNodeSizes = true;
}
int rh = treeUI.getRowHeight();
if(rh > 0)
preferredSize.height = rh;
}
/**
* Returns true if this node has a valid size.
*/
public boolean hasValidSize() {
return (preferredSize == null || preferredSize.height == 0);
}
/**
* Returns the preferred size needed to draw the value this
* node represents. Will always return a valid dimenion, but
* dimension may be 0, 0
*/
public Dimension getPreferredSize() {
return preferredSize;
}
/**
* Returns the location and size of this node.
*/
public Rectangle getNodeBounds() {
Dimension pSize = getPreferredSize();
return new Rectangle(treeUI.getXOriginOfNode(this), getYOrigin(),
pSize.width, pSize.height);
}
/**
* The highest visible nodes have a depth of 0.
*/
public int getVisibleLevel() {
if (treeUI.isRootVisible()) {
return getLevel();
} else {
return getLevel()-1;
}
}
/**
* Returns the row of the receiver.
*/
public int getRow() {
return treeUI.visibleNodes.indexOf(this);
}
/**
* Returns true if this node has been expanded at least once.
*/
public boolean hasBeenExpanded() {
return hasBeenExpanded;
}
/**
* Returns true if the receiver has been expanded.
*/
public boolean isExpanded() {
return expanded;
}
/**
* Returns true if the receiver is selected.
*/
public boolean isSelected() {
return treeUI.isSelectedIndex(getRow());
}
/**
* Returns true if the receiver is a leaf.
*/
public boolean isLeaf() {
return treeUI.getModel().isLeaf(this.getValue());
}
/**
* Returns the last visible node that is a child of this
* instance.
*/
public VisibleTreeNode getLastVisibleNode() {
VisibleTreeNode node = this;
while(node.isExpanded() && node.getChildCount() > 0)
node = (VisibleTreeNode)node.getLastChild();
return node;
}
/**
* If createIfNeeded is true the children will be loaded from
* the model if they haven't already been loaded. The children
* are then loaded, regardless of whether or not this
* node is currently expanded.
*/
public Enumeration getLoadedChildren(boolean createIfNeeded) {
if(!createIfNeeded || hasBeenExpanded)
return super.children();
VisibleTreeNode newNode;
Object realNode = getValue();
TreeModel treeModel = treeUI.getModel();
int count = treeModel.getChildCount(realNode);
hasBeenExpanded = true;
int childRow = getRow();
if(childRow == -1) {
for (int i = 0; i < count; i++) {
newNode = treeUI.createNodeForValue
(treeModel.getChild(realNode, i), -1);
this.add(newNode);
}
}
else {
childRow++;
for (int i = 0; i < count; i++) {
newNode = treeUI.createNodeForValue
(treeModel.getChild(realNode, i), childRow++);
this.add(newNode);
}
}
return super.children();
}
/**
* Returns the children of the receiver.
* If the receiver is not currently expanded, this will return an
* empty enumeration.
*/
public Enumeration children() {
if (!this.isExpanded()) {
return DefaultMutableTreeNode.EMPTY_ENUMERATION;
} else {
return super.children();
}
}
/**
* Returns true if the receiver is currently visible.
*/
public boolean isVisible() {
return treeUI.visibleNodes.contains(this);
}
/**
* Messaged when the child count has changed and this node hasn't
* yet been expanded. This is meant to be used by subclassers.
*/
public void modelChildCountChanged() {
}
/**
* Returns the number of children this will have. If the children
* have not yet been loaded, this messages the model.
*/
public int getModelChildCount() {
if(hasBeenExpanded)
return super.getChildCount();
return treeUI.getModel().getChildCount(getValue());
}
/**
* Returns the number of visible children. This is a deep search.
*/
public int visibleChildCount() {
/* This instance is included in the preorder enumeration, so
start with -1. */
int childCount = -1;
Enumeration cursor = preorderEnumeration();
while(cursor.hasMoreElements()) {
childCount++;
cursor.nextElement();
}
return childCount;
}
/**
* Toggles the receiver between expanded and collapsed.
*/
public void toggleExpanded() {
if (isExpanded()) {
collapse();
} else {
expand();
}
}
/**
* Messaged from expand and collapse. This is meant for subclassers
* that may wish to do something interesting with this.
*/
protected void didAdjustTree() {
}
/**
* Expands the receiver.
*/
public void expand() {
expand(true);
}
/**
* Expands this node in the tree. This will load the children
* from the treeModel if this node has not previously been
* expanded. If <I>adjustTree</I> is true the tree and selection
* are updated accordingly.
*/
protected void expand(boolean adjustTree) {
if (!isExpanded() && !isLeaf()) {
Vector visibleNodes = getVisibleNodes();
boolean isFixed = treeUI.isFixedRowHeight();
expanded = true;
int originalRow = getRow();
if (!hasBeenExpanded) {
VisibleTreeNode newNode;
Object realNode = getValue();
TreeModel treeModel = treeUI.getModel();
int count = treeModel.getChildCount(realNode);
hasBeenExpanded = true;
if(originalRow == -1) {
for (int i = 0; i < count; i++) {
newNode = treeUI.createNodeForValue(treeModel.getChild
(realNode, i), -1);
this.add(newNode);
}
}
else {
int offset = originalRow + 1;
for (int i = 0; i < count; i++) {
newNode = treeUI.createNodeForValue(treeModel.getChild
(realNode, i), offset);
this.add(newNode);
}
}
}
int i = originalRow;
Enumeration cursor = preorderEnumeration();
cursor.nextElement(); // don't add me, I'm already in
int newYOrigin;
if(isFixed)
newYOrigin = 0;
else if(this == treeUI.treeCacheRoot && !treeUI.isRootVisible())
newYOrigin = 0;
else
newYOrigin = getYOrigin() + this.getPreferredSize().height;
VisibleTreeNode aNode;
if(!isFixed)
{
boolean updateNodeSizes = treeUI.updateNodeSizes;
while (cursor.hasMoreElements()) {
aNode = (VisibleTreeNode)cursor.nextElement();
if(!updateNodeSizes && !aNode.hasValidSize())
aNode.updatePreferredSize(i + 1);
aNode.setYOrigin(newYOrigin);
newYOrigin += aNode.getPreferredSize().height;
visibleNodes.insertElementAt(aNode, ++i);
}
}
else
{
while (cursor.hasMoreElements()) {
aNode = (VisibleTreeNode)cursor.nextElement();
visibleNodes.insertElementAt(aNode, ++i);
}
}
int endRow = i;
if(originalRow != endRow && adjustTree)
{
/* Adjust the Y origin of any nodes following this row. */
if(!isFixed && getChildCount() > 0 && ++i < treeUI.getRowCount())
{
int counter;
int heightDiff = newYOrigin -
(getYOrigin() + this.getPreferredSize().height);
for(counter = visibleNodes.size() - 1;counter >= i;
counter--)
((VisibleTreeNode)visibleNodes.elementAt(counter)).
shiftYOriginBy(heightDiff);
}
didAdjustTree();
treeUI.visibleNodesChanged();
}
treeUI.pathWasExpanded(getTreePath());
TreeSelectionModel treeSelectionModel = treeUI.getSelectionModel();
/* Update the selection, if the list selection model wants
to select all the items that were added, then we need
to update the list selection model. */
if(treeSelectionModel != null) {
if(originalRow != -1 && originalRow < endRow &&
treeSelectionModel.isRowSelected(originalRow) &&
treeSelectionModel.isRowSelected(originalRow + 1)) {
TreePath[] toSelect;
toSelect = new TreePath[endRow - originalRow];
for(i = endRow; i > originalRow; i--)
toSelect[endRow - i] = treeUI.getNode(i).getTreePath();
treeSelectionModel.addSelectionPaths(toSelect);
}
else
treeSelectionModel.resetRowSelection();
}
}
}
/**
* Collapsed the receiver.
*/
public void collapse() {
collapse(true);
}
/**
* Collapses this node in the tree. If <I>adjustTree</I> is
* true the tree and selection are updated accordingly.
*/
protected void collapse(boolean adjustTree) {
if (isExpanded()) {
Vector selectedPaths = null;
Enumeration cursor = preorderEnumeration();
cursor.nextElement(); // don't remove me, I'm still visible
int rowsDeleted = 0;
boolean isFixed = treeUI.isFixedRowHeight();
int lastYEnd;
if(isFixed)
lastYEnd = 0;
else
lastYEnd = this.getPreferredSize().height + getYOrigin();
int startYEnd = lastYEnd;
int myRow = getRow();
Vector visibleNodes = getVisibleNodes();
TreeSelectionModel treeSelectionModel;
treeSelectionModel = treeUI.getSelectionModel();
if(!isFixed)
{
while(cursor.hasMoreElements()) {
VisibleTreeNode node = (VisibleTreeNode)cursor.
nextElement();
if (visibleNodes.contains(node)) {
rowsDeleted++;
if(treeSelectionModel != null &&
treeSelectionModel.isRowSelected
(rowsDeleted + myRow)) {
if(selectedPaths == null)
selectedPaths = new Vector();
selectedPaths.addElement(node.getTreePath());
}
visibleNodes.removeElement(node);
lastYEnd = node.getYOrigin() +
node.getPreferredSize().height;
}
}
}
else
{
while(cursor.hasMoreElements()) {
VisibleTreeNode node = (VisibleTreeNode)cursor.
nextElement();
if (visibleNodes.contains(node)) {
rowsDeleted++;
if(treeSelectionModel != null &&
treeSelectionModel.isRowSelected
(rowsDeleted + myRow)) {
if(selectedPaths == null)
selectedPaths = new Vector();
selectedPaths.addElement(node.getTreePath());
}
visibleNodes.removeElement(node);
}
}
}
if(rowsDeleted > 0 && adjustTree && myRow != -1)
{
/* Adjust the Y origin of any rows following this one. */
if(!isFixed && (myRow + 1) < treeUI.getRowCount() &&
startYEnd != lastYEnd)
{
int counter, maxCounter, shiftAmount;
shiftAmount = startYEnd - lastYEnd;
for(counter = myRow + 1, maxCounter =
visibleNodes.size();
counter < maxCounter;counter++)
((VisibleTreeNode)visibleNodes.elementAt(counter))
.shiftYOriginBy(shiftAmount);
}
expanded = false;
didAdjustTree();
treeUI.visibleNodesChanged();
}
else
expanded = false;
treeUI.pathWasCollapsed(getTreePath());
/* Adjust the selections. */
if(treeSelectionModel != null && rowsDeleted > 0 &&
myRow != -1) {
if(selectedPaths != null) {
int maxCounter = selectedPaths.size();
TreePath[] treePaths = new TreePath[maxCounter];
selectedPaths.copyInto(treePaths);
treeSelectionModel.removeSelectionPaths(treePaths);
treeSelectionModel.addSelectionPath(getTreePath());
}
else
treeSelectionModel.resetRowSelection();
}
}
}
/**
* Returns the value the receiver is representing. This is a cover
* for getUserObject.
*/
public Object getValue() {
return getUserObject();
}
/**
* Returns a TreePath instance for this node.
*/
protected TreePath getTreePath() {
return treeUI.createTreePathFor(this);
}
/**
* Returns the visibleNodes instance variable of the tree the receiver
* is contained in.
*/
protected Vector getVisibleNodes() {
return treeUI.visibleNodes;
}
/**
* Messages the tree with updateYLocationsFrom(row).
*/
protected void updateTreeYLocationsFrom(int row) {
treeUI.updateYLocationsFrom(row);
}
}