/***************************************************************************** * Copyright (c) 2009 CEA * * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Atos Origin - Initial API and implementation * *****************************************************************************/ package org.eclipse.papyrus.uml.diagram.sequence.edit.policies; import java.util.ArrayList; import java.util.List; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPolicy; import org.eclipse.gef.GraphicalEditPart; import org.eclipse.gef.Request; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.CompoundCommand; import org.eclipse.gef.commands.UnexecutableCommand; import org.eclipse.gef.requests.ChangeBoundsRequest; import org.eclipse.gef.requests.CreateRequest; import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; import org.eclipse.gmf.runtime.diagram.ui.commands.SetBoundsCommand; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeNodeEditPart; import org.eclipse.gmf.runtime.diagram.ui.editpolicies.XYLayoutEditPolicy; import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages; import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewAndElementRequest; import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest; import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest.ViewDescriptor; import org.eclipse.gmf.runtime.emf.type.core.IHintedType; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.papyrus.uml.diagram.common.commands.PreserveAnchorsPositionCommand; import org.eclipse.papyrus.uml.diagram.common.draw2d.LifelineDotLineFigure; import org.eclipse.papyrus.uml.diagram.common.editpolicies.BorderItemResizableEditPolicy; import org.eclipse.papyrus.uml.diagram.sequence.apex.command.ApexPreserveAnchorsPositionCommand; import org.eclipse.papyrus.uml.diagram.sequence.apex.util.ApexSequenceRequestConstants; import org.eclipse.papyrus.uml.diagram.sequence.command.CustomZOrderCommand; import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.ActionExecutionSpecificationEditPart; import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.BehaviorExecutionSpecificationEditPart; import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.CombinedFragment2EditPart; import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.DestructionOccurrenceSpecificationEditPart; import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.LifelineEditPart; import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.StateInvariantEditPart; import org.eclipse.papyrus.uml.diagram.sequence.part.UMLVisualIDRegistry; import org.eclipse.papyrus.uml.diagram.sequence.providers.UMLElementTypes; import org.eclipse.papyrus.uml.diagram.sequence.util.OccurrenceSpecificationMoveHelper; import org.eclipse.papyrus.uml.diagram.sequence.util.SequenceRequestConstant; import org.eclipse.papyrus.uml.diagram.sequence.util.SequenceUtil; /** * The custom LayoutEditPolicy for LifelineEditPart. */ public class LifelineXYLayoutEditPolicy extends XYLayoutEditPolicy { /** Initialization width of Execution Specification. */ public final static int EXECUTION_INIT_WIDTH = 16; /** Initialization width of CoRegion. */ public final static int COREGION_INIT_WIDTH = 30; /** Initialization height of Execution Specification. */ private final static int EXECUTION_INIT_HEIGHT = 50; /** Initialization height of a time bar figure. */ private static final int TIME_BAR_HEIGHT = 1; /** The default spacing used between Execution Specification */ private final static int SPACING_HEIGHT = 5; /** * {@inheritDoc} */ @Override protected Command getCreateCommand(CreateRequest request) { if(request instanceof CreateViewRequest) { CreateViewRequest cvr = (CreateViewRequest)request; if(cvr.getViewDescriptors().size() > 0) { ViewDescriptor viewDescriptor = cvr.getViewDescriptors().iterator().next(); String semanticHint = viewDescriptor.getSemanticHint(); // force location of time/duration elements and ES String timeConstraintHint = ((IHintedType)UMLElementTypes.TimeConstraint_3019).getSemanticHint(); String timeObservationHint = ((IHintedType)UMLElementTypes.TimeObservation_3020).getSemanticHint(); String durationConstraintOnLifelineHint = ((IHintedType)UMLElementTypes.DurationConstraint_3021).getSemanticHint(); String actionExecutionSpecificationHint = ((IHintedType)UMLElementTypes.ActionExecutionSpecification_3006).getSemanticHint(); String behaviorExecutionSpecificationHint = ((IHintedType)UMLElementTypes.BehaviorExecutionSpecification_3003).getSemanticHint(); String coRegionHint = ((IHintedType)UMLElementTypes.CombinedFragment_3018).getSemanticHint(); if(timeConstraintHint.equals(semanticHint) || timeObservationHint.equals(semanticHint)) { Command cmd = getCommandForTimeObservationOrConstraint(cvr, viewDescriptor); if(cmd != null) { return cmd; } } if(durationConstraintOnLifelineHint.equals(semanticHint)) { Command cmd = getCommandForDurationConstraint(cvr, viewDescriptor); if(cmd != null) { return cmd; } } if(actionExecutionSpecificationHint.equals(semanticHint) || behaviorExecutionSpecificationHint.equals(semanticHint)) { Command cmd = getCommandForExecutionSpecificationCreation(cvr, viewDescriptor); if(cmd != null) { return cmd; } } if(coRegionHint.equals(semanticHint)) { Command cmd = getCommandForCoRegionCreation(cvr, viewDescriptor); if(cmd != null) { return cmd; } } } } return super.getCreateCommand(request); } @Override protected EditPolicy createChildEditPolicy(EditPart child) { View childView = (View)child.getModel(); switch(UMLVisualIDRegistry.getVisualID(childView)) { case DestructionOccurrenceSpecificationEditPart.VISUAL_ID: return new BorderItemResizableEditPolicy(); } return super.createChildEditPolicy(child); } protected Rectangle getCurrentConstraintFor(GraphicalEditPart child) { IFigure fig = child.getFigure(); Object con = fig.getParent().getLayoutManager() .getConstraint(fig); if(con instanceof Rectangle) return (Rectangle) con; return fig.getBounds(); } protected Object getConstraintFor(CreateRequest request) { Rectangle constraint = (Rectangle)super.getConstraintFor(request); if (request instanceof CreateViewAndElementRequest) { CreateViewAndElementRequest req = (CreateViewAndElementRequest)request; IHintedType type = (IHintedType) UMLElementTypes.Lifeline_3001; if (type.getSemanticHint().equals(req.getViewAndElementDescriptor().getSemanticHint())) { constraint.y = 0 ; // fix layout offset } } return constraint; } private static Rectangle getNewBoundsForCoRegion(LifelineEditPart lifelineEP, Rectangle bounds) { Rectangle newBounds = bounds.getCopy(); // Get the dotline figure LifelineDotLineFigure figureLifelineDotLineFigure = lifelineEP.getPrimaryShape().getFigureLifelineDotLineFigure(); // Translate the absolute location to relative figureLifelineDotLineFigure.translateToRelative(newBounds); newBounds.translate(figureLifelineDotLineFigure.getBounds().getLocation().getCopy().negate()); Rectangle dotLineFigureBounds = figureLifelineDotLineFigure.getBounds(); newBounds.x = dotLineFigureBounds.width / 2 - COREGION_INIT_WIDTH / 2; newBounds.width = COREGION_INIT_WIDTH; return newBounds; } private Command getCommandForCoRegionCreation(CreateViewRequest cvr, ViewDescriptor viewDescriptor) { Rectangle newBounds = new Rectangle(); if(cvr.getLocation() != null) { newBounds.setLocation(cvr.getLocation()); } if(cvr.getSize() != null) { newBounds.setSize(cvr.getSize()); } else { newBounds.width = -1; newBounds.height = -1; } if(newBounds.x < 0 || newBounds.y < 0) { newBounds.x = newBounds.y = 0; } newBounds = getNewBoundsForCoRegion((LifelineEditPart)getHost(), newBounds); TransactionalEditingDomain editingDomain = ((IGraphicalEditPart)getHost()).getEditingDomain(); return new ICommandProxy(new SetBoundsCommand(editingDomain, DiagramUIMessages.SetLocationCommand_Label_Resize, viewDescriptor, newBounds)); } private Command getCommandForExecutionSpecificationCreation(CreateViewRequest cvr, ViewDescriptor viewDescriptor) { Point newLocation = cvr.getLocation().getCopy(); if(newLocation.x < 0 || newLocation.y < 0) { newLocation.x = newLocation.y = 0; } LifelineEditPart editPart = (LifelineEditPart)getHost(); // Get the dotline figure LifelineDotLineFigure figureLifelineDotLineFigure = editPart.getPrimaryShape().getFigureLifelineDotLineFigure(); List<ShapeNodeEditPart> executionSpecificationList = editPart.getChildShapeNodeEditPart(); // Translate the absolute location to relative figureLifelineDotLineFigure.translateToRelative(newLocation); Rectangle dotLineFigureBounds = figureLifelineDotLineFigure.getBounds(); // If we are creating an ES from the popup menu bar // We need to get a valid location to be able to create the ES figure if(newLocation.y < dotLineFigureBounds.y) { int max = dotLineFigureBounds.y; for(ShapeNodeEditPart sp : executionSpecificationList) { int figureBottom = sp.getFigure().getBounds().y + sp.getFigure().getBounds().height; if(figureBottom > max) { max = figureBottom; } } // Vertically, the new ES is located after all existing ES on the lifeline newLocation.y = max + SPACING_HEIGHT; // Horizontally, the figure is placed at the center of the lifeline newLocation.x = dotLineFigureBounds.x + dotLineFigureBounds.width / 2 - EXECUTION_INIT_WIDTH / 2; } // Get the height of the Execution specification int newHeight = getFigureHeight(cvr); // Define the bounds of the new Execution specification Rectangle newBounds = new Rectangle(newLocation.x, newLocation.y, -1, newHeight); ShapeNodeEditPart parent = getParent((LifelineEditPart)getHost(), newBounds, executionSpecificationList); newBounds = getExecutionSpecificationNewBounds(true, editPart, new Rectangle(), newBounds, new ArrayList<ShapeNodeEditPart>(0), false); if(newBounds == null) { return UnexecutableCommand.INSTANCE; } Command p = new ICommandProxy(new SetBoundsCommand(editPart.getEditingDomain(), "Creation of an ExecutionSpecification", viewDescriptor, newBounds)); // resize parent bar if(parent != null){ p = p.chain(resizeParentExecutionSpecification((LifelineEditPart)getHost(), parent, newBounds.getCopy(), executionSpecificationList)); } return p; } private static Command resizeParentExecutionSpecification(LifelineEditPart lifelinePart, ShapeNodeEditPart part, Rectangle childBounds, List<ShapeNodeEditPart> list) { Rectangle bounds = getRelativeBounds(part.getFigure()); childBounds.x = bounds.x; childBounds.width = bounds.width; if(bounds.contains(childBounds)) return null; bounds.union(childBounds); Command c = new ICommandProxy(new SetBoundsCommand(part.getEditingDomain(), "Resize of Parent Bar", part, bounds.getCopy())); list.remove(part); ShapeNodeEditPart parent = getParent(lifelinePart, part.getFigure().getBounds(), list); if(parent == null) return c; return c.chain(resizeParentExecutionSpecification(lifelinePart, parent, bounds.getCopy(), list)); } /** * Get the command for setting initial bounds of a Time Observation or Constraint representation * * @param cver * the request * @return command or null if none is appropriate */ private Command getCommandForTimeObservationOrConstraint(CreateViewRequest cvr, ViewDescriptor viewDescriptor) { Object loc = cvr.getExtendedData().get(SequenceRequestConstant.OCCURRENCE_SPECIFICATION_LOCATION); if(loc instanceof Point) { IFigure parentFigure = ((IGraphicalEditPart)getHost()).getFigure(); Point referencePoint = ((Point)loc).getCopy(); parentFigure.translateToRelative(referencePoint); referencePoint.translate(parentFigure.getBounds().getLocation().getCopy().negate()); // Get the height of the element int newHeight = getFigureHeight(cvr); // Define the bounds of the new time element Rectangle newBounds = new Rectangle(referencePoint.x, referencePoint.y - newHeight / 2, -1, newHeight); TransactionalEditingDomain editingDomain = ((IGraphicalEditPart)getHost()).getEditingDomain(); return new ICommandProxy(new SetBoundsCommand(editingDomain, DiagramUIMessages.SetLocationCommand_Label_Resize, viewDescriptor, newBounds)); } return null; } /** * Get the command for setting initial bounds of a Duration Constraint representation * * @param cver * the request * @return command or null if none is appropriate */ private Command getCommandForDurationConstraint(CreateViewRequest cvr, ViewDescriptor viewDescriptor) { Object locTop = cvr.getExtendedData().get(SequenceRequestConstant.OCCURRENCE_SPECIFICATION_LOCATION); Object locBottom = cvr.getExtendedData().get(SequenceRequestConstant.OCCURRENCE_SPECIFICATION_LOCATION_2); if(locTop instanceof Point && locBottom instanceof Point) { IFigure parentFigure = ((IGraphicalEditPart)getHost()).getFigure(); Point referenceTop = ((Point)locTop).getCopy(); Point referenceBottom = ((Point)locBottom).getCopy(); // Get the height of the element int newHeight = referenceBottom.y - referenceTop.y; if(newHeight > 0) { parentFigure.translateToRelative(referenceTop); Point parentFigDelta = parentFigure.getBounds().getLocation().getCopy().negate(); referenceTop.translate(parentFigDelta); // Define the bounds of the new time element Rectangle newBounds = new Rectangle(referenceTop.x, referenceTop.y, -1, newHeight); TransactionalEditingDomain editingDomain = ((IGraphicalEditPart)getHost()).getEditingDomain(); return new ICommandProxy(new SetBoundsCommand(editingDomain, DiagramUIMessages.SetLocationCommand_Label_Resize, viewDescriptor, newBounds)); } else if(newHeight < 0) { parentFigure.translateToRelative(referenceBottom); Point parentFigDelta = parentFigure.getBounds().getLocation().getCopy().negate(); referenceBottom.translate(parentFigDelta); // Define the bounds of the new time element Rectangle newBounds = new Rectangle(referenceBottom.x, referenceBottom.y, -1, -newHeight); TransactionalEditingDomain editingDomain = ((IGraphicalEditPart)getHost()).getEditingDomain(); return new ICommandProxy(new SetBoundsCommand(editingDomain, DiagramUIMessages.SetLocationCommand_Label_Resize, viewDescriptor, newBounds)); } } return null; } /** * Get the adapted height, taking in account the represented figure * * @param cr * the create request * @return the height defined in the create request or a default value depending on the created figure */ private int getFigureHeight(CreateRequest cr) { final String timeObsHint = ((IHintedType)UMLElementTypes.TimeObservation_3020).getSemanticHint(); final String timeCstHint = ((IHintedType)UMLElementTypes.TimeConstraint_3019).getSemanticHint(); String semHint = null; if(cr instanceof CreateViewAndElementRequest) { semHint = ((CreateViewAndElementRequest)cr).getViewAndElementDescriptor().getSemanticHint(); } int newHeight; if(timeObsHint.equals(semHint) || timeCstHint.equals(semHint)) { // height for a time bar (takes precedence on request's size) newHeight = TIME_BAR_HEIGHT; } else if(cr.getSize() != null) { // heigh from request newHeight = cr.getSize().height; } else { newHeight = EXECUTION_INIT_HEIGHT; } return newHeight; } /** * apex updated * * Useful operation to know where the figure of a ExecutionSpecification EditPart should be * positioned within a Lifeline EditPart. The notToCheckList is needed to avoid checking those * ExecutionSpecification EditParts. The returned bounds are relative to the Lifeline figure so * they can be used, directly, within a SetBoundsCommand. * * @param lifelineEP * the lifeline ep * @param oldBounds * The old bounds of the ES * @param newBounds * The new initial bounds * @param notToCheckExecutionSpecificationList * The ExecutionSpecification EditPart's List that won't be checked * * @return The new bounds of the executionSpecificationEP figure */ protected final static Rectangle getExecutionSpecificationNewBounds(boolean isMove, LifelineEditPart lifelineEP, Rectangle oldBounds, Rectangle newBounds, List<ShapeNodeEditPart> notToCheckExecutionSpecificationList, boolean useFixedXPos) { // Lifeline's figure where the child is drawn Rectangle dotLineBounds = lifelineEP.getPrimaryShape().getFigureLifelineDotLineFigure().getBounds(); // if ExecutionSpecification is resize outside of the lifeline bounds if(newBounds.y <= dotLineBounds.y || newBounds.x < dotLineBounds.x || newBounds.x > dotLineBounds.right()) { return null; } List<ShapeNodeEditPart> toCheckExecutionSpecificationList = lifelineEP.getChildShapeNodeEditPart(); toCheckExecutionSpecificationList.removeAll(notToCheckExecutionSpecificationList); if(isMove) { ShapeNodeEditPart parent = getParent(lifelineEP, newBounds, toCheckExecutionSpecificationList); if(useFixedXPos) { newBounds.x = oldBounds.x; } else if(parent == null) { // No mother, center position int width = newBounds.width > 0 ? newBounds.width : EXECUTION_INIT_WIDTH; newBounds.x = dotLineBounds.x + dotLineBounds.width / 2 - width / 2; } else { Rectangle parentBounds = parent.getFigure().getBounds(); int width = parentBounds.width > 0 ? parentBounds.width : EXECUTION_INIT_WIDTH; newBounds.x = parentBounds.x + width / 2 + 1; } } else { ShapeNodeEditPart oldParent = getParent(lifelineEP, oldBounds, toCheckExecutionSpecificationList); // forbid resize if the new bounds exceed Y-wise the bounds of a non-parent ES for(ShapeNodeEditPart esPart : toCheckExecutionSpecificationList) { Rectangle esBounds = esPart.getFigure().getBounds(); int esYBottom = esBounds.y + esBounds.height; if(esPart != oldParent) { /* apex improved start */ // 하단에 ExecutionSpecification이 있으면 더 이상 확장이 불가능 /* apex improved end */ /* apex replaced if(((oldBounds.y + oldBounds.height) <= esBounds.y && (newBounds.y + newBounds.height) >= esBounds.y) || (oldBounds.y >= esYBottom && newBounds.y <= esYBottom)) { return null; } // */ } } } // Change to relative bounds of the LifelineEP newBounds.x -= dotLineBounds.x; newBounds.y -= dotLineBounds.y; return newBounds; } /** * {@inheritDoc} */ @Override protected Command getResizeChildrenCommand(ChangeBoundsRequest request) { // This policy is hosted in a LifelineEditPart LifelineEditPart lifelineEP = (LifelineEditPart)getHost(); Command command = getResizeOrMoveChildrenCommand(lifelineEP, request, false, true, false); if(command == null) { command = super.getResizeChildrenCommand(request); } return command; } /** * {@inheritDoc} */ protected Command getMoveChildrenCommand(Request request) { // This policy is hosted in a LifelineEditPart LifelineEditPart lifelineEP = (LifelineEditPart)getHost(); Command command = getResizeOrMoveChildrenCommand(lifelineEP, (ChangeBoundsRequest)request, true, true, false); if(command == null) { command = super.getMoveChildrenCommand(request); } return command; } /** * apex updated * * @param lifelineEP * @param request * @param isMove * @param updateEnclosingInteraction * @param useFixedXPos * @return */ @SuppressWarnings("unchecked") public static Command getResizeOrMoveChildrenCommand(LifelineEditPart lifelineEP, ChangeBoundsRequest request, boolean isMove, boolean updateEnclosingInteraction, boolean useFixedXPos) { List<EditPart> editParts = request.getEditParts(); if(editParts != null) { CompoundCommand compoundCmd = new CompoundCommand(); compoundCmd.setLabel("Move or resize"); compoundCmd.setDebugLabel("Debug: Move or resize of an ExecutionSpecification"); for(EditPart ep : editParts) { if(ep instanceof ActionExecutionSpecificationEditPart || ep instanceof BehaviorExecutionSpecificationEditPart) { // an execution specification have been moved or resized ShapeNodeEditPart executionSpecificationEP = (ShapeNodeEditPart)ep; // Check if height is within the limits of the figure Dimension newSizeDelta = adaptSizeDeltaToMaxHeight(executionSpecificationEP.getFigure(), request.getSizeDelta()); // Current bounds of the ExecutionSpecification Rectangle oldBounds = executionSpecificationEP.getFigure().getBounds().getCopy(); Rectangle newBounds = oldBounds.getCopy(); // According to the parameters, the new bounds would be the following newBounds.x += request.getMoveDelta().x; newBounds.y += request.getMoveDelta().y; newBounds.height += newSizeDelta.height; // Not to check list List<ShapeNodeEditPart> notToCheckExecutionSpecificationList = new BasicEList<ShapeNodeEditPart>(); // Affixed ExecutionSpecification List notToCheckExecutionSpecificationList.addAll(getAffixedExecutionSpecificationEditParts(executionSpecificationEP)); // Add also current ExecutionSpecification EditPart notToCheckExecutionSpecificationList.add(executionSpecificationEP); // find parent bar List<ShapeNodeEditPart> executionSpecificationList = lifelineEP.getChildShapeNodeEditPart(); executionSpecificationList.remove(executionSpecificationEP); ShapeNodeEditPart parentBar = getParent(lifelineEP, newBounds, executionSpecificationList); // change bounds to relative newBounds = getExecutionSpecificationNewBounds(isMove, lifelineEP, oldBounds, newBounds, notToCheckExecutionSpecificationList, useFixedXPos); if(newBounds == null) { return UnexecutableCommand.INSTANCE; } if(parentBar != null){ compoundCmd.add(resizeParentExecutionSpecification(lifelineEP, parentBar, newBounds.getCopy(), executionSpecificationList)); } // Create and add the command to the compound command SetBoundsCommand setBoundsCmd = new SetBoundsCommand(executionSpecificationEP.getEditingDomain(), "Resize of a ExecutionSpecification", executionSpecificationEP, newBounds); compoundCmd.add(new ICommandProxy(setBoundsCmd)); Rectangle realMoveDelta = getRealMoveDelta(getRelativeBounds(executionSpecificationEP.getFigure()), newBounds); if(isMove) { // Move also children compoundCmd.add(createMovingAffixedExecutionSpecificationCommand(executionSpecificationEP, realMoveDelta, newBounds)); compoundCmd.add(createZOrderCommand(lifelineEP, executionSpecificationEP, newBounds, notToCheckExecutionSpecificationList)); } /* apex added start */ else { Object data = request.getExtendedData().get(SequenceRequestConstant.DO_NOT_MOVE_EDIT_PARTS); if (!Boolean.TRUE.equals(data)) { compoundCmd.add(apexCreateMovingAffixedExecutionSpecificationCommand(executionSpecificationEP, realMoveDelta, newBounds)); compoundCmd.add(createZOrderCommand(lifelineEP, executionSpecificationEP, newBounds, notToCheckExecutionSpecificationList)); } } /* apex added end */ // Move also linked Time elements compoundCmd = OccurrenceSpecificationMoveHelper.completeMoveExecutionSpecificationCommand(compoundCmd, executionSpecificationEP, newBounds, request); IFigure parentFigure = executionSpecificationEP.getFigure().getParent(); parentFigure.translateToAbsolute(newBounds); // translateToAbsolute only does half of the work, I don't know why newBounds.translate(parentFigure.getBounds().getLocation()); if(updateEnclosingInteraction) { // update the enclosing interaction of a moved execution specification compoundCmd.add(SequenceUtil.createUpdateEnclosingInteractionCommand(executionSpecificationEP, request.getMoveDelta(), newSizeDelta)); } // keep absolute position of anchors /* apex improved start */ Object relative = request.getExtendedData().get(ApexSequenceRequestConstants.APEX_PRESERVE_ANCHOR_RELATIVE_BOUNDS); ApexPreserveAnchorsPositionCommand command = new ApexPreserveAnchorsPositionCommand(executionSpecificationEP, new Dimension(realMoveDelta.width, realMoveDelta.height), PreserveAnchorsPositionCommand.PRESERVE_Y, executionSpecificationEP.getFigure(), request.getResizeDirection(), relative); compoundCmd.add(new ICommandProxy(command)); /* apex improved end */ /* apex replaced compoundCmd.add(new ICommandProxy(new LifelineEditPart.PreserveAnchorsPositionCommandEx(executionSpecificationEP, new Dimension(realMoveDelta.width, realMoveDelta.height), PreserveAnchorsPositionCommand.PRESERVE_Y, executionSpecificationEP.getFigure(), request.getResizeDirection()))); */ } if(ep instanceof CombinedFragment2EditPart) { CombinedFragment2EditPart cf2EP = (CombinedFragment2EditPart)ep; IFigure cf2Figure = cf2EP.getFigure(); Rectangle bounds = cf2Figure.getBounds().getCopy(); cf2Figure.getParent().translateToAbsolute(bounds); Dimension sizeDelta = request.getSizeDelta(); if(sizeDelta != null) { if(sizeDelta.width != 0) { return UnexecutableCommand.INSTANCE; } bounds.resize(sizeDelta); } Point moveDelta = request.getMoveDelta(); if(moveDelta != null) { bounds.translate(moveDelta); } // Create and add the set bounds command to the compound command SetBoundsCommand setBoundsCmd = new SetBoundsCommand(cf2EP.getEditingDomain(), "Resize of a CoRegion", cf2EP, getNewBoundsForCoRegion(lifelineEP, bounds)); compoundCmd.add(new ICommandProxy(setBoundsCmd)); } /* apex added start */ if(ep instanceof StateInvariantEditPart) { StateInvariantEditPart siEP = (StateInvariantEditPart)ep; IFigure siFigure = siEP.getFigure(); Rectangle newBounds = siFigure.getBounds().getCopy(); // Get the dotline figure LifelineDotLineFigure figureLifelineDotLineFigure = lifelineEP.getPrimaryShape().getFigureLifelineDotLineFigure(); Point moveDelta = request.getMoveDelta(); if(moveDelta != null) { newBounds.translate(moveDelta); newBounds.y -= 10; } // Create and add the set bounds command to the compound command SetBoundsCommand setBoundsCmd = new SetBoundsCommand(siEP.getEditingDomain(), "Apex Move of a StateInvariant", siEP, newBounds); compoundCmd.add(new ICommandProxy(setBoundsCmd)); InteractionCompartmentXYLayoutEditPolicy.apexMoveBelowItems(request, siEP, compoundCmd); } if(ep instanceof DestructionOccurrenceSpecificationEditPart) { DestructionOccurrenceSpecificationEditPart doEP = (DestructionOccurrenceSpecificationEditPart)ep; IFigure siFigure = doEP.getFigure(); Rectangle bounds = siFigure.getBounds().getCopy(); Point moveDelta = request.getMoveDelta(); if(moveDelta != null) { bounds.translate(moveDelta); } // Create and add the set bounds command to the compound command SetBoundsCommand setBoundsCmd = new SetBoundsCommand(doEP.getEditingDomain(), "Apex Move of a DestructionEvent", doEP, bounds); compoundCmd.add(new ICommandProxy(setBoundsCmd)); } /* apex added end */ } if(!compoundCmd.isEmpty()) { return compoundCmd; } } return null; } /** * Command for change ZOrder of ExecutionSpecification ordered from parent to children. * * @param lifelineEP * the lifeline ep * @param executionSpecificationEP * the execution specification ep * @param newBounds * the new bounds * @param notToCheckExecutionSpecificationList * the not to check bes list * * @return the command */ protected final static Command createZOrderCommand(LifelineEditPart lifelineEP, ShapeNodeEditPart executionSpecificationEP, Rectangle newBounds, List<ShapeNodeEditPart> notToCheckExecutionSpecificationList) { List<ShapeNodeEditPart> toCheckExecutionSpecificationList = lifelineEP.getChildShapeNodeEditPart(); toCheckExecutionSpecificationList.removeAll(notToCheckExecutionSpecificationList); CompoundCommand cmd = new CompoundCommand(); for(ShapeNodeEditPart externalExecutionSpecificationEP : toCheckExecutionSpecificationList) { Rectangle externalExecutionSpecificationBounds = getRelativeBounds(externalExecutionSpecificationEP.getFigure()); // Check if there is any contact if(externalExecutionSpecificationBounds.touches(newBounds)) { View containerView = ViewUtil.getContainerView(executionSpecificationEP.getPrimaryView()); if(containerView != null) { int i = 0; int parentIndex = -1; int childIndex = -1; for(Object child : containerView.getChildren()) { if(child == externalExecutionSpecificationEP.getPrimaryView()) { parentIndex = i; } else if(child == executionSpecificationEP.getPrimaryView()) { childIndex = i; } if(parentIndex != -1 && childIndex != -1) { if(childIndex > parentIndex) { cmd.add(new ICommandProxy(new CustomZOrderCommand(executionSpecificationEP.getEditingDomain(), executionSpecificationEP.getPrimaryView(), parentIndex))); cmd.add(new ICommandProxy(new CustomZOrderCommand(externalExecutionSpecificationEP.getEditingDomain(), externalExecutionSpecificationEP.getPrimaryView(), childIndex))); } else { break; } } i++; } } } } if(!cmd.isEmpty()) { return cmd; } return null; } /** * Useful operation to know where the figure of a ExecutionSpecification EditPart should be * positioned within a Lifeline EditPart. The notToCheckList is needed to avoid checking those * ExecutionSpecification EditParts. The returned bounds are relative to the Lifeline figure so * they can be used, directly, within a SetBoundsCommand. * * @param lifelineDotLineFigure * @param newBounds * The new initial bounds * @param executionSpecifactionEditPart * @param notToCheckExecutionSpecificationList * The ExecutionSpecification EditPart's List that won't be checked * * @return The new bounds of the executionSpecificationEP figure */ /** * Get the (futur) parent of a ExecutionSpecification * @param lifelinePart * * @param childBounds * the child bounds * @param toCheckExecutionSpecificationList * List of EditPart to check * @return The parent */ protected final static ShapeNodeEditPart getParent(LifelineEditPart lifelinePart, Rectangle childBounds, List<ShapeNodeEditPart> toCheckExecutionSpecificationList) { ShapeNodeEditPart parent = null; // Loop through the ExecutionSpecification list and try to find the most to the right // ExecutionSpecification within the executionSpecificationEP Y-axis bounds Rectangle externalBounds = childBounds.getCopy(); for(ShapeNodeEditPart externalExecutionSpecificationEP : toCheckExecutionSpecificationList) { Rectangle externalExecutionSpecificationBounds = externalExecutionSpecificationEP.getFigure().getBounds(); externalBounds.x = externalExecutionSpecificationBounds.x; externalBounds.width = externalExecutionSpecificationBounds.width; /* apex improved start */ if (externalExecutionSpecificationBounds.touches(externalBounds) && externalExecutionSpecificationBounds.y < externalBounds.y) { // start 위치가 더 하단일 경우에만 우측으로 이동 if(parent == null || externalExecutionSpecificationBounds.x > parent.getFigure().getBounds().x) { parent = externalExecutionSpecificationEP; } } /* apex improved start */ /* apex replaced if(externalExecutionSpecificationBounds.touches(externalBounds) && externalExecutionSpecificationBounds.x < childBounds.x) { if(parent == null || externalExecutionSpecificationBounds.x > parent.getFigure().getBounds().x) { parent = externalExecutionSpecificationEP; } } */ } return parent; } /** * Used to modify the sizeDelta if the given value is higher/lower than the highest/lowest * allowed values of the figure. * * @param figure * the figure * @param sizeDelta * the size delta * * @return a corrected sizeDelta */ protected final static Dimension adaptSizeDeltaToMaxHeight(IFigure figure, Dimension sizeDelta) { Dimension newSizeDelta = new Dimension(sizeDelta); int figureHeight = figure.getBounds().height; int maximunFigureHeight = figure.getMaximumSize().height; int minimunFigureHeight = figure.getMinimumSize().height; int height = figureHeight + newSizeDelta.height; if(height > maximunFigureHeight) { newSizeDelta.height = maximunFigureHeight - figureHeight; } else if(height < minimunFigureHeight) { newSizeDelta.height = minimunFigureHeight - figureHeight; } return newSizeDelta; } /** * Returns all the ExecutionSpecification EditParts that are affixed to the right side of the * given ExecutionSpecification EditPart. Not only the ones directly affixed to the * executionSpecificationEP are returned, but the ones that are indirectly affixed as well (this * is done recursively) * * @param executionSpecificationEP * the execution specification ep * * @return the list of affixed ExecutionSpecification. If there is no affixed * ExecutionSpecification, then an empty list will be returned */ protected final static List<ShapeNodeEditPart> getAffixedExecutionSpecificationEditParts(ShapeNodeEditPart executionSpecificationEP) { List<ShapeNodeEditPart> notToCheckExecutionSpecificationList = new ArrayList<ShapeNodeEditPart>(); return getAffixedExecutionSpecificationEditParts(executionSpecificationEP, notToCheckExecutionSpecificationList); } /** * Operation used by the above operation. It's main goal is to obtain, recursively, all the * affixed ExecutionSpecification. In order to do so, it is needed a ExecutionSpecification * EditPart and the notToCheckList. * * @param executionSpecificationEP * the execution specification ep * @param notToCheckExecutionSpecificationList * the not to check ExecutionSpecification list * * @return the list of affixed ExecutionSpecification. If there is no affixed * ExecutionSpecification, then an empty list will be returned */ protected final static List<ShapeNodeEditPart> getAffixedExecutionSpecificationEditParts(ShapeNodeEditPart executionSpecificationEP, List<ShapeNodeEditPart> notToCheckExecutionSpecificationList) { // Add itself to the notToCheck list List<ShapeNodeEditPart> newNotToCheckExecutionSpecificationList = new ArrayList<ShapeNodeEditPart>(notToCheckExecutionSpecificationList); newNotToCheckExecutionSpecificationList.add(executionSpecificationEP); // LifelineEditPart where the ExecutionSpecification EditPart is contained LifelineEditPart lifelineEP = (LifelineEditPart)executionSpecificationEP.getParent(); // ExecutionSpecification EditParts list List<ShapeNodeEditPart> executionSpecificationList = lifelineEP.getChildShapeNodeEditPart(); executionSpecificationList.remove(newNotToCheckExecutionSpecificationList); // List to store the Affixed ExecutionSpecification List<ShapeNodeEditPart> affixedExecutionSpecificationList = new ArrayList<ShapeNodeEditPart>(); // Loop ExecutionSpecificationough the ExecutionSpecification list for(ShapeNodeEditPart childExecutionSpecificationEP : executionSpecificationList) { if(isAffixedToRight(executionSpecificationEP.getFigure().getBounds(), childExecutionSpecificationEP.getFigure().getBounds())) { affixedExecutionSpecificationList.add(childExecutionSpecificationEP); // Add also it's affixed ExecutionSpecification affixedExecutionSpecificationList.addAll(getAffixedExecutionSpecificationEditParts(childExecutionSpecificationEP, newNotToCheckExecutionSpecificationList)); } } // To the ExecutionSpecification list return affixedExecutionSpecificationList; } /** * Checks whether the right EditPart is affixed to the left EditPart. In order to do so, the * operation checks if the right figure is really on the right and, if so, it just returns true * if figures touch each other. * * @param leftFigure * The left rectangle * @param rightFigure * The right rectangle * * @return true if the rectangles of both figures touch and the right figure is really on the * right. False otherwise */ protected final static boolean isAffixedToRight(Rectangle leftFigure, Rectangle rightFigure) { return leftFigure.touches(rightFigure) && leftFigure.x < rightFigure.x; } /** * apex updated */ @Override /* apex added start */ protected Command createAddCommand(ChangeBoundsRequest request, EditPart child, Object constraint) { if (child instanceof LifelineEditPart) { return null; } return super.createAddCommand(request, child, constraint); } /* apex added end */ /** * If a ExecutionSpecification EditPart is going to be moved according to a moveDelta, this * operation returns a compoundCommand that also moves the affixed ExecutionSpecification * according to that delta. * * @param executionSpecificationEP * The ExecutionSpecification EditPart that is going to be moved * @param moveDelta * The moveDelta of the previous EditPart * @param newBounds * the new bounds * * @return the compound command */ protected final static CompoundCommand createMovingAffixedExecutionSpecificationCommand(ShapeNodeEditPart executionSpecificationEP, Rectangle moveDelta, Rectangle newBounds) { if(moveDelta.y != 0 || moveDelta.height != 0) { CompoundCommand compoundCmd = new CompoundCommand(); for(ShapeNodeEditPart childExecutionSpecificationEP : getAffixedExecutionSpecificationEditParts(executionSpecificationEP)) { // Get Relative Bounds Rectangle childBounds = getRelativeBounds(childExecutionSpecificationEP.getFigure()); // Apply delta childBounds.y += moveDelta.y; childBounds.x += moveDelta.x; // Create the child's SetBoundsCommand SetBoundsCommand childSetBoundsCmd = new SetBoundsCommand(executionSpecificationEP.getEditingDomain(), "Movement of affixed ExecutionSpecification", childExecutionSpecificationEP, childBounds); compoundCmd.add(new ICommandProxy(childSetBoundsCmd)); IFigure parentFigure = childExecutionSpecificationEP.getFigure().getParent(); parentFigure.translateToAbsolute(newBounds); // translateToAbsolute only does half of the work, I don't know why newBounds.translate(parentFigure.getBounds().getLocation()); // change the enclosing interaction of the moved affixed child if necessary compoundCmd.add(SequenceUtil.createUpdateEnclosingInteractionCommand(childExecutionSpecificationEP, moveDelta.getLocation(), moveDelta.getSize())); // Move it's children as well if(!getAffixedExecutionSpecificationEditParts(childExecutionSpecificationEP).isEmpty()) { compoundCmd.add(createMovingAffixedExecutionSpecificationCommand(childExecutionSpecificationEP, moveDelta, childBounds)); } } if(!compoundCmd.isEmpty()) { return compoundCmd; } } return null; } /** * If a ExecutionSpecification EditPart is going to be moved according to a moveDelta, this * operation returns a compoundCommand that also moves the affixed ExecutionSpecification * according to that delta. * * @param executionSpecificationEP * The ExecutionSpecification EditPart that is going to be moved * @param moveDelta * The moveDelta of the previous EditPart * @param newBounds * the new bounds * * @return the compound command */ protected final static CompoundCommand apexCreateMovingAffixedExecutionSpecificationCommand(ShapeNodeEditPart executionSpecificationEP, Rectangle moveDelta, Rectangle newBounds) { // if(moveDelta.y != 0 || moveDelta.height != 0) { CompoundCommand compoundCmd = new CompoundCommand(); List<ShapeNodeEditPart> newAffixedExecutionSpecificationEditParts = apexGetAffixedExecutionSpecificationEditParts(executionSpecificationEP, newBounds); for(ShapeNodeEditPart childExecutionSpecificationEP : newAffixedExecutionSpecificationEditParts) { // Get Relative Bounds Rectangle childBounds = getRelativeBounds(childExecutionSpecificationEP.getFigure()); Rectangle childNewBounds = childBounds.getCopy(); // Apply delta childNewBounds.x = newBounds.x + newBounds.width / 2 + 1; // Create the child's SetBoundsCommand SetBoundsCommand childSetBoundsCmd = new SetBoundsCommand(executionSpecificationEP.getEditingDomain(), "Movement of affixed ExecutionSpecification", childExecutionSpecificationEP, childNewBounds); compoundCmd.add(new ICommandProxy(childSetBoundsCmd)); // change the enclosing interaction of the moved affixed child if necessary compoundCmd.add(SequenceUtil.createUpdateEnclosingInteractionCommand(childExecutionSpecificationEP, moveDelta.getLocation(), moveDelta.getSize())); // Move it's children as well // if(!apexGetAffixedExecutionSpecificationEditParts(childExecutionSpecificationEP, childNewBounds).isEmpty()) { Rectangle childMoveDelta = new Rectangle(); childMoveDelta.x = childNewBounds.x - childBounds.x; compoundCmd.add(apexCreateMovingAffixedExecutionSpecificationCommand(childExecutionSpecificationEP, childMoveDelta, childNewBounds)); // } } LifelineEditPart lifelineEP = (LifelineEditPart)executionSpecificationEP.getParent(); Rectangle dotLineBounds = lifelineEP.getPrimaryShape().getFigureLifelineDotLineFigure().getBounds(); int width = newBounds.width > 0 ? newBounds.width : EXECUTION_INIT_WIDTH; Rectangle oldBounds = getRelativeBounds(executionSpecificationEP.getFigure()); List<ShapeNodeEditPart> oldAffixedExecutionSpecificationEditParts = apexGetAffixedExecutionSpecificationEditParts(executionSpecificationEP, oldBounds); oldAffixedExecutionSpecificationEditParts.removeAll(newAffixedExecutionSpecificationEditParts); for (ShapeNodeEditPart childExecutionSpecificationEP : oldAffixedExecutionSpecificationEditParts) { // Get Relative Bounds Rectangle oldChildBounds = getRelativeBounds(childExecutionSpecificationEP.getFigure()); Rectangle newChildBounds = oldChildBounds.getCopy(); if (moveDelta.x != 0) { newChildBounds.x = oldChildBounds.x + moveDelta.x ; } else { newChildBounds.x = dotLineBounds.x + dotLineBounds.width / 2 - width / 2; newChildBounds.x -= dotLineBounds.x; } // Create the child's SetBoundsCommand SetBoundsCommand childSetBoundsCmd = new SetBoundsCommand(executionSpecificationEP.getEditingDomain(), "Movement of affixed ExecutionSpecification", childExecutionSpecificationEP, newChildBounds); compoundCmd.add(new ICommandProxy(childSetBoundsCmd)); Rectangle childMoveDelta = getRealMoveDelta(oldChildBounds, newChildBounds); compoundCmd.add(apexCreateMovingAffixedExecutionSpecificationCommand(childExecutionSpecificationEP, childMoveDelta, newChildBounds)); } if(!compoundCmd.isEmpty()) { return compoundCmd; } // } return null; } /** * Returns all the ExecutionSpecification EditParts that are affixed to the right side of the * given ExecutionSpecification EditPart. Not only the ones directly affixed to the * executionSpecificationEP are returned, but the ones that are indirectly affixed as well (this * is done recursively) * * @param executionSpecificationEP * the execution specification ep * @param newBounds * new bounds of executionSpecificationEP * * @return the list of affixed ExecutionSpecification. If there is no affixed * ExecutionSpecification, then an empty list will be returned */ protected final static List<ShapeNodeEditPart> apexGetAffixedExecutionSpecificationEditParts(ShapeNodeEditPart executionSpecificationEP, Rectangle newBounds) { List<ShapeNodeEditPart> notToCheckExecutionSpecificationList = new ArrayList<ShapeNodeEditPart>(); return apexGetAffixedExecutionSpecificationEditParts(executionSpecificationEP, newBounds, notToCheckExecutionSpecificationList); } /** * Operation used by the above operation. It's main goal is to obtain, recursively, all the * affixed ExecutionSpecification. In order to do so, it is needed a ExecutionSpecification * EditPart and the notToCheckList. * * @param executionSpecificationEP * the execution specification ep * @param newBounds * new bounds of executionSpecificationEP * @param notToCheckExecutionSpecificationList * the not to check ExecutionSpecification list * * @return the list of affixed ExecutionSpecification. If there is no affixed * ExecutionSpecification, then an empty list will be returned */ protected final static List<ShapeNodeEditPart> apexGetAffixedExecutionSpecificationEditParts(ShapeNodeEditPart executionSpecificationEP, Rectangle newBounds, List<ShapeNodeEditPart> notToCheckExecutionSpecificationList) { if (newBounds == null) { return getAffixedExecutionSpecificationEditParts(executionSpecificationEP, notToCheckExecutionSpecificationList); } // Add itself to the notToCheck list List<ShapeNodeEditPart> newNotToCheckExecutionSpecificationList = new ArrayList<ShapeNodeEditPart>(notToCheckExecutionSpecificationList); newNotToCheckExecutionSpecificationList.add(executionSpecificationEP); // LifelineEditPart where the ExecutionSpecification EditPart is contained LifelineEditPart lifelineEP = (LifelineEditPart)executionSpecificationEP.getParent(); // ExecutionSpecification EditParts list List<ShapeNodeEditPart> executionSpecificationList = lifelineEP.getChildShapeNodeEditPart(); executionSpecificationList.removeAll(newNotToCheckExecutionSpecificationList); // List to store the Affixed ExecutionSpecification List<ShapeNodeEditPart> affixedExecutionSpecificationList = new ArrayList<ShapeNodeEditPart>(); // Loop ExecutionSpecificationough the ExecutionSpecification list for(ShapeNodeEditPart childExecutionSpecificationEP : executionSpecificationList) { Rectangle childBounds = getRelativeBounds(childExecutionSpecificationEP.getFigure()); if(newBounds.touches(childBounds) && newBounds.y < childBounds.y) { affixedExecutionSpecificationList.add(childExecutionSpecificationEP); } } // To the ExecutionSpecification list return affixedExecutionSpecificationList; } /** * Given an AbstractGraphialEditPart and the new relative bounds that the EditPart will have, it * returns the real delta applied to the movement. * * @param oldRelativeBounds * The old position of the mentioned EditPart * @param newRelativeBounds * The new position of the mentioned EditPart * * @return The real MoveDelta applied */ protected final static Rectangle getRealMoveDelta(Rectangle oldRelativeBounds, Rectangle newRelativeBounds) { Rectangle realMoveDelta = new Rectangle(); realMoveDelta.x = newRelativeBounds.x - oldRelativeBounds.x; realMoveDelta.y = newRelativeBounds.y - oldRelativeBounds.y; realMoveDelta.height = newRelativeBounds.height - oldRelativeBounds.height; realMoveDelta.width = newRelativeBounds.width - oldRelativeBounds.width; return realMoveDelta; } /** * It returns the relative bounds of an Figure. * * @param figure * The Figure * * @return The relative bounds regarding it's parent figure */ protected final static Rectangle getRelativeBounds(IFigure figure) { Rectangle relBounds = figure.getBounds().getCopy(); Rectangle parentRectangle = figure.getParent().getBounds(); relBounds.x -= parentRectangle.x; relBounds.y -= parentRectangle.y; return relBounds; } }