public void updateChildren(VisualConnection connectionNode, BarState currentState) {
   for (VisualConnection child : connectionNode.getChildren())
     if (child.getNode().isVisible()) {
       child.setState(currentState);
       updateChildren(child, currentState);
     }
 }
  private void display(Graphics2D g, VisualConnection node, float alpha) {

    node.paint(g, alpha);

    for (VisualConnection child : node.getChildren())
      if (child.getNode().isVisible()) display(g, child, alpha);
  }
  private void moveCategory(
      DimensionHandle dimension, CategoryHandle category, int index, VisualConnection node) {

    if (node.getChildren() == null) return;

    ArrayList<VisualConnection> children = node.getChildren();

    if (!children.isEmpty()
        && children.get(0).getNode().getToCategory().getDimension().equals(dimension)) {

      VisualConnection moveCat = null;

      for (VisualConnection child : children)
        if (child.getNode().getToCategory().equals(category)) moveCat = child;

      if (moveCat != null) {
        children.remove(moveCat);
        if (index < children.size()) children.add(index, moveCat);
        else children.add(moveCat);
      }
    } else {
      for (VisualConnection child : children) {
        moveCategory(dimension, category, index, child);
      }
    }
  }
  /**
   * Lay out the leftmost incomplete branch of this connection node.
   *
   * @param connectionNode
   */
  public boolean layoutBranch(
      VisualConnection connectionNode, BarState currentState, int totalWidth) {

    // If this node isn't laid out already, lay it out.
    if (!connectionNode.isLaidOut()) {

      connectionNode.layout(connectionNode.getParent().getWidth());
      connectionNode.setLaidOut(true);

      // If this is a leaf, this node is completed.
      if (connectionNode.getChildren().isEmpty()) {
        connectionNode.setComplete(true);
        return true;
      }
    }

    // If this is not a leaf, call layoutBranch on this node's
    // leftmost incomplete child.
    for (VisualConnection child : connectionNode.getChildren())
      if (!child.isComplete())
        if (layoutBranch(child, currentState, totalWidth) == false) return false;

    // If all of the children are complete, this node is
    // completed.
    connectionNode.setComplete(true);

    return true;
  }
  private void orderChildren(
      DimensionHandle dimension, List<CategoryHandle> order, VisualConnection node) {
    if (node.getChildren() == null) return;

    if (!node.getChildren().isEmpty()
        && node.getChildren().get(0).getNode().getToCategory().getDimension().equals(dimension))
      orderChildren(node, order);
    else for (VisualConnection vc : node.getChildren()) orderChildren(dimension, order, vc);
  }
 public String highlightRibbon(int x, int y, CategoryTree dataTree) {
   clearSelection();
   VisualConnection selectedRibbon = highlightRibbon(x, y, root);
   if (selectedRibbon != null) {
     setSelected(selectedRibbon);
     return selectedRibbon.getTooltip(dataTree.getFilteredTotal());
   }
   return null;
 }
  private void addChildren(
      VisualConnection parentNode,
      CategoryNode dataNode,
      ArrayList<VisualAxis> axes,
      int childLevel) {

    if (childLevel == 1) {

      /* The first level is made up of invisible connections.
       * These can be thought of as ribbons that represent only
       * a single category in the first dimension, which then
       * branch out to form the lower ribbons.  They're not visible
       * because they're redundant with the first level category
       * bars, but they're convenient for treating the ribbons as
       * a tree.
       */
      for (CategoryNode child : dataNode.getChildren()) {
        VisualConnection newChild = parentNode.addChild(new InvisibleConnection(parentNode, child));
        addChildren(newChild, child, axes, childLevel + 1);
      }

      orderChildren(parentNode, ((CategoricalAxis) axes.get(0)).getCategoryOrder());
    } else if (childLevel <= axes.size()) {

      // The lower levels are ribbons.
      if (currentRState == RibbonState.BASIC) {
        for (CategoryNode child : dataNode.getChildren()) {
          VisualConnection newChild =
              parentNode.addChild(
                  new BasicRibbon(
                      parentNode,
                      child,
                      (CategoricalAxis) axes.get(childLevel - 2),
                      (CategoricalAxis) axes.get(childLevel - 1)));

          addChildren(newChild, child, axes, childLevel + 1);
        }

        orderChildren(parentNode, ((CategoricalAxis) axes.get(childLevel - 1)).getCategoryOrder());
      } else if (currentRState == RibbonState.CURVED) {
        for (CategoryNode child : dataNode.getChildren()) {
          VisualConnection newChild =
              parentNode.addChild(
                  new CurvedRibbon(
                      parentNode,
                      child,
                      (CategoricalAxis) axes.get(childLevel - 2),
                      (CategoricalAxis) axes.get(childLevel - 1)));

          addChildren(newChild, child, axes, childLevel + 1);
        }

        orderChildren(parentNode, ((CategoricalAxis) axes.get(childLevel - 1)).getCategoryOrder());
      }
    }
  }
  /**
   * Lays out the ribbons in a bundled style. Basically, this means doing a series of phased
   * depth-first traversals on each of the top-level category nodes.
   *
   * @param minX The left bound of the display space.
   * @param maxX The right bound of the display space.
   */
  public void doLayoutBundled(int minX, int maxX, BarState currentState) {

    root.setWidth(maxX - minX);

    // Set all the layout and completeness flags to false.
    resetForBundling(root);

    // If there are no connections, return.
    if (!root.getChildren().isEmpty()) {

      // While the root is not set to complete...
      while (!root.isComplete()) {

        // Iterate through each of the invisible nodes, each of
        // which represents a top-level category bar. At each
        // step, lay out the leftmost incomplete branch of the
        // top-level node.
        for (VisualConnection invisible : root.getChildren()) {

          if (!invisible.isComplete()) layoutBranch(invisible, currentState, (maxX - minX));

          // If this node is complete and is the last of the root's
          // children, then we're done.
          else if (invisible == root.getChildren().get(root.getChildren().size() - 1))
            root.setComplete(true);
        }
      }
    }
    setColors(root);
  }
  private VisualConnection highlightRibbon(int x, int y, VisualConnection node) {

    VisualConnection returnNode = null;

    if (node.contains(x, y)) returnNode = node;

    for (VisualConnection child : node.getChildren()) {
      VisualConnection temp = highlightRibbon(x, y, child);
      if (returnNode == null) returnNode = temp;
    }

    return returnNode;
  }
  private void orderChildren(VisualConnection parentNode, List<CategoryHandle> categoryOrder) {

    ArrayList<VisualConnection> newList = new ArrayList<VisualConnection>();

    newList.addAll(parentNode.getChildren());

    parentNode.getChildren().clear();

    for (CategoryHandle cat : categoryOrder) {
      for (VisualConnection ribbon : newList) {
        if (ribbon.getNode().getToCategory().equals(cat)) {
          parentNode.getChildren().add(ribbon);
        }
      }
    }
  }
  public void setColors(VisualConnection connectionNode) {

    // The top level categorical axis determines the color scheme.
    if (connectionNode.getChildren().isEmpty()) return;

    if (connectionNode == root) {
      for (VisualConnection childNode : connectionNode.getChildren()) {
        childNode.setColorBrewerIndex(childNode.getNode().getToCategory().getCategoryNum() - 1);
        setColors(childNode);
      }
    } else {
      for (VisualConnection childNode : connectionNode.getChildren()) {
        childNode.setColorBrewerIndex(connectionNode.getColorBrewerIndex());
        setColors(childNode);
      }
    }
  }
 public void layoutChildren(VisualConnection connectionNode, BarState currentState) {
   for (VisualConnection child : connectionNode.getChildren())
     if (child.getNode().isVisible()) {
       child.setState(currentState);
       child.layout(connectionNode.getFutureWidth());
       layoutChildren(child, currentState);
     }
 }
 public void print(VisualConnection node) {
   System.out.println(node.toString());
   for (VisualConnection child : node.getChildren()) print(child);
 }
 public void doLayoutBranching(int minX, int maxX, BarState currentState) {
   root.setWidth(maxX - minX);
   layoutChildren(root, currentState);
   setColors(root);
 }
 public void findCategoryBarNode(CategoryHandle category, VisualConnection node) {
   if (!node.equals(root) && node.getNode().getToCategory().equals(category)) {
     test = node;
   } else for (VisualConnection child : node.getChildren()) findCategoryBarNode(category, child);
 }
  private void selectCategory(CategoryHandle category, VisualConnection node) {

    if (!node.equals(root) && node.getNode().getToCategory().equals(category))
      for (VisualConnection child : node.getChildren()) selectDown(child);
    else for (VisualConnection child : node.getChildren()) selectCategory(category, child);
  }
  public void resetForBundling(VisualConnection connectionNode) {
    connectionNode.setComplete(false);
    connectionNode.setLaidOut(false);

    for (VisualConnection child : connectionNode.getChildren()) resetForBundling(child);
  }
  private void clearSelection(VisualConnection node) {

    node.setSelected(false);

    for (VisualConnection child : node.getChildren()) clearSelection(child);
  }
  private void selectUp(VisualConnection connection) {

    connection.setSelected(true);

    if (!connection.equals(root)) selectUp(connection.getParent());
  }
  private void selectDown(VisualConnection connection) {

    connection.setSelected(true);

    for (VisualConnection child : connection.getChildren()) selectDown(child);
  }
  private void displaySelected(Graphics2D g, VisualConnection node) {
    if (node.isSelected()) node.paintSelected(g);

    for (VisualConnection child : node.getChildren())
      if (child.getNode().isVisible()) displaySelected(g, child);
  }
 public void updateState(int minX, int maxX, BarState currentState) {
   root.setWidth(maxX - minX);
   updateChildren(root, currentState);
 }