Exemple #1
0
 private void checkLayout(Node root) {
   if (root instanceof Split) {
     Split split = (Split) root;
     if (split.getChildren().size() <= 2) {
       throwInvalidLayout("Split must have > 2 children", root);
     }
     Iterator<Node> splitChildren = split.getChildren().iterator();
     double weight = 0.0;
     while (splitChildren.hasNext()) {
       Node splitChild = splitChildren.next();
       if (splitChild instanceof Divider) {
         throwInvalidLayout("expected a Split or Leaf Node", splitChild);
       }
       if (splitChildren.hasNext()) {
         Node dividerChild = splitChildren.next();
         if (!(dividerChild instanceof Divider)) {
           throwInvalidLayout("expected a Divider Node", dividerChild);
         }
       }
       weight += splitChild.getWeight();
       checkLayout(splitChild);
     }
     if (weight > 1.0 + 0.000000001) {
         /* add some epsilon to a double check */
       throwInvalidLayout("Split children's total weight > 1.0", root);
     }
   }
 }
Exemple #2
0
 private Dimension minimumNodeSize(Node root) {
   if (root instanceof Leaf) {
     Component child = childForNode(root);
     return (child != null) ? child.getMinimumSize() : new Dimension(0, 0);
   } else if (root instanceof Divider) {
     int dividerSize = getDividerSize();
     return new Dimension(dividerSize, dividerSize);
   } else {
     Split split = (Split) root;
     List<Node> splitChildren = split.getChildren();
     int width = 0;
     int height = 0;
     if (split.isRowLayout()) {
       for (Node splitChild : splitChildren) {
         Dimension size = minimumNodeSize(splitChild);
         width += size.width;
         height = Math.max(height, size.height);
       }
     } else {
       for (Node splitChild : splitChildren) {
         Dimension size = minimumNodeSize(splitChild);
         width = Math.max(width, size.width);
         height += size.height;
       }
     }
     return new Dimension(width, height);
   }
 }
Exemple #3
0
 private Dimension preferredNodeSize(Node root) {
   if (root instanceof Leaf) return preferredComponentSize(root);
   else if (root instanceof Divider) {
     int dividerSize = getDividerSize();
     return new Dimension(dividerSize, dividerSize);
   } else {
     Split split = (Split) root;
     List<Node> splitChildren = split.getChildren();
     int width = 0;
     int height = 0;
     if (split.isRowLayout()) {
       for (Node splitChild : splitChildren) {
         Dimension size = preferredNodeSize(splitChild);
         width += size.width;
         height = Math.max(height, size.height);
       }
     } else {
       for (Node splitChild : splitChildren) {
         Dimension size = preferredNodeSize(splitChild);
         width = Math.max(width, size.width);
         height += size.height;
       }
     }
     return new Dimension(width, height);
   }
 }
Exemple #4
0
 private Node siblingAtOffset(int offset) {
   Split parent = parent_get();
   if (parent == null) return null;
   List<Node> siblings = parent.getChildren();
   int index = siblings.indexOf(this);
   if (index == -1) return null;
   index += offset;
   return ((index > -1) && (index < siblings.size())) ? siblings.get(index) : null;
 }
Exemple #5
0
 private static void addSplitChild(Split parent, Node child) {
   List<Node> children = new ArrayList<Node>(parent.getChildren());
   if (children.size() == 0) {
     children.add(child);
   } else {
     children.add(new Divider());
     children.add(child);
   }
   parent.setChildren(children);
 }
Exemple #6
0
 private static void printModel(String indent, Node root) {
   if (root instanceof Split) {
     Split split = (Split) root;
     System.out.println(indent + split);
     for (Node child : split.getChildren()) {
       printModel(indent + "  ", child);
     }
   } else {
     System.out.println(indent + root);
   }
 }
Exemple #7
0
 private Divider dividerAt(Node root, int x, int y) {
   if (root instanceof Divider) {
     Divider divider = (Divider) root;
     return (divider.getBounds().contains(x, y)) ? divider : null;
   } else if (root instanceof Split) {
     Split split = (Split) root;
     for (Node child : split.getChildren()) {
       if (child.getBounds().contains(x, y)) return dividerAt(child, x, y);
     }
   }
   return null;
 }
Exemple #8
0
 private static Node parseModel(Reader r) {
   StreamTokenizer st = new StreamTokenizer(r);
   try {
     Split root = new Split();
     parseSplit(st, root);
     return root.getChildren().get(0);
   } catch (Exception e) {
     Main.error(e);
   } finally {
     Utils.close(r);
   }
   return null;
 }
Exemple #9
0
 private void minimizeSplitBounds(Split split, Rectangle bounds) {
   Rectangle splitBounds = new Rectangle(bounds.x, bounds.y, 0, 0);
   List<Node> splitChildren = split.getChildren();
   Node lastChild = splitChildren.get(splitChildren.size() - 1);
   Rectangle lastChildBounds = lastChild.getBounds();
   if (split.isRowLayout()) {
     int lastChildMaxX = lastChildBounds.x + lastChildBounds.width;
     splitBounds.add(lastChildMaxX, bounds.y + bounds.height);
   } else {
     int lastChildMaxY = lastChildBounds.y + lastChildBounds.height;
     splitBounds.add(bounds.x + bounds.width, lastChildMaxY);
   }
   split.setBounds(splitBounds);
 }
Exemple #10
0
 private static Node parseModel(Reader r) {
   StreamTokenizer st = new StreamTokenizer(r);
   try {
     Split root = new Split();
     parseSplit(st, root);
     return root.getChildren().get(0);
   } catch (Exception e) {
     System.err.println(e);
   } finally {
     try {
       r.close();
     } catch (IOException ignore) {
     }
   }
   return null;
 }
Exemple #11
0
  /* First pass of the layout algorithm.
   *
   * If the Dividers are "floating" then set the bounds of each
   * node to accomodate the preferred size of all of the
   * Leaf's java.awt.Components.  Otherwise, just set the bounds
   * of each Leaf/Split node so that it's to the left of (for
   * Split.isRowLayout() Split children) or directly above
   * the Divider that follows.
   *
   * This pass sets the bounds of each Node in the layout model.  It
   * does not resize any of the parent Container's
   * (java.awt.Component) children.  That's done in the second pass,
   * see layoutGrow() and layoutShrink().
   */
  private void layout1(Node root, Rectangle bounds) {
    if (root instanceof Leaf) {
      root.setBounds(bounds);
    } else if (root instanceof Split) {
      Split split = (Split) root;
      Iterator<Node> splitChildren = split.getChildren().iterator();
      Rectangle childBounds = null;
      int dividerSize = getDividerSize();

      /* Layout the Split's child Nodes' along the X axis.  The bounds
       * of each child will have the same y coordinate and height as the
       * layout1() bounds argument.
       *
       * Note: the column layout code - that's the "else" clause below
       * this if, is identical to the X axis (rowLayout) code below.
       */
      if (split.isRowLayout()) {
        double x = bounds.getX();
        while (splitChildren.hasNext()) {
          Node splitChild = splitChildren.next();
          Divider dividerChild =
              (splitChildren.hasNext()) ? (Divider) (splitChildren.next()) : null;

          double childWidth = 0.0;
          if (getFloatingDividers()) {
            childWidth = preferredNodeSize(splitChild).getWidth();
          } else {
            if (dividerChild != null) {
              childWidth = dividerChild.getBounds().getX() - x;
            } else {
              childWidth = split.getBounds().getMaxX() - x;
            }
          }
          childBounds = boundsWithXandWidth(bounds, x, childWidth);
          layout1(splitChild, childBounds);

          if (getFloatingDividers() && (dividerChild != null)) {
            double dividerX = childBounds.getMaxX();
            Rectangle dividerBounds = boundsWithXandWidth(bounds, dividerX, dividerSize);
            dividerChild.setBounds(dividerBounds);
          }
          if (dividerChild != null) {
            x = dividerChild.getBounds().getMaxX();
          }
        }
      }

      /* Layout the Split's child Nodes' along the Y axis.  The bounds
       * of each child will have the same x coordinate and width as the
       * layout1() bounds argument.  The algorithm is identical to what's
       * explained above, for the X axis case.
       */
      else {
        double y = bounds.getY();
        while (splitChildren.hasNext()) {
          Node splitChild = splitChildren.next();
          Divider dividerChild =
              (splitChildren.hasNext()) ? (Divider) (splitChildren.next()) : null;

          double childHeight = 0.0;
          if (getFloatingDividers()) {
            childHeight = preferredNodeSize(splitChild).getHeight();
          } else {
            if (dividerChild != null) {
              childHeight = dividerChild.getBounds().getY() - y;
            } else {
              childHeight = split.getBounds().getMaxY() - y;
            }
          }
          childBounds = boundsWithYandHeight(bounds, y, childHeight);
          layout1(splitChild, childBounds);

          if (getFloatingDividers() && (dividerChild != null)) {
            double dividerY = childBounds.getMaxY();
            Rectangle dividerBounds = boundsWithYandHeight(bounds, dividerY, dividerSize);
            dividerChild.setBounds(dividerBounds);
          }
          if (dividerChild != null) {
            y = dividerChild.getBounds().getMaxY();
          }
        }
      }
      /* The bounds of the Split node root are set to be just
       * big enough to contain all of its children, but only
       * along the axis it's allocating space on.  That's
       * X for rows, Y for columns.  The second pass of the
       * layout algorithm - see layoutShrink()/layoutGrow()
       * allocates extra space.
       */
      minimizeSplitBounds(split, bounds);
    }
  }
Exemple #12
0
  private void layoutGrow(Split split, Rectangle bounds) {
    Rectangle splitBounds = split.getBounds();
    ListIterator<Node> splitChildren = split.getChildren().listIterator();
    Node lastWeightedChild = split.lastWeightedChild();

    /* Layout the Split's child Nodes' along the X axis.  The bounds
     * of each child will have the same y coordinate and height as the
     * layoutGrow() bounds argument.  Extra width is allocated to the
     * to each child with a non-zero weight:
     *     newWidth = currentWidth + (extraWidth * splitChild.getWeight())
     * Any extraWidth "left over" (that's availableWidth in the loop
     * below) is given to the last child.  Note that Dividers always
     * have a weight of zero, and they're never the last child.
     */
    if (split.isRowLayout()) {
      double x = bounds.getX();
      double extraWidth = bounds.getWidth() - splitBounds.getWidth();
      double availableWidth = extraWidth;

      while (splitChildren.hasNext()) {
        Node splitChild = splitChildren.next();
        Rectangle splitChildBounds = splitChild.getBounds();
        double splitChildWeight = splitChild.getWeight();

        if (!splitChildren.hasNext()) {
          double newWidth = bounds.getMaxX() - x;
          Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth);
          layout2(splitChild, newSplitChildBounds);
        } else if ((availableWidth > 0.0) && (splitChildWeight > 0.0)) {
          double allocatedWidth =
              (splitChild.equals(lastWeightedChild))
                  ? availableWidth
                  : Math.rint(splitChildWeight * extraWidth);
          double newWidth = splitChildBounds.getWidth() + allocatedWidth;
          Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth);
          layout2(splitChild, newSplitChildBounds);
          availableWidth -= allocatedWidth;
        } else {
          double existingWidth = splitChildBounds.getWidth();
          Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, existingWidth);
          layout2(splitChild, newSplitChildBounds);
        }
        x = splitChild.getBounds().getMaxX();
      }
    }

    /* Layout the Split's child Nodes' along the Y axis.  The bounds
     * of each child will have the same x coordinate and width as the
     * layoutGrow() bounds argument.  Extra height is allocated to the
     * to each child with a non-zero weight:
     *     newHeight = currentHeight + (extraHeight * splitChild.getWeight())
     * Any extraHeight "left over" (that's availableHeight in the loop
     * below) is given to the last child.  Note that Dividers always
     * have a weight of zero, and they're never the last child.
     */
    else {
      double y = bounds.getY();
      double extraHeight = bounds.getMaxY() - splitBounds.getHeight();
      double availableHeight = extraHeight;

      while (splitChildren.hasNext()) {
        Node splitChild = splitChildren.next();
        Rectangle splitChildBounds = splitChild.getBounds();
        double splitChildWeight = splitChild.getWeight();

        if (!splitChildren.hasNext()) {
          double newHeight = bounds.getMaxY() - y;
          Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight);
          layout2(splitChild, newSplitChildBounds);
        } else if ((availableHeight > 0.0) && (splitChildWeight > 0.0)) {
          double allocatedHeight =
              (splitChild.equals(lastWeightedChild))
                  ? availableHeight
                  : Math.rint(splitChildWeight * extraHeight);
          double newHeight = splitChildBounds.getHeight() + allocatedHeight;
          Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight);
          layout2(splitChild, newSplitChildBounds);
          availableHeight -= allocatedHeight;
        } else {
          double existingHeight = splitChildBounds.getHeight();
          Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, existingHeight);
          layout2(splitChild, newSplitChildBounds);
        }
        y = splitChild.getBounds().getMaxY();
      }
    }
  }
Exemple #13
0
  private void layoutShrink(Split split, Rectangle bounds) {
    Rectangle splitBounds = split.getBounds();
    ListIterator<Node> splitChildren = split.getChildren().listIterator();

    if (split.isRowLayout()) {
      int totalWidth = 0; // sum of the children's widths
      int minWeightedWidth = 0; // sum of the weighted childrens' min widths
      int totalWeightedWidth = 0; // sum of the weighted childrens' widths
      for (Node splitChild : split.getChildren()) {
        int nodeWidth = splitChild.getBounds().width;
        int nodeMinWidth = Math.min(nodeWidth, minimumNodeSize(splitChild).width);
        totalWidth += nodeWidth;
        if (splitChild.getWeight() > 0.0) {
          minWeightedWidth += nodeMinWidth;
          totalWeightedWidth += nodeWidth;
        }
      }

      double x = bounds.getX();
      double extraWidth = splitBounds.getWidth() - bounds.getWidth();
      double availableWidth = extraWidth;
      boolean onlyShrinkWeightedComponents = (totalWeightedWidth - minWeightedWidth) > extraWidth;

      while (splitChildren.hasNext()) {
        Node splitChild = splitChildren.next();
        Rectangle splitChildBounds = splitChild.getBounds();
        double minSplitChildWidth = minimumNodeSize(splitChild).getWidth();
        double splitChildWeight =
            (onlyShrinkWeightedComponents)
                ? splitChild.getWeight()
                : (splitChildBounds.getWidth() / totalWidth);

        if (!splitChildren.hasNext()) {
          double newWidth = Math.max(minSplitChildWidth, bounds.getMaxX() - x);
          Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth);
          layout2(splitChild, newSplitChildBounds);
        } else if ((availableWidth > 0.0) && (splitChildWeight > 0.0)) {
          double allocatedWidth = Math.rint(splitChildWeight * extraWidth);
          double oldWidth = splitChildBounds.getWidth();
          double newWidth = Math.max(minSplitChildWidth, oldWidth - allocatedWidth);
          Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, newWidth);
          layout2(splitChild, newSplitChildBounds);
          availableWidth -= (oldWidth - splitChild.getBounds().getWidth());
        } else {
          double existingWidth = splitChildBounds.getWidth();
          Rectangle newSplitChildBounds = boundsWithXandWidth(bounds, x, existingWidth);
          layout2(splitChild, newSplitChildBounds);
        }
        x = splitChild.getBounds().getMaxX();
      }
    } else {
      int totalHeight = 0; // sum of the children's heights
      int minWeightedHeight = 0; // sum of the weighted childrens' min heights
      int totalWeightedHeight = 0; // sum of the weighted childrens' heights
      for (Node splitChild : split.getChildren()) {
        int nodeHeight = splitChild.getBounds().height;
        int nodeMinHeight = Math.min(nodeHeight, minimumNodeSize(splitChild).height);
        totalHeight += nodeHeight;
        if (splitChild.getWeight() > 0.0) {
          minWeightedHeight += nodeMinHeight;
          totalWeightedHeight += nodeHeight;
        }
      }

      double y = bounds.getY();
      double extraHeight = splitBounds.getHeight() - bounds.getHeight();
      double availableHeight = extraHeight;
      boolean onlyShrinkWeightedComponents =
          (totalWeightedHeight - minWeightedHeight) > extraHeight;

      while (splitChildren.hasNext()) {
        Node splitChild = splitChildren.next();
        Rectangle splitChildBounds = splitChild.getBounds();
        double minSplitChildHeight = minimumNodeSize(splitChild).getHeight();
        double splitChildWeight =
            (onlyShrinkWeightedComponents)
                ? splitChild.getWeight()
                : (splitChildBounds.getHeight() / totalHeight);

        if (!splitChildren.hasNext()) {
          double oldHeight = splitChildBounds.getHeight();
          double newHeight = Math.max(minSplitChildHeight, bounds.getMaxY() - y);
          Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight);
          layout2(splitChild, newSplitChildBounds);
          availableHeight -= (oldHeight - splitChild.getBounds().getHeight());
        } else if ((availableHeight > 0.0) && (splitChildWeight > 0.0)) {
          double allocatedHeight = Math.rint(splitChildWeight * extraHeight);
          double oldHeight = splitChildBounds.getHeight();
          double newHeight = Math.max(minSplitChildHeight, oldHeight - allocatedHeight);
          Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, newHeight);
          layout2(splitChild, newSplitChildBounds);
          availableHeight -= (oldHeight - splitChild.getBounds().getHeight());
        } else {
          double existingHeight = splitChildBounds.getHeight();
          Rectangle newSplitChildBounds = boundsWithYandHeight(bounds, y, existingHeight);
          layout2(splitChild, newSplitChildBounds);
        }
        y = splitChild.getBounds().getMaxY();
      }
    }

    /* The bounds of the Split node root are set to be
     * big enough to contain all of its children. Since
     * Leaf children can't be reduced below their
     * (corresponding java.awt.Component) minimum sizes,
     * the size of the Split's bounds maybe be larger than
     * the bounds we were asked to fit within.
     */
    minimizeSplitBounds(split, bounds);
  }