diff --git a/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/internal/refresh/GMFHelper.java b/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/internal/refresh/GMFHelper.java index 24fb82a0ca..fcf9a7a7ef 100644 --- a/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/internal/refresh/GMFHelper.java +++ b/plugins/org.eclipse.sirius.diagram.ui/src-diag/org/eclipse/sirius/diagram/ui/internal/refresh/GMFHelper.java @@ -30,7 +30,6 @@ import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Insets; import org.eclipse.draw2d.geometry.Point; -import org.eclipse.draw2d.geometry.PrecisionPoint; import org.eclipse.draw2d.geometry.PrecisionRectangle; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.emf.common.util.EList; @@ -78,7 +77,6 @@ import org.eclipse.sirius.diagram.ui.business.internal.query.DNodeContainerQuery; import org.eclipse.sirius.diagram.ui.business.internal.query.DNodeQuery; import org.eclipse.sirius.diagram.ui.edit.api.part.AbstractDiagramElementContainerEditPart; -import org.eclipse.sirius.diagram.ui.edit.api.part.IDiagramNameEditPart; import org.eclipse.sirius.diagram.ui.internal.edit.parts.AbstractDNodeContainerCompartmentEditPart; import org.eclipse.sirius.diagram.ui.internal.edit.parts.DNodeContainer2EditPart; import org.eclipse.sirius.diagram.ui.internal.edit.parts.DNodeList2EditPart; @@ -160,17 +158,17 @@ public static Point getAbsoluteLocation(Node node) { */ public static Point getAbsoluteLocation(Node node, boolean insetsAware) { Node currentNode = node; - Point absoluteNodeLocation = getLocation(currentNode); + Point absoluteNodeLocation = getLocation(currentNode, insetsAware); // TODO: Location of title "DNode*NameEditPart", x coordinate, can be wrong according to label alignment. This // problem is not yet handled. - if (currentNode.eContainer() instanceof Node) { - currentNode = (Node) currentNode.eContainer(); - Point parentNodeLocation = getAbsoluteLocation(currentNode, insetsAware); - absoluteNodeLocation.translate(parentNodeLocation); - if (insetsAware) { - translateWithInsets(absoluteNodeLocation, node); - } - } + // if (currentNode.eContainer() instanceof Node) { + // currentNode = (Node) currentNode.eContainer(); + // Point parentNodeLocation = getAbsoluteLocation(currentNode, insetsAware); + // absoluteNodeLocation.translate(parentNodeLocation); + // if (insetsAware) { + // translateWithInsets(absoluteNodeLocation, node); + // } + // } return absoluteNodeLocation; } @@ -303,6 +301,11 @@ public static Dimension getBottomRightInsets(Node container) { result.setHeight(result.height() + borderSize.height()); } } + } else if (nodeQuery.isListCompartment()) { + // Add the corresponding margin of {1, 4, 0, 4} of + // org.eclipse.sirius.diagram.ui.internal.edit.parts.AbstractDNodeListCompartmentEditPart.createFigure() + result.setWidth(4); + result.setHeight(0); } return result; } @@ -429,10 +432,11 @@ private static void translateWithInsets(Rectangle boundsToTranslate, Node curren * the node whose location to compute. * @return the location of the node. */ - public static Point getLocation(Node node) { + public static Point getLocation(Node node, boolean insetsAware) { Point location = new Point(0, 0); LayoutConstraint layoutConstraint = node.getLayoutConstraint(); - if (layoutConstraint instanceof Bounds) { + if (layoutConstraint instanceof Bounds && !(new ViewQuery(node).isVerticalRegion())) { + // The bounds is computed for vertical region, even if it is stored in GMF data Bounds gmfBounds = (Bounds) layoutConstraint; location.setX(gmfBounds.getX()); location.setY(gmfBounds.getY()); @@ -451,44 +455,98 @@ public static Point getLocation(Node node) { } } } - } else if (new ViewQuery(node).isListCompartment()) { - // Translate from the title (previous children) - Point titleBottomRightCorner = getBottomRight((Node) node.eContainer(), node, false); - location.translate(0, titleBottomRightCorner.preciseY()); + } + if (new ViewQuery(node).isListCompartment()) { + // Translate the compartment to be just below the the title, the x coordinate is also the same (same parent + // insets) + Rectangle titleBounds = getAbsoluteBounds(getPreviousChild(node), true); + location.translate(titleBounds.preciseX(), titleBounds.preciseY() + titleBounds.preciseHeight()); // Translate from the spacing (5 pixels) location.translate(0, IContainerLabelOffsets.LABEL_OFFSET); } else if (new ViewQuery(node).isVerticalRegionContainerCompartment()) { - // Translate from the previous children (including title) - Point titleBottomRightCorner = getBottomRight((Node) node.eContainer(), node, false); - location.translate(0, titleBottomRightCorner.preciseY()); + // Translate the compartment to be just below the the title, the x coordinate is also the same (same parent + // insets) + Rectangle titleBounds = getAbsoluteBounds(getPreviousChild(node), true); + location.translate(titleBounds.preciseX(), titleBounds.preciseY() + titleBounds.preciseHeight()); // Translate from the spacing (5 pixels) location.translate(0, IContainerLabelOffsets.LABEL_OFFSET); } else if (new ViewQuery(node).isListItem()) { if (node.eContainer() instanceof Node container) { if (container.getChildren().get(0) == node) { + Point parentNodeLocation = getAbsoluteLocation(container, insetsAware); + location.translate(parentNodeLocation); + if (insetsAware) { + translateWithInsets(location, node); + } + // This is the first list item, add a one margin border over it. // location.translate(0, 1); TODO: Here or in parent insets ? } else { // Translate from the previous children - Point childrenBottomRightCorner = getBottomRight(container, node, false); - location.translate(0, childrenBottomRightCorner.preciseY()); + Rectangle previousChildBounds = getAbsoluteBounds(getPreviousChild(node), true); + location.translate(previousChildBounds.preciseX(), previousChildBounds.preciseY() + previousChildBounds.preciseHeight()); } } - } - if (new ViewQuery(node).isVerticalRegion()) { + } else if (new ViewQuery(node).isVerticalRegion()) { if (node.eContainer() instanceof Node container) { if (container.getChildren().get(0) == node) { - // This is the first vertical region. - // location.translate(0, 1); TODO: Here or in parent insets ? + Point parentNodeLocation = getAbsoluteLocation(container, insetsAware); + location.translate(parentNodeLocation); + if (insetsAware) { + translateWithInsets(location, node); + } } else { - // Replace location by the the previous children bottom corner - location = new PrecisionPoint(0, getBottomRight(container, node, false).preciseY()); + // Translate from the previous children + Rectangle previousChildBounds = getAbsoluteBounds(getPreviousChild(node), true); + location.translate(previousChildBounds.preciseX(), previousChildBounds.preciseY() + previousChildBounds.preciseHeight()); + } + } + } else { + if (node.eContainer() instanceof Node container) { + Point parentNodeLocation = getAbsoluteLocation(container, insetsAware); + location.translate(parentNodeLocation); + if (insetsAware) { + translateWithInsets(location, node); } } + } + // if (new ViewQuery(node).isVerticalRegion()) { + // if (node.eContainer() instanceof Node container) { + // if (container.getChildren().get(0) == node) { + // // This is the first vertical region. + // // location.translate(0, 1); TODO: Here or in parent insets ? + // } else { + // // Replace location by the the previous children bottom corner + // // location = new PrecisionPoint(0, getBottomRight(container, node, false).preciseY()); + // // location = new PrecisionPoint(0, getAbsoluteBounds(getPreviousChild(node), + // // true).getBottomRight().preciseY()); + // } + // } + // } return location; } + private static Node getPreviousChild(Node node) { + Node previousChild = null; + boolean found = false; + if (node.eContainer() instanceof Node container) { + for (Iterator children = Iterators.filter(container.getChildren().iterator(), Node.class); children.hasNext() && !found; /* */) { + Node child = children.next(); + if (node == child) { + found = true; + } else { + previousChild = child; + } + } + } + if (found) { + return previousChild; + } else { + return null; + } + } + private static void updateLocation(Point location, int position, Bounds parentBounds, Bounds gmfBounds) { switch (position) { case PositionConstants.NORTH: @@ -549,8 +607,27 @@ public static Rectangle getAbsoluteBounds(Node node, boolean insetsAware) { * @return the absolute bounds of the node relative to the origin (Diagram) */ public static Rectangle getAbsoluteBounds(Node node, boolean insetsAware, boolean boxForConnection) { - Rectangle bounds = getBounds(node, false, false, boxForConnection, false); + return getAbsoluteBounds(node, insetsAware, false, false); + } + + /** + * Get the absolute bounds relative to the origin (Diagram). + * + * @param node + * the GMF Node + * @param insetsAware + * true to consider the draw2D figures insets. Warning: Those insets are based on the + * current Sirius editParts and could become wrong if a developer customizes them. + * @param boxForConnection + * true if we want to have the bounds used to compute connection anchor from source or target, false + * otherwise + * @param recursiveGetBounds + * true if this method is called from a parent "getBounds" call, false otherwise. + * @return the absolute bounds of the node relative to the origin (Diagram) + */ + public static Rectangle getAbsoluteBounds(Node node, boolean insetsAware, boolean boxForConnection, boolean recursiveGetBounds) { Point location = getAbsoluteLocation(node, insetsAware); + Rectangle bounds = getBounds(node, false, false, boxForConnection, recursiveGetBounds, location); return new PrecisionRectangle(location.preciseX(), location.preciseY(), bounds.preciseWidth(), bounds.preciseHeight()); } @@ -700,15 +777,38 @@ public static Rectangle getBounds(Node node, boolean useFigureForAutoSizeConstra * @return the bounds of the node. */ public static Rectangle getBounds(Node node, boolean useFigureForAutoSizeConstraint, boolean forceFigureAutoSize, boolean boxForConnection, boolean recursiveGetBounds) { - PrecisionRectangle bounds = new PrecisionRectangle(0, 0, 0, 0); + return getBounds(node, useFigureForAutoSizeConstraint, forceFigureAutoSize, boxForConnection, recursiveGetBounds, new Point()); + } + + /** + * Compute the bounds of a GMF node. + * + * @param node + * the node whose bounds to compute. + * @param useFigureForAutoSizeConstraint + * true to use figure for auto size constraint + * @param forceFigureAutoSize + * if useFigureForAutoSizeConstraint and if the found edit part supports it, force auto size and validate + * the parent to get the auto-sized dimension (during auto-size for example) + * @param boxForConnection + * true if we want to have the bounds used to compute connection anchor from source or target, false + * otherwise + * @param recursiveGetBounds + * true if this method is called from a parent "getBounds" call, false otherwise. + * + * @return the bounds of the node. + */ + public static Rectangle getBounds(Node node, boolean useFigureForAutoSizeConstraint, boolean forceFigureAutoSize, boolean boxForConnection, boolean recursiveGetBounds, + Point computedAbsoluteLocation) { + PrecisionRectangle bounds = new PrecisionRectangle(computedAbsoluteLocation.preciseX(), computedAbsoluteLocation.preciseY(), 0, 0); LayoutConstraint layoutConstraint = node.getLayoutConstraint(); EObject element = node.getElement(); if (element instanceof AbstractDNode) { AbstractDNode abstractDNode = (AbstractDNode) element; - if (layoutConstraint instanceof Location) { - bounds.setX(((Location) layoutConstraint).getX()); - bounds.setY(((Location) layoutConstraint).getY()); - } + // if (layoutConstraint instanceof Location) { + // bounds.setX(((Location) layoutConstraint).getX()); + // bounds.setY(((Location) layoutConstraint).getY()); + // } if (layoutConstraint instanceof Size) { bounds.setWidth(((Size) layoutConstraint).getWidth()); bounds.setHeight(((Size) layoutConstraint).getHeight()); @@ -774,7 +874,7 @@ public static double getShadowBorderSize(Node node) { public static boolean isShadowBorderNeeded(Node node) { boolean needShadowBorder = false; EObject element = node.getElement(); - if (!new ViewQuery(node).isFreeFormCompartment() && element instanceof DDiagramElementContainer) { + if (!new ViewQuery(node).isFreeFormCompartment() && !new ViewQuery(node).isListCompartment() && !new ViewQuery(node).isForNameEditPart() && element instanceof DDiagramElementContainer) { DDiagramElementContainer ddec = (DDiagramElementContainer) element; needShadowBorder = !(new DDiagramElementContainerExperimentalQuery(ddec).isRegion() || ddec.getOwnedStyle() instanceof WorkspaceImage); } @@ -796,15 +896,22 @@ public static boolean isShadowBorderNeeded(Node node) { * @param recursive * true if this method is called from a "parent" call, false otherwise. */ - private static void replaceAutoSize(Node node, Rectangle bounds, boolean useFigureForAutoSizeConstraint, Dimension providedDefaultSize, boolean recursive) { + private static void replaceAutoSize(Node node, PrecisionRectangle bounds, boolean useFigureForAutoSizeConstraint, Dimension providedDefaultSize, boolean recursive) { if (bounds.width == -1 || bounds.height == -1) { Dimension defaultSize = providedDefaultSize; if (providedDefaultSize == null) { // if there is no default size, we compute it from the given // node. EObject element = node.getElement(); - if (new ViewQuery(node).isFreeFormCompartment() || new ViewQuery(node).isListCompartment()) { + ViewQuery nodeQuery = new ViewQuery(node); + if (nodeQuery.isFreeFormCompartment() || nodeQuery.isListCompartment()) { defaultSize = new Dimension(ResizableCompartmentFigure.MIN_CLIENT_DP, ResizableCompartmentFigure.MIN_CLIENT_DP); + if (nodeQuery.isVerticalRegionContainerCompartment() || nodeQuery.isListCompartment()) { + if (node.getChildren().isEmpty()) { + // Add one margin border (even if empty) + defaultSize.expand(0, 1); + } + } } else if (element instanceof AbstractDNode) { defaultSize = getDefaultSize((AbstractDNode) element); } @@ -841,26 +948,29 @@ private static void replaceAutoSize(Node node, Rectangle bounds, boolean useFigu // Compute the bounds of all children and use the lowest // one (y+height) for height and the rightmost one // (x+width) for width plus the margin. - Point bottomRight = getBottomRight(node, recursive); + Rectangle childrenBounds = getChildrenBounds(node, recursive); + // Add the potential shadow border size and the bottom right insets of the node (ie container) double shadowBorderSize = getShadowBorderSize(node); - Dimension topLeftInsets = getTopLeftInsets(node); Dimension bottomRightInsets = getBottomRightInsets(node); - if (!recursive) { - bottomRight.setX(bottomRight.x + Double.valueOf(shadowBorderSize).intValue() + topLeftInsets.width() + bottomRightInsets.width()); - bottomRight.setY(bottomRight.y + Double.valueOf(shadowBorderSize).intValue() + topLeftInsets.height() + bottomRightInsets.height()); - } + // Do not add bottom and right insets and shadow if there is at least one border on corresponding + // side + int borderNodesSides = getBorderNodesSides(node, childrenBounds); + boolean isBorderNodeOnRightSide = recursive && (PositionConstants.RIGHT & borderNodesSides) == PositionConstants.RIGHT; + boolean isBorderNodeOnBottomSide = recursive && (PositionConstants.BOTTOM & borderNodesSides) == PositionConstants.BOTTOM; + childrenBounds.resize(isBorderNodeOnRightSide ? 0 : bottomRightInsets.width() + shadowBorderSize, isBorderNodeOnBottomSide ? 0 : bottomRightInsets.height() + shadowBorderSize); + // Replace -1 by the new computed values if (bounds.width == -1) { - if (bottomRight.x > defaultSize.width) { - bounds.setWidth(bottomRight.x); - } else { - bounds.setWidth(defaultSize.width); + bounds.setPreciseWidth(defaultSize.preciseWidth()); + double deltaWidth = childrenBounds.getRight().preciseX() - bounds.getRight().preciseX(); + if (deltaWidth > 0) { + bounds.resize(deltaWidth, 0); } } if (bounds.height == -1) { - if (bottomRight.y > defaultSize.height) { - bounds.setHeight(bottomRight.y); - } else { - bounds.setHeight(defaultSize.height); + bounds.setPreciseHeight(defaultSize.preciseHeight()); + double deltaHeight = childrenBounds.getBottom().preciseY() - bounds.getBottom().preciseY(); + if (deltaHeight > 0) { + bounds.resize(0, deltaHeight); } } } @@ -874,6 +984,29 @@ private static void replaceAutoSize(Node node, Rectangle bounds, boolean useFigu } } + private static int getBorderNodesSides(Node container, Rectangle containerChildrenBounds) { + int result = PositionConstants.NONE; + for (Iterator children = Iterators.filter(container.getChildren().iterator(), Node.class); children.hasNext(); /* */) { + Node child = children.next(); + if (new NodeQuery(child).isBorderedNode()) { + Rectangle borderNodeBounds = getAbsoluteBounds(child, true); + if (borderNodeBounds.preciseX() == containerChildrenBounds.preciseX()) { + result = result | PositionConstants.LEFT; + } + if (borderNodeBounds.preciseY() == containerChildrenBounds.preciseY()) { + result = result | PositionConstants.TOP; + } + if (borderNodeBounds.preciseX() + borderNodeBounds.preciseWidth() == containerChildrenBounds.preciseX() + containerChildrenBounds.preciseWidth()) { + result = result | PositionConstants.RIGHT; + } + if (borderNodeBounds.preciseY() + borderNodeBounds.preciseHeight() == containerChildrenBounds.preciseY() + containerChildrenBounds.preciseHeight()) { + result = result | PositionConstants.BOTTOM; + } + } + } + return result; + } + private static void lookForNextRegionLocation(Rectangle bounds, Node node) { EObject element = node.getElement(); if (element instanceof DDiagramElementContainer && node.eContainer() instanceof Node) { @@ -923,101 +1056,66 @@ private static void lookForNextRegionLocation(Rectangle bounds, Node node) { * * @return Point at the bottom right of the rectangle */ - public static Point getBottomRight(Node container, boolean considerBorderNodes) { - return getBottomRight(container, null, considerBorderNodes); - } - - /** - * Returns a new Point representing the bottom right point of all bounds of children of this Node. Useful for Node - * with size of -1x-1 to be more accurate (but it is still not necessarily the same size that draw2d). - * - * @param container - * the node whose bottom right corner is to compute. - * @param considerBorderNode - * true to consider border nodes when computing the bottom right corner point, false otherwise. - * - * @return Point at the bottom right of the rectangle - */ - public static Point getBottomRight(Node container, Node childToStopTo, boolean considerBorderNodes) { - int right = 0; - int bottom = 0; - boolean stop = false; - for (Iterator children = Iterators.filter(container.getChildren().iterator(), Node.class); children.hasNext() && !stop; /* */) { - ViewQuery containerViewQuery = new ViewQuery(container); - Node child = children.next(); - if (child.equals(childToStopTo)) { - stop = true; - if (containerViewQuery.isListCompartment() || containerViewQuery.isVerticalRegionContainerCompartment()) { - if (bottom == 0) { - // The bottom right is asked for the first list item of the list, add a one margin border over - // it. - bottom = 1; - } - } + public static Rectangle getChildrenBounds(Node container, boolean considerBorderNodes) { + ViewQuery containerViewQuery = new ViewQuery(container); + Rectangle result = null; + if (container.getChildren().isEmpty()) { + if (containerViewQuery.isListCompartment() || containerViewQuery.isVerticalRegionContainerCompartment()) { + // Add one vertical margin border (even if empty) + result = new PrecisionRectangle(0, 0, 0, 1); } else { + result = new PrecisionRectangle(); + } + } + if (containerViewQuery.isListContainer() || containerViewQuery.isVerticalRegionContainerCompartment() || containerViewQuery.isListCompartment()) { + if (!container.getChildren().isEmpty()) { + Node lastChild = getLastChild(container, considerBorderNodes); + result = getAbsoluteBounds(lastChild, true, false, true); + } + } else { + for (Iterator children = Iterators.filter(container.getChildren().iterator(), Node.class); children.hasNext(); /* */) { + Node child = children.next(); // The border nodes are ignored, except if it is expected to consider it (auto-size of a container with // children having border nodes) if (considerBorderNodes || !(new NodeQuery(child).isBorderedNode())) { - Rectangle bounds = getBounds(child, false, false, false, true); - if (containerViewQuery.isListContainer() && new ViewQuery(child).isListCompartment()) { - // Add the corresponding margin of {1, 4, 0, 4} of - // org.eclipse.sirius.diagram.ui.internal.edit.parts.AbstractDNodeListCompartmentEditPart.createFigure() - bounds.translate(8, 1); - // Translate from the title (previous children) - bounds.translate(0, bottom); - // Translate from the spacing (5 pixels) - bounds.translate(0, AbstractDiagramElementContainerEditPart.DEFAULT_SPACING); - } else if (new ViewQuery(child).isVerticalRegionContainerCompartment()) { - // Add the corresponding margin of {1, 0, 0, 0} of - // org.eclipse.sirius.diagram.ui.edit.internal.part.DiagramContainerEditPartOperation.refreshBorder(AbstractDiagramElementContainerEditPart, - // ViewNodeContainerFigureDesc, ContainerStyle) - bounds.translate(0, 1); - // Translate from the title (previous children) - bounds.translate(0, bottom); - // Translate from the spacing (5 pixels) - bounds.translate(0, AbstractDiagramElementContainerEditPart.DEFAULT_SPACING); - } else if (containerViewQuery.isVerticalRegionContainerCompartment() && new ViewQuery(child).isFreeFormContainer()) { - if (bottom == 0) { - // This is the first region - // Add a one margin border over the first region - bounds.translate(0, 1); - } else { - // This is one of the following region - // Sometimes, we have not to translate from the previous children, as for region, the - // location is sometimes - // stored in the node. - boolean translateFromPreviousChildren = bounds.preciseY() == 0; - // TODO: Compute border size - int borderSize = 1; - // bounds.translate(0, Math.max(0, borderSize - 1) + IContainerLabelOffsets.LABEL_OFFSET); - bounds.translate(0, Math.max(0, borderSize - 1)); - // Sometimes, we have not to translate from the previous children, as for region, the - // location is sometimes - // stored in the node. - if (translateFromPreviousChildren) { - bounds.translate(0, bottom); - } + Rectangle childAbsoluteBounds = getAbsoluteBounds(child, true, false, true); + // TODO : Use intersection here ? + if (result == null) { + result = childAbsoluteBounds.getCopy(); + } else { + double deltaX = result.preciseX() - childAbsoluteBounds.preciseX(); + if (deltaX > 0) { + result.translate(-deltaX, 0); + result.resize(deltaX, 0); } - } else if (containerViewQuery.isListCompartment()) { - if (bottom == 0) { - // Add a one margin border over the first list item - bounds.translate(0, 1); + double deltaY = result.preciseY() - childAbsoluteBounds.preciseY(); + if (deltaY > 0) { + result.translate(0, -deltaY); + result.resize(0, deltaY); + } + double deltaWidth = childAbsoluteBounds.getRight().preciseX() - result.getRight().preciseX(); + if (deltaWidth > 0) { + result.resize(deltaWidth, 0); + } + double deltaHeight = childAbsoluteBounds.getBottom().preciseY() - result.getBottom().preciseY(); + if (deltaHeight > 0) { + result.resize(0, deltaHeight); } - // Translate from the previous list item - bounds.translate(0, bottom); - } - // TODO: Shift bounds for list, or Vertical and Horizontal compartment. - Point bottomRight = bounds.getBottomRight(); - if (bottomRight.x > right) { - right = bottomRight.x; - } - if (bottomRight.y > bottom) { - bottom = bottomRight.y; } } } } - return new Point(right, bottom); + return result; + } + + private static Node getLastChild(Node container, boolean considerBorderNodes) { + for (int i = container.getChildren().size() - 1; i >= 0; i--) { + Node currentNode = (Node) container.getChildren().get(i); + if (considerBorderNodes || !new NodeQuery(currentNode).isBorderedNode()) { + return currentNode; + } + } + return null; } private static Dimension getDefaultSize(AbstractDNode abstractDNode) {