/**
   * 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();
      }
    }
  }
  /**
   * 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;
  }
    /**
     * Removes the child at <code>modelIndex</code>. <code>isChildVisible</code> should be true if
     * the receiver is visible and expanded.
     */
    protected void removeChildAtModelIndex(int modelIndex, boolean isChildVisible) {
      FHTreeStateNode childNode = getChildAtModelIndex(modelIndex);

      if (childNode != null) {
        int row = childNode.getRow();
        int index = getIndex(childNode);

        childNode.collapse(false);
        remove(index);
        adjustChildIndexs(index, -1);
        childCount--;
        if (isChildVisible) {
          // Adjust the rows.
          resetChildrenRowsFrom(row, index, modelIndex);
        }
      } else {
        int maxCounter = getChildCount();
        FHTreeStateNode aChild;

        for (int counter = 0; counter < maxCounter; counter++) {
          aChild = (FHTreeStateNode) getChildAt(counter);
          if (aChild.childIndex >= modelIndex) {
            if (isChildVisible) {
              adjustRowBy(-1, counter);
              adjustRowCountBy(-1);
            }
            // Since matched and children are always sorted by
            // index, no need to continue testing with the
            // above.
            for (; counter < maxCounter; counter++)
              ((FHTreeStateNode) getChildAt(counter)).childIndex--;
            childCount--;
            return;
          }
        }
        // No children to adjust, but it was a child, so we still need
        // to adjust nodes after this one.
        if (isChildVisible) {
          adjustRowBy(-1, maxCounter);
          adjustRowCountBy(-1);
        }
        childCount--;
      }
    }