/**
   * Causes an {@link SmallStepNodeComponent#update() on all
   * nodes that have changed.<br>
   * <br>
   * When all updates are done relayouts the View.
   *
   * @see #relayout()
   */
  @Override
  protected void nodesChanged(TreeModelEvent event) {
    boolean relayout = false;
    Object[] children = event.getChildren();
    if (children == null) {

      // if the children are null and the path only contains one element
      // this element is the root node.
      if (event.getPath().length == 1) {
        SmallStepProofNode proofNode = (SmallStepProofNode) event.getPath()[0];
        SmallStepNodeComponent nodeComponent = (SmallStepNodeComponent) proofNode.getUserObject();
        if (nodeComponent != null) {
          nodeComponent.update();
          relayout = true;
        }
      }
    } else {
      for (int i = 0; i < children.length; i++) {
        if (children[i] instanceof ProofNode) {
          SmallStepProofNode proofNode = (SmallStepProofNode) children[i];

          SmallStepNodeComponent nodeComponent = (SmallStepNodeComponent) proofNode.getUserObject();
          if (nodeComponent != null) {
            nodeComponent.update();
            relayout = true;
          }
        }
      }
    }
    if (relayout) {
      relayout();
    }
  }
 /**
  * Returns the first child of the given node.<br>
  * <br>
  * If the node has no children, <i>null</i> is returned.
  *
  * @param node
  * @return
  */
 private SmallStepProofNode getFirstChild(SmallStepProofNode node) {
   try {
     return node.getFirstChild();
   } catch (Exception e) {
     // nothing
   }
   return null;
 }
  /**
   * Removes all userobjects from the nodes that will be removed by the {@link SmallStepProofModel}
   * later.
   */
  @Override
  protected void nodesRemoved(TreeModelEvent event) {
    Object[] children = event.getChildren();
    if (children == null) {
      return;
    }
    for (int i = 0; i < children.length; i++) {
      if (children[i] instanceof ProofNode) {
        SmallStepProofNode proofNode = (SmallStepProofNode) children[i];

        SmallStepNodeComponent nodeComponent = (SmallStepNodeComponent) proofNode.getUserObject();
        if (nodeComponent != null) {
          remove(nodeComponent);
          proofNode.setUserObject(null);
        }
      }
    }
  }
  /**
   * If <code>advanced</code> is <code>true</code>, the small step component will display only axiom
   * rules in the rule menu, otherwise, in beginner mode, meta rules will also be displayed.
   *
   * @param pAdvanced <code>true</code> to display only axiom rules.
   * @see #isAdvanced()
   */
  void setAdvanced(boolean pAdvanced) {
    // check if we have a new setting
    if (this.advanced != pAdvanced) {
      // remember the new setting
      this.advanced = pAdvanced;

      // make sure all nodes have valid user objects
      checkForUserObject((SmallStepProofNode) this.proofModel.getRoot());

      // update all active nodes
      Enumeration<ProofNode> enumeration = this.proofModel.getRoot().postorderEnumeration();
      while (enumeration.hasMoreElements()) {
        // tell the component belonging to this node, that we have a new advanced state
        SmallStepProofNode node = (SmallStepProofNode) enumeration.nextElement();
        SmallStepNodeComponent component = (SmallStepNodeComponent) node.getUserObject();
        component.setAdvanced(pAdvanced);
      }
    }
  }
  /**
   * Traverses the ProofTree recursivly and checks the size of the expression for every node.
   *
   * @param node When calling this method: the rootNode of the tree.
   */
  void checkExpressionSize(SmallStepProofNode node) {
    if (node == null) {
      return;
    }

    SmallStepNodeComponent nodeComponent = (SmallStepNodeComponent) node.getUserObject();
    nodeComponent.checkNeededExpressionSize(this.availableWidth - this.border);

    // proceed with the next child
    checkExpressionSize(getFirstChild(node));
  }
  /**
   * Traverses the ProofTree recursivly and informing every node for the maximum size of the rule
   * combo on the left-hand-side.
   *
   * @param node When calling this method: the rootNode of the tree.
   * @param maxRuleWidth The maximum Width of the rules.
   */
  void updateMaxRuleWidth(SmallStepProofNode node, int maxRuleWidth) {
    if (node == null) {
      return;
    }

    // inform the node of the max rule width
    SmallStepNodeComponent nodeComponent = (SmallStepNodeComponent) node.getUserObject();
    nodeComponent.setMaxRuleWidth(maxRuleWidth);

    // proceed with the next child node
    updateMaxRuleWidth(getFirstChild(node), maxRuleWidth);
  }
  /**
   * Traversing the ProofTree recursivly and adds a SmallStepNodeComponent where none is.<br>
   * <br>
   * Usualy only at newly added nodes the SmallStepNodeComponent is missing.
   *
   * @param node When calling this method: the rootNode of the tree.
   */
  void checkForUserObject(SmallStepProofNode node) {
    if (node == null) {
      return;
    }

    SmallStepNodeComponent nodeComponent = (SmallStepNodeComponent) node.getUserObject();
    if (nodeComponent == null) {

      // create the noded that has not been there yet
      nodeComponent =
          new SmallStepNodeComponent(
              node, this.model, this.translator, this.spacing, this.advanced);

      // add the needed listener
      nodeComponent.addSmallStepNodeListener(
          new SmallStepNodeListener() {
            public void nodeChanged(SmallStepNodeComponent pNode) {
              SmallStepComponent.this.relayout();
            }

            public void repaintAll() {
              SmallStepComponent.this.repaint();
            }

            public void requestJumpToNode(ProofNode pNode) {
              SmallStepComponent.this.setJumpNode(pNode);
            }
          });

      nodeComponent.update();

      // save it to the node
      node.setUserObject(nodeComponent);

      // and add the SmallStepNodeComponent to the gui
      add(nodeComponent);
    }

    checkForUserObject(getFirstChild(node));
  }
  /**
   * Causes all userobject from all nodes to reset the layout.<br>
   * <br>
   * Resetting means that every {@link PrettyStringRenderer} and {@link EnvironmentRenderer}
   * recalculates their needed font sizes.
   */
  private void resetUserObject(SmallStepProofNode node) {
    if (node == null) {
      return;
    }

    SmallStepNodeComponent nodeComponent = (SmallStepNodeComponent) node.getUserObject();
    if (nodeComponent == null) {
      return;
    }

    nodeComponent.reset();

    resetUserObject(getFirstChild(node));
  }
  /**
   * Traverses the ProofTree recursivly and checks the needed size for the rule combo on the
   * left-hand-side.<br>
   * <br>
   * The <i>currentWidth</i> is the current maximum width that has been evaluated. When calling this
   * function this should be somthing small. Just set it to <b>0</b>.
   *
   * @param node When calling this method: the rootNode of the tree.
   * @param pCurrentWidth Used internaly. Should be set to <b>0</b>.
   * @return
   */
  int checkMaxRuleWidth(SmallStepProofNode node, int pCurrentWidth) {
    int currentWidth = pCurrentWidth;
    if (node == null) {
      return currentWidth;
    }

    // get the size of the current node
    SmallStepNodeComponent nodeComponent = (SmallStepNodeComponent) node.getUserObject();
    int nodeWidth = nodeComponent.getMinRuleSize().width;

    // only the maximum width is of interest
    currentWidth = Math.max(currentWidth, nodeWidth);

    // return the recursive result of the next node

    return checkMaxRuleWidth(getFirstChild(node), currentWidth);
  }
  /**
   * Iterates through the entire tree an places every node.<br>
   * <br>
   * There are some things to take care of, when the nodes get placed:<br>
   * <br>
   * The expression and the rules of the parent node are in one row so when the nodes get placed the
   * actualHeight of each node must be the maximum of both. They are placed together in on step. So
   * the rules of each node are placed together with the expression of its child node, if there is
   * one.<br>
   * If there is no parent node (that would be the first node, the root node), only the expression
   * needs to get places.<br>
   * If the node has no child (that would be the last node in the tree), the rules must be placed
   * directly because there is no child node that would place them.
   *
   * @param node The rootNode
   * @param pX The horizontal start position
   * @param pY Ther vertical start position
   * @return The size needed to show all the nodes.
   */
  Dimension placeNode(SmallStepProofNode pNode, int pX, int pY) {
    int x = pX;
    int y = pY;

    this.actualPageSpaceCounter = y;

    SmallStepProofNode node = pNode;
    Dimension size = new Dimension(0, 0);

    // save the space the next node will be moved down
    int movedDown = 0;

    int lastNodeHeight = this.actualPageSpaceCounter;

    while (node != null) {
      SmallStepNodeComponent nodeComponent = (SmallStepNodeComponent) node.getUserObject();

      // set the origin of this node
      nodeComponent.setOrigion(new Point(x, y));

      // if the node has no parent node it appears to be the rootNode
      //
      // the expression of the rootNode can be placed without checking anything
      if (node.getParent() == null) {
        nodeComponent.placeExpression();

        // move the positioning
        y += nodeComponent.getRuleTop();
        this.actualPageSpaceCounter = y;

        // evaluate the new dimensions
        size.height = y;

      } else {
        // evaluate the max size of this nodes expression and the parent
        // nodes rules
        SmallStepProofNode parentNode = node.getParent();
        SmallStepNodeComponent parentNodeComponent =
            (SmallStepNodeComponent) parentNode.getUserObject();

        Dimension expSize = nodeComponent.getExpressionSize();
        Dimension ruleSize = parentNodeComponent.getRuleSize();

        int maxHeight = Math.max(expSize.height, ruleSize.height);

        // provide printing
        // if the actualPageSpaceCounter has not enough space for the next node perform a pagebrak
        if (this.actualPageSpaceCounter + maxHeight + lastNodeHeight > this.availableHeight) {
          {
            // save the space the node is moved down
            movedDown = (this.availableHeight - this.actualPageSpaceCounter) / 2; // Wäää?

            // move the next node down
            y += movedDown;

            // restart the actualPageSpaceCounter
            this.actualPageSpaceCounter = -movedDown; // Wäää?

            // inform both component about the actual height they should use to
            // place them
            parentNodeComponent.setActualRuleHeight(maxHeight);
            nodeComponent.setActualExpressionHeight(maxHeight);

            // let both components place theire elements
            parentNodeComponent.placeRules();
            nodeComponent.placeExpression();

            // this finishes the parentNode so it can be placed
            parentNodeComponent.setBounds();

            // the additional height come from the actual node
            y += nodeComponent.getRuleTop();
            this.actualPageSpaceCounter += nodeComponent.getRuleTop();

            // System.out.println(maxHeight+" - "+nodeComponent.getRuleTop());

            // evaluate the new dimensions
            size.height = y;

            // the actual width of the entire component can now be checked
            // on the finshed node. the parent node
            size.width = Math.max(size.width, x + parentNodeComponent.getSize().width);
          }
        } else {

          // inform both component about the actual height they should use to
          // place them
          parentNodeComponent.setActualRuleHeight(maxHeight);
          nodeComponent.setActualExpressionHeight(maxHeight);

          // let both components place theire elements
          // the movedDown saves the information how far the the parent-Rules must be moved down...
          parentNodeComponent.placeRules(movedDown);
          nodeComponent.placeExpression();

          // this finishes the parentNode so it can be placed
          parentNodeComponent.setBounds(movedDown);

          movedDown = 0;

          // the additional height come from the actual node
          y += nodeComponent.getRuleTop();
          this.actualPageSpaceCounter += nodeComponent.getRuleTop();
          // System.out.println(maxHeight+" - "+nodeComponent.getRuleTop());

          // evaluate the new dimensions
          size.height = y;

          // the actual width of the entire component can now be checked
          // on the finshed node. the parent node
          size.width = Math.max(size.width, x + parentNodeComponent.getSize().width);
        }

        lastNodeHeight = maxHeight;
        // tmpPaper += maxHeight;

      }

      // if the node has no children the rules need to get
      // placed here with the expression
      if (getFirstChild(node) == null) {

        if (this.model.isFinished()) {
          nodeComponent.hideRules();
          nodeComponent.setBounds();

          size.width = Math.max(size.width, x + nodeComponent.getSize().width);
        } else {
          // the rules can savely be positioned
          nodeComponent.placeRules();

          // and the node itself can be placed
          nodeComponent.setBounds();

          // evaluate the new dimension
          size.height += nodeComponent.getActualRuleHeight();

          // the actual width of the entire component can now be checked
          // on the finshed node.
          size.width = Math.max(size.width, x + nodeComponent.getSize().width);
        }

        return size;
      }

      node = node.getFirstChild();
    }

    return size;
  }