/** * Updates <code>nextIndex</code> returning false if it is beyond the number of children of * parent. */ protected boolean updateNextIndex() { // nextIndex == -1 identifies receiver, make sure is expanded // before descend. if (nextIndex == -1 && !parent.isExpanded()) { return false; } // Check that it can have kids if (childCount == 0) { return false; } // Make sure next index not beyond child count. else if (++nextIndex >= childCount) { return false; } FHTreeStateNode child = parent.getChildAtModelIndex(nextIndex); if (child != null && child.isExpanded()) { parent = child; nextIndex = -1; childCount = treeModel.getChildCount(child.getUserObject()); } return true; }
/** * 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. * * <p>e.path() returns the path the parent of the changed node(s). * * <p>e.childIndices() returns the index(es) of the changed node(s). */ public void treeNodesChanged(TreeModelEvent e) { if (e != null) { int changedIndexs[]; FHTreeStateNode changedParent = getNodeForPath(SwingUtilities2.getTreePath(e, getModel()), false, false); int maxCounter; changedIndexs = e.getChildIndices(); /* Only need to update the children if the node has been expanded once. */ // PENDING(scott): make sure childIndexs is sorted! if (changedParent != null) { if (changedIndexs != null && (maxCounter = changedIndexs.length) > 0) { Object parentValue = changedParent.getUserObject(); for (int counter = 0; counter < maxCounter; counter++) { FHTreeStateNode child = changedParent.getChildAtModelIndex(changedIndexs[counter]); if (child != null) { child.setUserObject(treeModel.getChild(parentValue, changedIndexs[counter])); } } if (changedParent.isVisible() && changedParent.isExpanded()) visibleNodesChanged(); } // Null for root indicates it changed. else if (changedParent == root && changedParent.isVisible() && changedParent.isExpanded()) { visibleNodesChanged(); } } } }
/** * Returns the bounds for the given node. If <code>childIndex</code> is -1, the bounds of <code> * parent</code> are returned, otherwise the bounds of the node at <code>childIndex</code> are * returned. */ private Rectangle getBounds(FHTreeStateNode parent, int childIndex, Rectangle placeIn) { boolean expanded; int level; int row; Object value; if (childIndex == -1) { // Getting bounds for parent row = parent.getRow(); value = parent.getUserObject(); expanded = parent.isExpanded(); level = parent.getLevel(); } else { row = parent.getRowToModelIndex(childIndex); value = treeModel.getChild(parent.getUserObject(), childIndex); expanded = false; level = parent.getLevel() + 1; } Rectangle bounds = getNodeDimensions(value, row, level, expanded, boundsBuffer); // No node dimensions, bail. if (bounds == null) return null; if (placeIn == null) placeIn = new Rectangle(); placeIn.x = bounds.x; placeIn.height = getRowHeight(); placeIn.y = row * placeIn.height; placeIn.width = bounds.width; return placeIn; }
/** * 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. * * <p>e.path() holds the path to the node. * * <p>e.childIndices() returns null. */ public void treeStructureChanged(TreeModelEvent e) { if (e != null) { TreePath changedPath = SwingUtilities2.getTreePath(e, getModel()); FHTreeStateNode changedNode = getNodeForPath(changedPath, false, false); // Check if root has changed, either to a null root, or // to an entirely new root. if (changedNode == root || (changedNode == null && ((changedPath == null && treeModel != null && treeModel.getRoot() == null) || (changedPath != null && changedPath.getPathCount() <= 1)))) { rebuild(true); } else if (changedNode != null) { boolean wasExpanded, wasVisible; FHTreeStateNode parent = (FHTreeStateNode) changedNode.getParent(); wasExpanded = changedNode.isExpanded(); wasVisible = changedNode.isVisible(); int index = parent.getIndex(changedNode); changedNode.collapse(false); parent.remove(index); if (wasVisible && wasExpanded) { int row = changedNode.getRow(); parent.resetChildrenRowsFrom(row, index, changedNode.getChildIndex()); changedNode = getNodeForPath(changedPath, false, true); changedNode.expand(); } if (treeSelectionModel != null && wasVisible && wasExpanded) treeSelectionModel.resetRowSelection(); if (wasVisible) this.visibleNodesChanged(); } } }
/** * 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. * * <p>e.path() returns the former parent of the deleted nodes. * * <p>e.childIndices() returns the indices the nodes had before they were deleted in ascending * order. */ public void treeNodesRemoved(TreeModelEvent e) { if (e != null) { int changedIndexs[]; int maxCounter; TreePath parentPath = SwingUtilities2.getTreePath(e, getModel()); FHTreeStateNode changedParentNode = getNodeForPath(parentPath, false, false); changedIndexs = e.getChildIndices(); // PENDING(scott): make sure that changedIndexs are sorted in // ascending order. if (changedParentNode != null && changedIndexs != null && (maxCounter = changedIndexs.length) > 0) { Object[] children = e.getChildren(); boolean isVisible = (changedParentNode.isVisible() && changedParentNode.isExpanded()); for (int counter = maxCounter - 1; counter >= 0; counter--) { changedParentNode.removeChildAtModelIndex(changedIndexs[counter], isVisible); } if (isVisible) { if (treeSelectionModel != null) treeSelectionModel.resetRowSelection(); if (treeModel.getChildCount(changedParentNode.getUserObject()) == 0 && changedParentNode.isLeaf()) { // Node has become a leaf, collapse it. changedParentNode.collapse(false); } visibleNodesChanged(); } else if (changedParentNode.isVisible()) visibleNodesChanged(); } } }
/** Returns true if the value identified by row is currently expanded. */ public boolean isExpanded(TreePath path) { if (path != null) { FHTreeStateNode lastNode = getNodeForPath(path, true, false); return (lastNode != null && lastNode.isExpanded()); } return false; }
/** * Returns the row that the last item identified in path is visible at. Will return -1 if any of * the elements in path are not currently visible. */ public int getRowForPath(TreePath path) { if (path == null || root == null) return -1; FHTreeStateNode node = getNodeForPath(path, true, false); if (node != null) return node.getRow(); TreePath parentPath = path.getParentPath(); node = getNodeForPath(parentPath, true, false); if (node != null && node.isExpanded()) { return node.getRowToModelIndex( treeModel.getIndexOfChild( parentPath.getLastPathComponent(), path.getLastPathComponent())); } return -1; }
/** * Returns an Enumerator that increments over the visible paths starting at the passed in * location. The ordering of the enumeration is based on how the paths are displayed. */ public Enumeration<TreePath> getVisiblePathsFrom(TreePath path) { if (path == null) return null; FHTreeStateNode node = getNodeForPath(path, true, false); if (node != null) { return new VisibleFHTreeStateNodeEnumeration(node); } TreePath parentPath = path.getParentPath(); node = getNodeForPath(parentPath, true, false); if (node != null && node.isExpanded()) { return new VisibleFHTreeStateNodeEnumeration( node, treeModel.getIndexOfChild( parentPath.getLastPathComponent(), path.getLastPathComponent())); } return null; }
/** * Returns a rectangle giving the bounds needed to draw path. * * @param path a TreePath specifying a node * @param placeIn a Rectangle object giving the available space * @return a Rectangle object specifying the space to be used */ public Rectangle getBounds(TreePath path, Rectangle placeIn) { if (path == null) return null; FHTreeStateNode node = getNodeForPath(path, true, false); if (node != null) return getBounds(node, -1, placeIn); // node hasn't been created yet. TreePath parentPath = path.getParentPath(); node = getNodeForPath(parentPath, true, false); if (node != null && node.isExpanded()) { int childIndex = treeModel.getIndexOfChild(parentPath.getLastPathComponent(), path.getLastPathComponent()); if (childIndex != -1) return getBounds(node, childIndex, placeIn); } return null; }
/** * Invoked after nodes have been inserted into the tree. * * <p>e.path() returns the parent of the new nodes * * <p>e.childIndices() returns the indices of the new nodes in ascending order. */ public void treeNodesInserted(TreeModelEvent e) { if (e != null) { int changedIndexs[]; FHTreeStateNode changedParent = getNodeForPath(SwingUtilities2.getTreePath(e, getModel()), false, false); int maxCounter; changedIndexs = e.getChildIndices(); /* Only need to update the children if the node has been expanded once. */ // PENDING(scott): make sure childIndexs is sorted! if (changedParent != null && changedIndexs != null && (maxCounter = changedIndexs.length) > 0) { boolean isVisible = (changedParent.isVisible() && changedParent.isExpanded()); for (int counter = 0; counter < maxCounter; counter++) { changedParent.childInsertedAtModelIndex(changedIndexs[counter], isVisible); } if (isVisible && treeSelectionModel != null) treeSelectionModel.resetRowSelection(); if (changedParent.isVisible()) this.visibleNodesChanged(); } } }
/** * Returns true if this node is visible. This is determined by asking all the parents if they * are expanded. */ public boolean isVisible() { FHTreeStateNode parent = (FHTreeStateNode) getParent(); if (parent == null) return true; return (parent.isExpanded() && parent.isVisible()); }
/** Returns true if the path is expanded, and visible. */ public boolean getExpandedState(TreePath path) { FHTreeStateNode node = getNodeForPath(path, true, false); return (node != null) ? (node.isVisible() && node.isExpanded()) : false; }