/**
   * 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();
      }
    }
  }
  /**
   * 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();
        }
      }
    }
  }
  /**
   * 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();
      }
    }
  }