0N/A/*
2362N/A * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage javax.swing.tree;
0N/A
0N/Aimport javax.swing.event.TreeModelEvent;
0N/Aimport java.awt.Dimension;
0N/Aimport java.awt.Rectangle;
0N/Aimport java.util.Enumeration;
0N/Aimport java.util.Hashtable;
0N/Aimport java.util.NoSuchElementException;
0N/Aimport java.util.Stack;
0N/A
0N/A/**
0N/A * NOTE: This will become more open in a future release.
0N/A * <p>
0N/A * <strong>Warning:</strong>
0N/A * Serialized objects of this class will not be compatible with
0N/A * future Swing releases. The current serialization support is
0N/A * appropriate for short term storage or RMI between applications running
0N/A * the same version of Swing. As of 1.4, support for long term storage
0N/A * of all JavaBeans<sup><font size="-2">TM</font></sup>
0N/A * has been added to the <code>java.beans</code> package.
0N/A * Please see {@link java.beans.XMLEncoder}.
0N/A *
0N/A * @author Scott Violet
0N/A */
0N/A
0N/Apublic class FixedHeightLayoutCache extends AbstractLayoutCache {
0N/A /** Root node. */
0N/A private FHTreeStateNode root;
0N/A
0N/A /** Number of rows currently visible. */
0N/A private int rowCount;
0N/A
0N/A /**
0N/A * Used in getting sizes for nodes to avoid creating a new Rectangle
0N/A * every time a size is needed.
0N/A */
0N/A private Rectangle boundsBuffer;
0N/A
0N/A /**
0N/A * Maps from TreePath to a FHTreeStateNode.
0N/A */
625N/A private Hashtable<TreePath, FHTreeStateNode> treePathMapping;
0N/A
0N/A /**
0N/A * Used for getting path/row information.
0N/A */
0N/A private SearchInfo info;
0N/A
625N/A private Stack<Stack<TreePath>> tempStacks;
0N/A
0N/A
0N/A public FixedHeightLayoutCache() {
0N/A super();
625N/A tempStacks = new Stack<Stack<TreePath>>();
0N/A boundsBuffer = new Rectangle();
625N/A treePathMapping = new Hashtable<TreePath, FHTreeStateNode>();
0N/A info = new SearchInfo();
0N/A setRowHeight(1);
0N/A }
0N/A
0N/A /**
0N/A * Sets the TreeModel that will provide the data.
0N/A *
0N/A * @param newModel the TreeModel that is to provide the data
0N/A */
0N/A public void setModel(TreeModel newModel) {
0N/A super.setModel(newModel);
0N/A rebuild(false);
0N/A }
0N/A
0N/A /**
0N/A * Determines whether or not the root node from
0N/A * the TreeModel is visible.
0N/A *
0N/A * @param rootVisible true if the root node of the tree is to be displayed
0N/A * @see #rootVisible
0N/A */
0N/A public void setRootVisible(boolean rootVisible) {
0N/A if(isRootVisible() != rootVisible) {
0N/A super.setRootVisible(rootVisible);
0N/A if(root != null) {
0N/A if(rootVisible) {
0N/A rowCount++;
0N/A root.adjustRowBy(1);
0N/A }
0N/A else {
0N/A rowCount--;
0N/A root.adjustRowBy(-1);
0N/A }
0N/A visibleNodesChanged();
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets the height of each cell. If rowHeight is less than or equal to
0N/A * 0 this will throw an IllegalArgumentException.
0N/A *
0N/A * @param rowHeight the height of each cell, in pixels
0N/A */
0N/A public void setRowHeight(int rowHeight) {
0N/A if(rowHeight <= 0)
0N/A throw new IllegalArgumentException("FixedHeightLayoutCache only supports row heights greater than 0");
0N/A if(getRowHeight() != rowHeight) {
0N/A super.setRowHeight(rowHeight);
0N/A visibleNodesChanged();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the number of visible rows.
0N/A */
0N/A public int getRowCount() {
0N/A return rowCount;
0N/A }
0N/A
0N/A /**
0N/A * Does nothing, FixedHeightLayoutCache doesn't cache width, and that
0N/A * is all that could change.
0N/A */
0N/A public void invalidatePathBounds(TreePath path) {
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Informs the TreeState that it needs to recalculate all the sizes
0N/A * it is referencing.
0N/A */
0N/A public void invalidateSizes() {
0N/A // Nothing to do here, rowHeight still same, which is all
0N/A // this is interested in, visible region may have changed though.
0N/A visibleNodesChanged();
0N/A }
0N/A
0N/A /**
0N/A * Returns true if the value identified by row is currently expanded.
0N/A */
0N/A public boolean isExpanded(TreePath path) {
0N/A if(path != null) {
0N/A FHTreeStateNode lastNode = getNodeForPath(path, true, false);
0N/A
0N/A return (lastNode != null && lastNode.isExpanded());
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Returns a rectangle giving the bounds needed to draw path.
0N/A *
0N/A * @param path a TreePath specifying a node
0N/A * @param placeIn a Rectangle object giving the available space
0N/A * @return a Rectangle object specifying the space to be used
0N/A */
0N/A public Rectangle getBounds(TreePath path, Rectangle placeIn) {
0N/A if(path == null)
0N/A return null;
0N/A
0N/A FHTreeStateNode node = getNodeForPath(path, true, false);
0N/A
0N/A if(node != null)
0N/A return getBounds(node, -1, placeIn);
0N/A
0N/A // node hasn't been created yet.
0N/A TreePath parentPath = path.getParentPath();
0N/A
0N/A node = getNodeForPath(parentPath, true, false);
0N/A if (node != null && node.isExpanded()) {
0N/A int childIndex = treeModel.getIndexOfChild
0N/A (parentPath.getLastPathComponent(),
0N/A path.getLastPathComponent());
0N/A
0N/A if(childIndex != -1)
0N/A return getBounds(node, childIndex, placeIn);
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Returns the path for passed in row. If row is not visible
0N/A * null is returned.
0N/A */
0N/A public TreePath getPathForRow(int row) {
0N/A if(row >= 0 && row < getRowCount()) {
0N/A if(root.getPathForRow(row, getRowCount(), info)) {
0N/A return info.getPath();
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Returns the row that the last item identified in path is visible
0N/A * at. Will return -1 if any of the elements in path are not
0N/A * currently visible.
0N/A */
0N/A public int getRowForPath(TreePath path) {
0N/A if(path == null || root == null)
0N/A return -1;
0N/A
0N/A FHTreeStateNode node = getNodeForPath(path, true, false);
0N/A
0N/A if(node != null)
0N/A return node.getRow();
0N/A
0N/A TreePath parentPath = path.getParentPath();
0N/A
0N/A node = getNodeForPath(parentPath, true, false);
0N/A if(node != null && node.isExpanded()) {
0N/A return node.getRowToModelIndex(treeModel.getIndexOfChild
0N/A (parentPath.getLastPathComponent(),
0N/A path.getLastPathComponent()));
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A /**
0N/A * Returns the path to the node that is closest to x,y. If
0N/A * there is nothing currently visible this will return null, otherwise
0N/A * it'll always return a valid path. If you need to test if the
0N/A * returned object is exactly at x, y you should get the bounds for
0N/A * the returned path and test x, y against that.
0N/A */
0N/A public TreePath getPathClosestTo(int x, int y) {
0N/A if(getRowCount() == 0)
0N/A return null;
0N/A
0N/A int row = getRowContainingYLocation(y);
0N/A
0N/A return getPathForRow(row);
0N/A }
0N/A
0N/A /**
0N/A * Returns the number of visible children for row.
0N/A */
0N/A public int getVisibleChildCount(TreePath path) {
0N/A FHTreeStateNode node = getNodeForPath(path, true, false);
0N/A
0N/A if(node == null)
0N/A return 0;
0N/A return node.getTotalChildCount();
0N/A }
0N/A
0N/A /**
0N/A * Returns an Enumerator that increments over the visible paths
0N/A * starting at the passed in location. The ordering of the enumeration
0N/A * is based on how the paths are displayed.
0N/A */
0N/A public Enumeration<TreePath> getVisiblePathsFrom(TreePath path) {
0N/A if(path == null)
0N/A return null;
0N/A
0N/A FHTreeStateNode node = getNodeForPath(path, true, false);
0N/A
0N/A if(node != null) {
0N/A return new VisibleFHTreeStateNodeEnumeration(node);
0N/A }
0N/A TreePath parentPath = path.getParentPath();
0N/A
0N/A node = getNodeForPath(parentPath, true, false);
0N/A if(node != null && node.isExpanded()) {
0N/A return new VisibleFHTreeStateNodeEnumeration(node,
0N/A treeModel.getIndexOfChild(parentPath.getLastPathComponent(),
0N/A path.getLastPathComponent()));
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Marks the path <code>path</code> expanded state to
0N/A * <code>isExpanded</code>.
0N/A */
0N/A public void setExpandedState(TreePath path, boolean isExpanded) {
0N/A if(isExpanded)
0N/A ensurePathIsExpanded(path, true);
0N/A else if(path != null) {
0N/A TreePath parentPath = path.getParentPath();
0N/A
0N/A // YECK! Make the parent expanded.
0N/A if(parentPath != null) {
0N/A FHTreeStateNode parentNode = getNodeForPath(parentPath,
0N/A false, true);
0N/A if(parentNode != null)
0N/A parentNode.makeVisible();
0N/A }
0N/A // And collapse the child.
0N/A FHTreeStateNode childNode = getNodeForPath(path, true,
0N/A false);
0N/A
0N/A if(childNode != null)
0N/A childNode.collapse(true);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns true if the path is expanded, and visible.
0N/A */
0N/A public boolean getExpandedState(TreePath path) {
0N/A FHTreeStateNode node = getNodeForPath(path, true, false);
0N/A
0N/A return (node != null) ? (node.isVisible() && node.isExpanded()) :
0N/A false;
0N/A }
0N/A
0N/A //
0N/A // TreeModelListener methods
0N/A //
0N/A
0N/A /**
0N/A * <p>Invoked after a node (or a set of siblings) has changed in some
0N/A * way. The node(s) have not changed locations in the tree or
0N/A * altered their children arrays, but other attributes have
0N/A * changed and may affect presentation. Example: the name of a
0N/A * file has changed, but it is in the same location in the file
0N/A * system.</p>
0N/A *
0N/A * <p>e.path() returns the path the parent of the changed node(s).</p>
0N/A *
0N/A * <p>e.childIndices() returns the index(es) of the changed node(s).</p>
0N/A */
0N/A public void treeNodesChanged(TreeModelEvent e) {
0N/A if(e != null) {
0N/A int changedIndexs[];
0N/A FHTreeStateNode changedParent = getNodeForPath
0N/A (e.getTreePath(), false, false);
0N/A int maxCounter;
0N/A
0N/A changedIndexs = e.getChildIndices();
0N/A /* Only need to update the children if the node has been
0N/A expanded once. */
0N/A // PENDING(scott): make sure childIndexs is sorted!
0N/A if (changedParent != null) {
0N/A if (changedIndexs != null &&
0N/A (maxCounter = changedIndexs.length) > 0) {
0N/A Object parentValue = changedParent.getUserObject();
0N/A
0N/A for(int counter = 0; counter < maxCounter; counter++) {
0N/A FHTreeStateNode child = changedParent.
0N/A getChildAtModelIndex(changedIndexs[counter]);
0N/A
0N/A if(child != null) {
0N/A child.setUserObject(treeModel.getChild(parentValue,
0N/A changedIndexs[counter]));
0N/A }
0N/A }
0N/A if(changedParent.isVisible() && changedParent.isExpanded())
0N/A visibleNodesChanged();
0N/A }
0N/A // Null for root indicates it changed.
0N/A else if (changedParent == root && changedParent.isVisible() &&
0N/A changedParent.isExpanded()) {
0N/A visibleNodesChanged();
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * <p>Invoked after nodes have been inserted into the tree.</p>
0N/A *
0N/A * <p>e.path() returns the parent of the new nodes
0N/A * <p>e.childIndices() returns the indices of the new nodes in
0N/A * ascending order.
0N/A */
0N/A public void treeNodesInserted(TreeModelEvent e) {
0N/A if(e != null) {
0N/A int changedIndexs[];
0N/A FHTreeStateNode changedParent = getNodeForPath
0N/A (e.getTreePath(), false, false);
0N/A int maxCounter;
0N/A
0N/A changedIndexs = e.getChildIndices();
0N/A /* Only need to update the children if the node has been
0N/A expanded once. */
0N/A // PENDING(scott): make sure childIndexs is sorted!
0N/A if(changedParent != null && changedIndexs != null &&
0N/A (maxCounter = changedIndexs.length) > 0) {
0N/A boolean isVisible =
0N/A (changedParent.isVisible() &&
0N/A changedParent.isExpanded());
0N/A
0N/A for(int counter = 0; counter < maxCounter; counter++) {
0N/A changedParent.childInsertedAtModelIndex
0N/A (changedIndexs[counter], isVisible);
0N/A }
0N/A if(isVisible && treeSelectionModel != null)
0N/A treeSelectionModel.resetRowSelection();
0N/A if(changedParent.isVisible())
0N/A this.visibleNodesChanged();
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * <p>Invoked after nodes have been removed from the tree. Note that
0N/A * if a subtree is removed from the tree, this method may only be
0N/A * invoked once for the root of the removed subtree, not once for
0N/A * each individual set of siblings removed.</p>
0N/A *
0N/A * <p>e.path() returns the former parent of the deleted nodes.</p>
0N/A *
0N/A * <p>e.childIndices() returns the indices the nodes had before they were deleted in ascending order.</p>
0N/A */
0N/A public void treeNodesRemoved(TreeModelEvent e) {
0N/A if(e != null) {
0N/A int changedIndexs[];
0N/A int maxCounter;
0N/A TreePath parentPath = e.getTreePath();
0N/A FHTreeStateNode changedParentNode = getNodeForPath
0N/A (parentPath, false, false);
0N/A
0N/A changedIndexs = e.getChildIndices();
0N/A // PENDING(scott): make sure that changedIndexs are sorted in
0N/A // ascending order.
0N/A if(changedParentNode != null && changedIndexs != null &&
0N/A (maxCounter = changedIndexs.length) > 0) {
0N/A Object[] children = e.getChildren();
0N/A boolean isVisible =
0N/A (changedParentNode.isVisible() &&
0N/A changedParentNode.isExpanded());
0N/A
0N/A for(int counter = maxCounter - 1; counter >= 0; counter--) {
0N/A changedParentNode.removeChildAtModelIndex
0N/A (changedIndexs[counter], isVisible);
0N/A }
0N/A if(isVisible) {
0N/A if(treeSelectionModel != null)
0N/A treeSelectionModel.resetRowSelection();
0N/A if (treeModel.getChildCount(changedParentNode.
0N/A getUserObject()) == 0 &&
0N/A changedParentNode.isLeaf()) {
0N/A // Node has become a leaf, collapse it.
0N/A changedParentNode.collapse(false);
0N/A }
0N/A visibleNodesChanged();
0N/A }
0N/A else if(changedParentNode.isVisible())
0N/A visibleNodesChanged();
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * <p>Invoked after the tree has drastically changed structure from a
0N/A * given node down. If the path returned by e.getPath() is of length
0N/A * one and the first element does not identify the current root node
0N/A * the first element should become the new root of the tree.<p>
0N/A *
0N/A * <p>e.path() holds the path to the node.</p>
0N/A * <p>e.childIndices() returns null.</p>
0N/A */
0N/A public void treeStructureChanged(TreeModelEvent e) {
0N/A if(e != null) {
0N/A TreePath changedPath = e.getTreePath();
0N/A FHTreeStateNode changedNode = getNodeForPath
0N/A (changedPath, false, false);
0N/A
0N/A // Check if root has changed, either to a null root, or
0N/A // to an entirely new root.
0N/A if (changedNode == root ||
0N/A (changedNode == null &&
0N/A ((changedPath == null && treeModel != null &&
0N/A treeModel.getRoot() == null) ||
0N/A (changedPath != null && changedPath.getPathCount() <= 1)))) {
0N/A rebuild(true);
0N/A }
0N/A else if(changedNode != null) {
0N/A boolean wasExpanded, wasVisible;
0N/A FHTreeStateNode parent = (FHTreeStateNode)
0N/A changedNode.getParent();
0N/A
0N/A wasExpanded = changedNode.isExpanded();
0N/A wasVisible = changedNode.isVisible();
0N/A
0N/A int index = parent.getIndex(changedNode);
0N/A changedNode.collapse(false);
0N/A parent.remove(index);
0N/A
0N/A if(wasVisible && wasExpanded) {
0N/A int row = changedNode.getRow();
0N/A parent.resetChildrenRowsFrom(row, index,
0N/A changedNode.getChildIndex());
0N/A changedNode = getNodeForPath(changedPath, false, true);
0N/A changedNode.expand();
0N/A }
0N/A if(treeSelectionModel != null && wasVisible && wasExpanded)
0N/A treeSelectionModel.resetRowSelection();
0N/A if(wasVisible)
0N/A this.visibleNodesChanged();
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A //
0N/A // Local methods
0N/A //
0N/A
0N/A private void visibleNodesChanged() {
0N/A }
0N/A
0N/A /**
0N/A * Returns the bounds for the given node. If <code>childIndex</code>
0N/A * is -1, the bounds of <code>parent</code> are returned, otherwise
0N/A * the bounds of the node at <code>childIndex</code> are returned.
0N/A */
0N/A private Rectangle getBounds(FHTreeStateNode parent, int childIndex,
0N/A Rectangle placeIn) {
0N/A boolean expanded;
0N/A int level;
0N/A int row;
0N/A Object value;
0N/A
0N/A if(childIndex == -1) {
0N/A // Getting bounds for parent
0N/A row = parent.getRow();
0N/A value = parent.getUserObject();
0N/A expanded = parent.isExpanded();
0N/A level = parent.getLevel();
0N/A }
0N/A else {
0N/A row = parent.getRowToModelIndex(childIndex);
0N/A value = treeModel.getChild(parent.getUserObject(), childIndex);
0N/A expanded = false;
0N/A level = parent.getLevel() + 1;
0N/A }
0N/A
0N/A Rectangle bounds = getNodeDimensions(value, row, level,
0N/A expanded, boundsBuffer);
0N/A // No node dimensions, bail.
0N/A if(bounds == null)
0N/A return null;
0N/A
0N/A if(placeIn == null)
0N/A placeIn = new Rectangle();
0N/A
0N/A placeIn.x = bounds.x;
0N/A placeIn.height = getRowHeight();
0N/A placeIn.y = row * placeIn.height;
0N/A placeIn.width = bounds.width;
0N/A return placeIn;
0N/A }
0N/A
0N/A /**
0N/A * Adjust the large row count of the AbstractTreeUI the receiver was
0N/A * created with.
0N/A */
0N/A private void adjustRowCountBy(int changeAmount) {
0N/A rowCount += changeAmount;
0N/A }
0N/A
0N/A /**
0N/A * Adds a mapping for node.
0N/A */
0N/A private void addMapping(FHTreeStateNode node) {
0N/A treePathMapping.put(node.getTreePath(), node);
0N/A }
0N/A
0N/A /**
0N/A * Removes the mapping for a previously added node.
0N/A */
0N/A private void removeMapping(FHTreeStateNode node) {
0N/A treePathMapping.remove(node.getTreePath());
0N/A }
0N/A
0N/A /**
0N/A * Returns the node previously added for <code>path</code>. This may
0N/A * return null, if you to create a node use getNodeForPath.
0N/A */
0N/A private FHTreeStateNode getMapping(TreePath path) {
625N/A return treePathMapping.get(path);
0N/A }
0N/A
0N/A /**
0N/A * Sent to completely rebuild the visible tree. All nodes are collapsed.
0N/A */
0N/A private void rebuild(boolean clearSelection) {
0N/A Object rootUO;
0N/A
0N/A treePathMapping.clear();
0N/A if(treeModel != null && (rootUO = treeModel.getRoot()) != null) {
0N/A root = createNodeForValue(rootUO, 0);
0N/A root.path = new TreePath(rootUO);
0N/A addMapping(root);
0N/A if(isRootVisible()) {
0N/A rowCount = 1;
0N/A root.row = 0;
0N/A }
0N/A else {
0N/A rowCount = 0;
0N/A root.row = -1;
0N/A }
0N/A root.expand();
0N/A }
0N/A else {
0N/A root = null;
0N/A rowCount = 0;
0N/A }
0N/A if(clearSelection && treeSelectionModel != null) {
0N/A treeSelectionModel.clearSelection();
0N/A }
0N/A this.visibleNodesChanged();
0N/A }
0N/A
0N/A /**
0N/A * Returns the index of the row containing location. If there
0N/A * are no rows, -1 is returned. If location is beyond the last
0N/A * row index, the last row index is returned.
0N/A */
0N/A private int getRowContainingYLocation(int location) {
0N/A if(getRowCount() == 0)
0N/A return -1;
0N/A return Math.max(0, Math.min(getRowCount() - 1,
0N/A location / getRowHeight()));
0N/A }
0N/A
0N/A /**
0N/A * Ensures that all the path components in path are expanded, accept
0N/A * for the last component which will only be expanded if expandLast
0N/A * is true.
0N/A * Returns true if succesful in finding the path.
0N/A */
0N/A private boolean ensurePathIsExpanded(TreePath aPath,
0N/A boolean expandLast) {
0N/A if(aPath != null) {
0N/A // Make sure the last entry isn't a leaf.
0N/A if(treeModel.isLeaf(aPath.getLastPathComponent())) {
0N/A aPath = aPath.getParentPath();
0N/A expandLast = true;
0N/A }
0N/A if(aPath != null) {
0N/A FHTreeStateNode lastNode = getNodeForPath(aPath, false,
0N/A true);
0N/A
0N/A if(lastNode != null) {
0N/A lastNode.makeVisible();
0N/A if(expandLast)
0N/A lastNode.expand();
0N/A return true;
0N/A }
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Creates and returns an instance of FHTreeStateNode.
0N/A */
0N/A private FHTreeStateNode createNodeForValue(Object value,int childIndex) {
0N/A return new FHTreeStateNode(value, childIndex, -1);
0N/A }
0N/A
0N/A /**
0N/A * Messages getTreeNodeForPage(path, onlyIfVisible, shouldCreate,
0N/A * path.length) as long as path is non-null and the length is > 0.
0N/A * Otherwise returns null.
0N/A */
0N/A private FHTreeStateNode getNodeForPath(TreePath path,
0N/A boolean onlyIfVisible,
0N/A boolean shouldCreate) {
0N/A if(path != null) {
0N/A FHTreeStateNode node;
0N/A
0N/A node = getMapping(path);
0N/A if(node != null) {
0N/A if(onlyIfVisible && !node.isVisible())
0N/A return null;
0N/A return node;
0N/A }
0N/A if(onlyIfVisible)
0N/A return null;
0N/A
0N/A // Check all the parent paths, until a match is found.
625N/A Stack<TreePath> paths;
0N/A
0N/A if(tempStacks.size() == 0) {
625N/A paths = new Stack<TreePath>();
0N/A }
0N/A else {
625N/A paths = tempStacks.pop();
0N/A }
0N/A
0N/A try {
0N/A paths.push(path);
0N/A path = path.getParentPath();
0N/A node = null;
0N/A while(path != null) {
0N/A node = getMapping(path);
0N/A if(node != null) {
0N/A // Found a match, create entries for all paths in
0N/A // paths.
0N/A while(node != null && paths.size() > 0) {
625N/A path = paths.pop();
0N/A node = node.createChildFor(path.
0N/A getLastPathComponent());
0N/A }
0N/A return node;
0N/A }
0N/A paths.push(path);
0N/A path = path.getParentPath();
0N/A }
0N/A }
0N/A finally {
0N/A paths.removeAllElements();
0N/A tempStacks.push(paths);
0N/A }
0N/A // If we get here it means they share a different root!
0N/A return null;
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * FHTreeStateNode is used to track what has been expanded.
0N/A * FHTreeStateNode differs from VariableHeightTreeState.TreeStateNode
0N/A * in that it is highly model intensive. That is almost all queries to a
0N/A * FHTreeStateNode result in the TreeModel being queried. And it
0N/A * obviously does not support variable sized row heights.
0N/A */
0N/A private class FHTreeStateNode extends DefaultMutableTreeNode {
0N/A /** Is this node expanded? */
0N/A protected boolean isExpanded;
0N/A
0N/A /** Index of this node from the model. */
0N/A protected int childIndex;
0N/A
0N/A /** Child count of the receiver. */
0N/A protected int childCount;
0N/A
0N/A /** Row of the receiver. This is only valid if the row is expanded.
0N/A */
0N/A protected int row;
0N/A
0N/A /** Path of this node. */
0N/A protected TreePath path;
0N/A
0N/A
0N/A public FHTreeStateNode(Object userObject, int childIndex, int row) {
0N/A super(userObject);
0N/A this.childIndex = childIndex;
0N/A this.row = row;
0N/A }
0N/A
0N/A //
0N/A // Overriden DefaultMutableTreeNode methods
0N/A //
0N/A
0N/A /**
0N/A * Messaged when this node is added somewhere, resets the path
0N/A * and adds a mapping from path to this node.
0N/A */
0N/A public void setParent(MutableTreeNode parent) {
0N/A super.setParent(parent);
0N/A if(parent != null) {
0N/A path = ((FHTreeStateNode)parent).getTreePath().
0N/A pathByAddingChild(getUserObject());
0N/A addMapping(this);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Messaged when this node is removed from its parent, this messages
0N/A * <code>removedFromMapping</code> to remove all the children.
0N/A */
0N/A public void remove(int childIndex) {
0N/A FHTreeStateNode node = (FHTreeStateNode)getChildAt(childIndex);
0N/A
0N/A node.removeFromMapping();
0N/A super.remove(childIndex);
0N/A }
0N/A
0N/A /**
0N/A * Messaged to set the user object. This resets the path.
0N/A */
0N/A public void setUserObject(Object o) {
0N/A super.setUserObject(o);
0N/A if(path != null) {
0N/A FHTreeStateNode parent = (FHTreeStateNode)getParent();
0N/A
0N/A if(parent != null)
0N/A resetChildrenPaths(parent.getTreePath());
0N/A else
0N/A resetChildrenPaths(null);
0N/A }
0N/A }
0N/A
0N/A //
0N/A //
0N/A
0N/A /**
0N/A * Returns the index of the receiver in the model.
0N/A */
0N/A public int getChildIndex() {
0N/A return childIndex;
0N/A }
0N/A
0N/A /**
0N/A * Returns the <code>TreePath</code> of the receiver.
0N/A */
0N/A public TreePath getTreePath() {
0N/A return path;
0N/A }
0N/A
0N/A /**
0N/A * Returns the child for the passed in model index, this will
0N/A * return <code>null</code> if the child for <code>index</code>
0N/A * has not yet been created (expanded).
0N/A */
0N/A public FHTreeStateNode getChildAtModelIndex(int index) {
0N/A // PENDING: Make this a binary search!
0N/A for(int counter = getChildCount() - 1; counter >= 0; counter--)
0N/A if(((FHTreeStateNode)getChildAt(counter)).childIndex == index)
0N/A return (FHTreeStateNode)getChildAt(counter);
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Returns true if this node is visible. This is determined by
0N/A * asking all the parents if they are expanded.
0N/A */
0N/A public boolean isVisible() {
0N/A FHTreeStateNode parent = (FHTreeStateNode)getParent();
0N/A
0N/A if(parent == null)
0N/A return true;
0N/A return (parent.isExpanded() && parent.isVisible());
0N/A }
0N/A
0N/A /**
0N/A * Returns the row of the receiver.
0N/A */
0N/A public int getRow() {
0N/A return row;
0N/A }
0N/A
0N/A /**
0N/A * Returns the row of the child with a model index of
0N/A * <code>index</code>.
0N/A */
0N/A public int getRowToModelIndex(int index) {
0N/A FHTreeStateNode child;
0N/A int lastRow = getRow() + 1;
0N/A int retValue = lastRow;
0N/A
0N/A // This too could be a binary search!
0N/A for(int counter = 0, maxCounter = getChildCount();
0N/A counter < maxCounter; counter++) {
0N/A child = (FHTreeStateNode)getChildAt(counter);
0N/A if(child.childIndex >= index) {
0N/A if(child.childIndex == index)
0N/A return child.row;
0N/A if(counter == 0)
0N/A return getRow() + 1 + index;
0N/A return child.row - (child.childIndex - index);
0N/A }
0N/A }
0N/A // YECK!
0N/A return getRow() + 1 + getTotalChildCount() -
0N/A (childCount - index);
0N/A }
0N/A
0N/A /**
0N/A * Returns the number of children in the receiver by descending all
0N/A * expanded nodes and messaging them with getTotalChildCount.
0N/A */
0N/A public int getTotalChildCount() {
0N/A if(isExpanded()) {
0N/A FHTreeStateNode parent = (FHTreeStateNode)getParent();
0N/A int pIndex;
0N/A
0N/A if(parent != null && (pIndex = parent.getIndex(this)) + 1 <
0N/A parent.getChildCount()) {
0N/A // This node has a created sibling, to calc total
0N/A // child count directly from that!
0N/A FHTreeStateNode nextSibling = (FHTreeStateNode)parent.
0N/A getChildAt(pIndex + 1);
0N/A
0N/A return nextSibling.row - row -
0N/A (nextSibling.childIndex - childIndex);
0N/A }
0N/A else {
0N/A int retCount = childCount;
0N/A
0N/A for(int counter = getChildCount() - 1; counter >= 0;
0N/A counter--) {
0N/A retCount += ((FHTreeStateNode)getChildAt(counter))
0N/A .getTotalChildCount();
0N/A }
0N/A return retCount;
0N/A }
0N/A }
0N/A return 0;
0N/A }
0N/A
0N/A /**
0N/A * Returns true if this node is expanded.
0N/A */
0N/A public boolean isExpanded() {
0N/A return isExpanded;
0N/A }
0N/A
0N/A /**
0N/A * The highest visible nodes have a depth of 0.
0N/A */
0N/A public int getVisibleLevel() {
0N/A if (isRootVisible()) {
0N/A return getLevel();
0N/A } else {
0N/A return getLevel() - 1;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Recreates the receivers path, and all its childrens paths.
0N/A */
0N/A protected void resetChildrenPaths(TreePath parentPath) {
0N/A removeMapping(this);
0N/A if(parentPath == null)
0N/A path = new TreePath(getUserObject());
0N/A else
0N/A path = parentPath.pathByAddingChild(getUserObject());
0N/A addMapping(this);
0N/A for(int counter = getChildCount() - 1; counter >= 0; counter--)
0N/A ((FHTreeStateNode)getChildAt(counter)).
0N/A resetChildrenPaths(path);
0N/A }
0N/A
0N/A /**
0N/A * Removes the receiver, and all its children, from the mapping
0N/A * table.
0N/A */
0N/A protected void removeFromMapping() {
0N/A if(path != null) {
0N/A removeMapping(this);
0N/A for(int counter = getChildCount() - 1; counter >= 0; counter--)
0N/A ((FHTreeStateNode)getChildAt(counter)).removeFromMapping();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Creates a new node to represent <code>userObject</code>.
0N/A * This does NOT check to ensure there isn't already a child node
0N/A * to manage <code>userObject</code>.
0N/A */
0N/A protected FHTreeStateNode createChildFor(Object userObject) {
0N/A int newChildIndex = treeModel.getIndexOfChild
0N/A (getUserObject(), userObject);
0N/A
0N/A if(newChildIndex < 0)
0N/A return null;
0N/A
0N/A FHTreeStateNode aNode;
0N/A FHTreeStateNode child = createNodeForValue(userObject,
0N/A newChildIndex);
0N/A int childRow;
0N/A
0N/A if(isVisible()) {
0N/A childRow = getRowToModelIndex(newChildIndex);
0N/A }
0N/A else {
0N/A childRow = -1;
0N/A }
0N/A child.row = childRow;
0N/A for(int counter = 0, maxCounter = getChildCount();
0N/A counter < maxCounter; counter++) {
0N/A aNode = (FHTreeStateNode)getChildAt(counter);
0N/A if(aNode.childIndex > newChildIndex) {
0N/A insert(child, counter);
0N/A return child;
0N/A }
0N/A }
0N/A add(child);
0N/A return child;
0N/A }
0N/A
0N/A /**
0N/A * Adjusts the receiver, and all its children rows by
0N/A * <code>amount</code>.
0N/A */
0N/A protected void adjustRowBy(int amount) {
0N/A row += amount;
0N/A if(isExpanded) {
0N/A for(int counter = getChildCount() - 1; counter >= 0;
0N/A counter--)
0N/A ((FHTreeStateNode)getChildAt(counter)).adjustRowBy(amount);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Adjusts this node, its child, and its parent starting at
0N/A * an index of <code>index</code> index is the index of the child
0N/A * to start adjusting from, which is not necessarily the model
0N/A * index.
0N/A */
0N/A protected void adjustRowBy(int amount, int startIndex) {
0N/A // Could check isVisible, but probably isn't worth it.
0N/A if(isExpanded) {
0N/A // children following startIndex.
0N/A for(int counter = getChildCount() - 1; counter >= startIndex;
0N/A counter--)
0N/A ((FHTreeStateNode)getChildAt(counter)).adjustRowBy(amount);
0N/A }
0N/A // Parent
0N/A FHTreeStateNode parent = (FHTreeStateNode)getParent();
0N/A
0N/A if(parent != null) {
0N/A parent.adjustRowBy(amount, parent.getIndex(this) + 1);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Messaged when the node has expanded. This updates all of
0N/A * the receivers children rows, as well as the total row count.
0N/A */
0N/A protected void didExpand() {
0N/A int nextRow = setRowAndChildren(row);
0N/A FHTreeStateNode parent = (FHTreeStateNode)getParent();
0N/A int childRowCount = nextRow - row - 1;
0N/A
0N/A if(parent != null) {
0N/A parent.adjustRowBy(childRowCount, parent.getIndex(this) + 1);
0N/A }
0N/A adjustRowCountBy(childRowCount);
0N/A }
0N/A
0N/A /**
0N/A * Sets the receivers row to <code>nextRow</code> and recursively
0N/A * updates all the children of the receivers rows. The index the
0N/A * next row is to be placed as is returned.
0N/A */
0N/A protected int setRowAndChildren(int nextRow) {
0N/A row = nextRow;
0N/A
0N/A if(!isExpanded())
0N/A return row + 1;
0N/A
0N/A int lastRow = row + 1;
0N/A int lastModelIndex = 0;
0N/A FHTreeStateNode child;
0N/A int maxCounter = getChildCount();
0N/A
0N/A for(int counter = 0; counter < maxCounter; counter++) {
0N/A child = (FHTreeStateNode)getChildAt(counter);
0N/A lastRow += (child.childIndex - lastModelIndex);
0N/A lastModelIndex = child.childIndex + 1;
0N/A if(child.isExpanded) {
0N/A lastRow = child.setRowAndChildren(lastRow);
0N/A }
0N/A else {
0N/A child.row = lastRow++;
0N/A }
0N/A }
0N/A return lastRow + childCount - lastModelIndex;
0N/A }
0N/A
0N/A /**
0N/A * Resets the receivers childrens rows. Starting with the child
0N/A * at <code>childIndex</code> (and <code>modelIndex</code>) to
0N/A * <code>newRow</code>. This uses <code>setRowAndChildren</code>
0N/A * to recursively descend children, and uses
0N/A * <code>resetRowSelection</code> to ascend parents.
0N/A */
0N/A // This can be rather expensive, but is needed for the collapse
0N/A // case this is resulting from a remove (although I could fix
0N/A // that by having instances of FHTreeStateNode hold a ref to
0N/A // the number of children). I prefer this though, making determing
0N/A // the row of a particular node fast is very nice!
0N/A protected void resetChildrenRowsFrom(int newRow, int childIndex,
0N/A int modelIndex) {
0N/A int lastRow = newRow;
0N/A int lastModelIndex = modelIndex;
0N/A FHTreeStateNode node;
0N/A int maxCounter = getChildCount();
0N/A
0N/A for(int counter = childIndex; counter < maxCounter; counter++) {
0N/A node = (FHTreeStateNode)getChildAt(counter);
0N/A lastRow += (node.childIndex - lastModelIndex);
0N/A lastModelIndex = node.childIndex + 1;
0N/A if(node.isExpanded) {
0N/A lastRow = node.setRowAndChildren(lastRow);
0N/A }
0N/A else {
0N/A node.row = lastRow++;
0N/A }
0N/A }
0N/A lastRow += childCount - lastModelIndex;
0N/A node = (FHTreeStateNode)getParent();
0N/A if(node != null) {
0N/A node.resetChildrenRowsFrom(lastRow, node.getIndex(this) + 1,
0N/A this.childIndex + 1);
0N/A }
0N/A else { // This is the root, reset total ROWCOUNT!
0N/A rowCount = lastRow;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Makes the receiver visible, but invoking
0N/A * <code>expandParentAndReceiver</code> on the superclass.
0N/A */
0N/A protected void makeVisible() {
0N/A FHTreeStateNode parent = (FHTreeStateNode)getParent();
0N/A
0N/A if(parent != null)
0N/A parent.expandParentAndReceiver();
0N/A }
0N/A
0N/A /**
0N/A * Invokes <code>expandParentAndReceiver</code> on the parent,
0N/A * and expands the receiver.
0N/A */
0N/A protected void expandParentAndReceiver() {
0N/A FHTreeStateNode parent = (FHTreeStateNode)getParent();
0N/A
0N/A if(parent != null)
0N/A parent.expandParentAndReceiver();
0N/A expand();
0N/A }
0N/A
0N/A /**
0N/A * Expands the receiver.
0N/A */
0N/A protected void expand() {
0N/A if(!isExpanded && !isLeaf()) {
0N/A boolean visible = isVisible();
0N/A
0N/A isExpanded = true;
0N/A childCount = treeModel.getChildCount(getUserObject());
0N/A
0N/A if(visible) {
0N/A didExpand();
0N/A }
0N/A
0N/A // Update the selection model.
0N/A if(visible && treeSelectionModel != null) {
0N/A treeSelectionModel.resetRowSelection();
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Collapses the receiver. If <code>adjustRows</code> is true,
0N/A * the rows of nodes after the receiver are adjusted.
0N/A */
0N/A protected void collapse(boolean adjustRows) {
0N/A if(isExpanded) {
0N/A if(isVisible() && adjustRows) {
0N/A int childCount = getTotalChildCount();
0N/A
0N/A isExpanded = false;
0N/A adjustRowCountBy(-childCount);
0N/A // We can do this because adjustRowBy won't descend
0N/A // the children.
0N/A adjustRowBy(-childCount, 0);
0N/A }
0N/A else
0N/A isExpanded = false;
0N/A
0N/A if(adjustRows && isVisible() && treeSelectionModel != null)
0N/A treeSelectionModel.resetRowSelection();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns true if the receiver is a leaf.
0N/A */
0N/A public boolean isLeaf() {
0N/A TreeModel model = getModel();
0N/A
0N/A return (model != null) ? model.isLeaf(this.getUserObject()) :
0N/A true;
0N/A }
0N/A
0N/A /**
0N/A * Adds newChild to this nodes children at the appropriate location.
0N/A * The location is determined from the childIndex of newChild.
0N/A */
0N/A protected void addNode(FHTreeStateNode newChild) {
0N/A boolean added = false;
0N/A int childIndex = newChild.getChildIndex();
0N/A
0N/A for(int counter = 0, maxCounter = getChildCount();
0N/A counter < maxCounter; counter++) {
0N/A if(((FHTreeStateNode)getChildAt(counter)).getChildIndex() >
0N/A childIndex) {
0N/A added = true;
0N/A insert(newChild, counter);
0N/A counter = maxCounter;
0N/A }
0N/A }
0N/A if(!added)
0N/A add(newChild);
0N/A }
0N/A
0N/A /**
0N/A * Removes the child at <code>modelIndex</code>.
0N/A * <code>isChildVisible</code> should be true if the receiver
0N/A * is visible and expanded.
0N/A */
0N/A protected void removeChildAtModelIndex(int modelIndex,
0N/A boolean isChildVisible) {
0N/A FHTreeStateNode childNode = getChildAtModelIndex(modelIndex);
0N/A
0N/A if(childNode != null) {
0N/A int row = childNode.getRow();
0N/A int index = getIndex(childNode);
0N/A
0N/A childNode.collapse(false);
0N/A remove(index);
0N/A adjustChildIndexs(index, -1);
0N/A childCount--;
0N/A if(isChildVisible) {
0N/A // Adjust the rows.
0N/A resetChildrenRowsFrom(row, index, modelIndex);
0N/A }
0N/A }
0N/A else {
0N/A int maxCounter = getChildCount();
0N/A FHTreeStateNode aChild;
0N/A
0N/A for(int counter = 0; counter < maxCounter; counter++) {
0N/A aChild = (FHTreeStateNode)getChildAt(counter);
0N/A if(aChild.childIndex >= modelIndex) {
0N/A if(isChildVisible) {
0N/A adjustRowBy(-1, counter);
0N/A adjustRowCountBy(-1);
0N/A }
0N/A // Since matched and children are always sorted by
0N/A // index, no need to continue testing with the
0N/A // above.
0N/A for(; counter < maxCounter; counter++)
0N/A ((FHTreeStateNode)getChildAt(counter)).
0N/A childIndex--;
0N/A childCount--;
0N/A return;
0N/A }
0N/A }
0N/A // No children to adjust, but it was a child, so we still need
0N/A // to adjust nodes after this one.
0N/A if(isChildVisible) {
0N/A adjustRowBy(-1, maxCounter);
0N/A adjustRowCountBy(-1);
0N/A }
0N/A childCount--;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Adjusts the child indexs of the receivers children by
0N/A * <code>amount</code>, starting at <code>index</code>.
0N/A */
0N/A protected void adjustChildIndexs(int index, int amount) {
0N/A for(int counter = index, maxCounter = getChildCount();
0N/A counter < maxCounter; counter++) {
0N/A ((FHTreeStateNode)getChildAt(counter)).childIndex += amount;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Messaged when a child has been inserted at index. For all the
0N/A * children that have a childIndex >= index their index is incremented
0N/A * by one.
0N/A */
0N/A protected void childInsertedAtModelIndex(int index,
0N/A boolean isExpandedAndVisible) {
0N/A FHTreeStateNode aChild;
0N/A int maxCounter = getChildCount();
0N/A
0N/A for(int counter = 0; counter < maxCounter; counter++) {
0N/A aChild = (FHTreeStateNode)getChildAt(counter);
0N/A if(aChild.childIndex >= index) {
0N/A if(isExpandedAndVisible) {
0N/A adjustRowBy(1, counter);
0N/A adjustRowCountBy(1);
0N/A }
0N/A /* Since matched and children are always sorted by
0N/A index, no need to continue testing with the above. */
0N/A for(; counter < maxCounter; counter++)
0N/A ((FHTreeStateNode)getChildAt(counter)).childIndex++;
0N/A childCount++;
0N/A return;
0N/A }
0N/A }
0N/A // No children to adjust, but it was a child, so we still need
0N/A // to adjust nodes after this one.
0N/A if(isExpandedAndVisible) {
0N/A adjustRowBy(1, maxCounter);
0N/A adjustRowCountBy(1);
0N/A }
0N/A childCount++;
0N/A }
0N/A
0N/A /**
0N/A * Returns true if there is a row for <code>row</code>.
0N/A * <code>nextRow</code> gives the bounds of the receiver.
0N/A * Information about the found row is returned in <code>info</code>.
0N/A * This should be invoked on root with <code>nextRow</code> set
0N/A * to <code>getRowCount</code>().
0N/A */
0N/A protected boolean getPathForRow(int row, int nextRow,
0N/A SearchInfo info) {
0N/A if(this.row == row) {
0N/A info.node = this;
0N/A info.isNodeParentNode = false;
0N/A info.childIndex = childIndex;
0N/A return true;
0N/A }
0N/A
0N/A FHTreeStateNode child;
0N/A FHTreeStateNode lastChild = null;
0N/A
0N/A for(int counter = 0, maxCounter = getChildCount();
0N/A counter < maxCounter; counter++) {
0N/A child = (FHTreeStateNode)getChildAt(counter);
0N/A if(child.row > row) {
0N/A if(counter == 0) {
0N/A // No node exists for it, and is first.
0N/A info.node = this;
0N/A info.isNodeParentNode = true;
0N/A info.childIndex = row - this.row - 1;
0N/A return true;
0N/A }
0N/A else {
0N/A // May have been in last childs bounds.
0N/A int lastChildEndRow = 1 + child.row -
0N/A (child.childIndex - lastChild.childIndex);
0N/A
0N/A if(row < lastChildEndRow) {
0N/A return lastChild.getPathForRow(row,
0N/A lastChildEndRow, info);
0N/A }
0N/A // Between last child and child, but not in last child
0N/A info.node = this;
0N/A info.isNodeParentNode = true;
0N/A info.childIndex = row - lastChildEndRow +
0N/A lastChild.childIndex + 1;
0N/A return true;
0N/A }
0N/A }
0N/A lastChild = child;
0N/A }
0N/A
0N/A // Not in children, but we should have it, offset from
0N/A // nextRow.
0N/A if(lastChild != null) {
0N/A int lastChildEndRow = nextRow -
0N/A (childCount - lastChild.childIndex) + 1;
0N/A
0N/A if(row < lastChildEndRow) {
0N/A return lastChild.getPathForRow(row, lastChildEndRow, info);
0N/A }
0N/A // Between last child and child, but not in last child
0N/A info.node = this;
0N/A info.isNodeParentNode = true;
0N/A info.childIndex = row - lastChildEndRow +
0N/A lastChild.childIndex + 1;
0N/A return true;
0N/A }
0N/A else {
0N/A // No children.
0N/A int retChildIndex = row - this.row - 1;
0N/A
0N/A if(retChildIndex >= childCount) {
0N/A return false;
0N/A }
0N/A info.node = this;
0N/A info.isNodeParentNode = true;
0N/A info.childIndex = retChildIndex;
0N/A return true;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Asks all the children of the receiver for their totalChildCount
0N/A * and returns this value (plus stopIndex).
0N/A */
0N/A protected int getCountTo(int stopIndex) {
0N/A FHTreeStateNode aChild;
0N/A int retCount = stopIndex + 1;
0N/A
0N/A for(int counter = 0, maxCounter = getChildCount();
0N/A counter < maxCounter; counter++) {
0N/A aChild = (FHTreeStateNode)getChildAt(counter);
0N/A if(aChild.childIndex >= stopIndex)
0N/A counter = maxCounter;
0N/A else
0N/A retCount += aChild.getTotalChildCount();
0N/A }
0N/A if(parent != null)
0N/A return retCount + ((FHTreeStateNode)getParent())
0N/A .getCountTo(childIndex);
0N/A if(!isRootVisible())
0N/A return (retCount - 1);
0N/A return retCount;
0N/A }
0N/A
0N/A /**
0N/A * Returns the number of children that are expanded to
0N/A * <code>stopIndex</code>. This does not include the number
0N/A * of children that the child at <code>stopIndex</code> might
0N/A * have.
0N/A */
0N/A protected int getNumExpandedChildrenTo(int stopIndex) {
0N/A FHTreeStateNode aChild;
0N/A int retCount = stopIndex;
0N/A
0N/A for(int counter = 0, maxCounter = getChildCount();
0N/A counter < maxCounter; counter++) {
0N/A aChild = (FHTreeStateNode)getChildAt(counter);
0N/A if(aChild.childIndex >= stopIndex)
0N/A return retCount;
0N/A else {
0N/A retCount += aChild.getTotalChildCount();
0N/A }
0N/A }
0N/A return retCount;
0N/A }
0N/A
0N/A /**
0N/A * Messaged when this node either expands or collapses.
0N/A */
0N/A protected void didAdjustTree() {
0N/A }
0N/A
0N/A } // FixedHeightLayoutCache.FHTreeStateNode
0N/A
0N/A
0N/A /**
0N/A * Used as a placeholder when getting the path in FHTreeStateNodes.
0N/A */
0N/A private class SearchInfo {
0N/A protected FHTreeStateNode node;
0N/A protected boolean isNodeParentNode;
0N/A protected int childIndex;
0N/A
0N/A protected TreePath getPath() {
0N/A if(node == null)
0N/A return null;
0N/A
0N/A if(isNodeParentNode)
0N/A return node.getTreePath().pathByAddingChild(treeModel.getChild
0N/A (node.getUserObject(),
0N/A childIndex));
0N/A return node.path;
0N/A }
0N/A } // FixedHeightLayoutCache.SearchInfo
0N/A
0N/A
0N/A /**
0N/A * An enumerator to iterate through visible nodes.
0N/A */
0N/A // This is very similiar to
0N/A // VariableHeightTreeState.VisibleTreeStateNodeEnumeration
0N/A private class VisibleFHTreeStateNodeEnumeration
0N/A implements Enumeration<TreePath>
0N/A {
0N/A /** Parent thats children are being enumerated. */
0N/A protected FHTreeStateNode parent;
0N/A /** Index of next child. An index of -1 signifies parent should be
0N/A * visibled next. */
0N/A protected int nextIndex;
0N/A /** Number of children in parent. */
0N/A protected int childCount;
0N/A
0N/A protected VisibleFHTreeStateNodeEnumeration(FHTreeStateNode node) {
0N/A this(node, -1);
0N/A }
0N/A
0N/A protected VisibleFHTreeStateNodeEnumeration(FHTreeStateNode parent,
0N/A int startIndex) {
0N/A this.parent = parent;
0N/A this.nextIndex = startIndex;
0N/A this.childCount = treeModel.getChildCount(this.parent.
0N/A getUserObject());
0N/A }
0N/A
0N/A /**
0N/A * @return true if more visible nodes.
0N/A */
0N/A public boolean hasMoreElements() {
0N/A return (parent != null);
0N/A }
0N/A
0N/A /**
0N/A * @return next visible TreePath.
0N/A */
0N/A public TreePath nextElement() {
0N/A if(!hasMoreElements())
0N/A throw new NoSuchElementException("No more visible paths");
0N/A
0N/A TreePath retObject;
0N/A
0N/A if(nextIndex == -1)
0N/A retObject = parent.getTreePath();
0N/A else {
0N/A FHTreeStateNode node = parent.getChildAtModelIndex(nextIndex);
0N/A
0N/A if(node == null)
0N/A retObject = parent.getTreePath().pathByAddingChild
0N/A (treeModel.getChild(parent.getUserObject(),
0N/A nextIndex));
0N/A else
0N/A retObject = node.getTreePath();
0N/A }
0N/A updateNextObject();
0N/A return retObject;
0N/A }
0N/A
0N/A /**
0N/A * Determines the next object by invoking <code>updateNextIndex</code>
0N/A * and if not succesful <code>findNextValidParent</code>.
0N/A */
0N/A protected void updateNextObject() {
0N/A if(!updateNextIndex()) {
0N/A findNextValidParent();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Finds the next valid parent, this should be called when nextIndex
0N/A * is beyond the number of children of the current parent.
0N/A */
0N/A protected boolean findNextValidParent() {
0N/A if(parent == root) {
0N/A // mark as invalid!
0N/A parent = null;
0N/A return false;
0N/A }
0N/A while(parent != null) {
0N/A FHTreeStateNode newParent = (FHTreeStateNode)parent.
0N/A getParent();
0N/A
0N/A if(newParent != null) {
0N/A nextIndex = parent.childIndex;
0N/A parent = newParent;
0N/A childCount = treeModel.getChildCount
0N/A (parent.getUserObject());
0N/A if(updateNextIndex())
0N/A return true;
0N/A }
0N/A else
0N/A parent = null;
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Updates <code>nextIndex</code> returning false if it is beyond
0N/A * the number of children of parent.
0N/A */
0N/A protected boolean updateNextIndex() {
0N/A // nextIndex == -1 identifies receiver, make sure is expanded
0N/A // before descend.
0N/A if(nextIndex == -1 && !parent.isExpanded()) {
0N/A return false;
0N/A }
0N/A
0N/A // Check that it can have kids
0N/A if(childCount == 0) {
0N/A return false;
0N/A }
0N/A // Make sure next index not beyond child count.
0N/A else if(++nextIndex >= childCount) {
0N/A return false;
0N/A }
0N/A
0N/A FHTreeStateNode child = parent.getChildAtModelIndex(nextIndex);
0N/A
0N/A if(child != null && child.isExpanded()) {
0N/A parent = child;
0N/A nextIndex = -1;
0N/A childCount = treeModel.getChildCount(child.getUserObject());
0N/A }
0N/A return true;
0N/A }
0N/A } // FixedHeightLayoutCache.VisibleFHTreeStateNodeEnumeration
0N/A}