package org.eclipse.papyrus.uml.diagram.sequence.apex.edit.policies; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.draw2d.Connection; import org.eclipse.draw2d.FigureCanvas; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.PolylineConnection; import org.eclipse.draw2d.PositionConstants; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.PointList; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartViewer; 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.BendpointRequest; import org.eclipse.gef.requests.ChangeBoundsRequest; import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer; 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.ConnectionNodeEditPart; 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.ConnectionBendpointEditPolicy; import org.eclipse.gmf.runtime.diagram.ui.util.SelectInDiagramHelper; import org.eclipse.gmf.runtime.gef.ui.internal.editpolicies.LineMode; import org.eclipse.papyrus.uml.diagram.sequence.apex.command.ApexMoveInteractionFragmentsCommand; import org.eclipse.papyrus.uml.diagram.sequence.apex.interfaces.IApexLifelineEditPart; import org.eclipse.papyrus.uml.diagram.sequence.apex.util.ApexSequenceDiagramConstants; import org.eclipse.papyrus.uml.diagram.sequence.apex.util.ApexSequenceRequestConstants; import org.eclipse.papyrus.uml.diagram.sequence.apex.util.ApexSequenceUtil; import org.eclipse.papyrus.uml.diagram.sequence.draw2d.routers.MessageRouter.RouterKind; import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.LifelineEditPart; import org.eclipse.papyrus.uml.diagram.sequence.util.SequenceRequestConstant; import org.eclipse.papyrus.uml.diagram.sequence.util.SequenceUtil; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Control; import org.eclipse.uml2.uml.ExecutionSpecification; import org.eclipse.uml2.uml.InteractionFragment; import org.eclipse.uml2.uml.Message; import org.eclipse.uml2.uml.MessageEnd; import org.eclipse.uml2.uml.MessageOccurrenceSpecification; /** * @author Jiho * */ @SuppressWarnings("restriction") public class ApexMessageConnectionLineSegEditPolicy extends ConnectionBendpointEditPolicy { /* from MessageRouter */ private static final int MAX_DELTA = 10; public ApexMessageConnectionLineSegEditPolicy() { super(LineMode.ORTHOGONAL_FREE); } @Override public Command getCommand(Request request) { if(isHorizontal()) { return super.getCommand(request); } return null; } /** * Move the anchors along with the line and update bendpoints accordingly. */ @Override protected Command getBendpointsChangedCommand(BendpointRequest request) { if((getHost().getViewer() instanceof ScrollingGraphicalViewer) && (getHost().getViewer().getControl() instanceof FigureCanvas)) { SelectInDiagramHelper.exposeLocation((FigureCanvas)getHost().getViewer().getControl(), request.getLocation().getCopy()); } if(getHost() instanceof ConnectionNodeEditPart) { ConnectionNodeEditPart connectionPart = (ConnectionNodeEditPart)getHost(); Point oldLocation = SequenceUtil.getAbsoluteEdgeExtremity(connectionPart, true); Point location = request.getLocation().getCopy(); int movableTop = getMovableTopPosition(connectionPart, false); if (movableTop > location.y) { location.y = movableTop; } Point moveDelta = new Point(0, location.y - oldLocation.y); // BendpointRequest->ChangeBoundsRequest로 변환 ChangeBoundsRequest cbRequest = new ChangeBoundsRequest(REQ_MOVE); cbRequest.setMoveDelta(moveDelta); cbRequest.setLocation(location); cbRequest.setExtendedData(request.getExtendedData()); cbRequest.setConstrainedMove(isConstrainedMove(cbRequest)); Command result = getHost().getCommand(cbRequest); return result; } return UnexecutableCommand.INSTANCE; } protected Command getXXXCommand(ChangeBoundsRequest request) { if (isReordering(request)) { return getReorderingCommand(request); } ConnectionNodeEditPart connectionPart = (ConnectionNodeEditPart)getHost(); TransactionalEditingDomain editingDomain = connectionPart.getEditingDomain(); EditPartViewer viewer = connectionPart.getViewer(); InteractionFragment container = null; Message message = (Message)connectionPart.resolveSemanticElement(); MessageEnd sendEvent = message.getSendEvent(); if (sendEvent instanceof MessageOccurrenceSpecification) { container = ((MessageOccurrenceSpecification)sendEvent).getEnclosingOperand(); if (container == null) { container = ((MessageOccurrenceSpecification)sendEvent).getEnclosingInteraction(); } } if (container == null) { return null; } Point location = request.getLocation().getCopy(); Point oldLocation = ApexSequenceUtil.getAbsoluteEdgeExtremity(connectionPart, true); Rectangle extent = new Rectangle(oldLocation.x, oldLocation.y, 0, 0); List<IGraphicalEditPart> linkedEditParts = ApexSequenceUtil.apexGetLinkedEditPartList(connectionPart, false, true, false); for (IGraphicalEditPart linkedEditPart : linkedEditParts) { extent.union(ApexSequenceUtil.getAbsoluteBounds(linkedEditPart)); } Point realMoveDelta = request.getMoveDelta().getCopy(); int minY = getMovableTopPosition(connectionPart, false); if (minY > location.y) { realMoveDelta.y += minY - location.y; } ApexMoveInteractionFragmentsCommand amifCommand = new ApexMoveInteractionFragmentsCommand( editingDomain, viewer, container, extent, realMoveDelta, MARGIN, false); Command result = new ICommandProxy(amifCommand); return result; } private Command getReorderingCommand(ChangeBoundsRequest request) { CompoundCommand cmpCommand = new CompoundCommand(); ConnectionNodeEditPart connectionPart = (ConnectionNodeEditPart)getHost(); TransactionalEditingDomain editingDomain = connectionPart.getEditingDomain(); EditPartViewer viewer = connectionPart.getViewer(); Point location = request.getLocation().getCopy(); Integer[] reorderingLocations = ApexSequenceUtil.apexGetReorderingLocations(connectionPart, location); Integer nearLocation = null; for (Integer reorderingLocation : reorderingLocations) { if ((nearLocation != null && Math.abs(nearLocation + NEAR_LINE_MARGIN - location.y) > Math.abs(reorderingLocation + NEAR_LINE_MARGIN - location.y)) || (Math.abs(reorderingLocation + NEAR_LINE_MARGIN - location.y) < NEAR_LINE_TOLERANCE)) { nearLocation = reorderingLocation; } } if (nearLocation == null) { return null; } Point oldLocation = ApexSequenceUtil.getAbsoluteEdgeExtremity(connectionPart, true); Point referencePoint = new Point(oldLocation.x, nearLocation); InteractionFragment container = SequenceUtil.findInteractionFragmentContainerAt(referencePoint, connectionPart, true); if (container == null) { return null; } Rectangle extent = new Rectangle(oldLocation.x, oldLocation.y, 0, 0); List<IGraphicalEditPart> linkedEditParts = ApexSequenceUtil.apexGetLinkedEditPartList(connectionPart, false, true, false); for (IGraphicalEditPart linkedEditPart : linkedEditParts) { extent.union(ApexSequenceUtil.getAbsoluteBounds(linkedEditPart)); } Point realMoveDelta = request.getMoveDelta().getCopy(); realMoveDelta.y += nearLocation - location.y; if (realMoveDelta.y < 0) { // B,A,C의 순서 -> A,B,C의 순서 // C블록 하위로 이동 Point newMoveDelta = new Point(0, extent.y - nearLocation); ApexMoveInteractionFragmentsCommand cmd = new ApexMoveInteractionFragmentsCommand(editingDomain, viewer, container, new Point(0, extent.bottom() + 1), newMoveDelta, MARGIN); cmpCommand.add(new ICommandProxy(cmd)); // B블록->A블록 하위로 이동 newMoveDelta = new Point(0, extent.bottom() - nearLocation); cmd = new ApexMoveInteractionFragmentsCommand(editingDomain, viewer, container, new Rectangle(0, nearLocation, 0, extent.y - nearLocation - 1), newMoveDelta, MARGIN, true); cmpCommand.add(new ICommandProxy(cmd)); // A블록 포함한 하위 모두 상위로 이동 newMoveDelta = new Point(0, extent.y - nearLocation).getNegated(); cmd = new ApexMoveInteractionFragmentsCommand(editingDomain, viewer, container, new Point(0, extent.y), newMoveDelta, MARGIN); cmpCommand.add(new ICommandProxy(cmd)); return cmpCommand; } else if (realMoveDelta.y > 0) { // A,B,C의 순서 -> B,A,C의 순서 // C블록 하위로 이동 Point newMoveDelta = new Point(0, extent.height); ApexMoveInteractionFragmentsCommand cmd = new ApexMoveInteractionFragmentsCommand(editingDomain, viewer, container, new Point(0, nearLocation + 1), newMoveDelta, MARGIN); cmpCommand.add(new ICommandProxy(cmd)); // A블록->B블록 하위로 이동 newMoveDelta = new Point(0, nearLocation - extent.y); cmd = new ApexMoveInteractionFragmentsCommand(editingDomain, viewer, container, extent, newMoveDelta, MARGIN, true); cmpCommand.add(new ICommandProxy(cmd)); // B블록 포함한 하위 모두 상위로 이동 (A와 B사이의 공백 계산 불가) newMoveDelta = new Point(0, extent.height).getNegated(); cmd = new ApexMoveInteractionFragmentsCommand(editingDomain, viewer, container, new Point(0, extent.bottom() + 1), newMoveDelta, MARGIN); cmpCommand.add(new ICommandProxy(cmd)); return cmpCommand; } return null; } private Command getConstrainedMoveCommand(ChangeBoundsRequest request) { ConnectionNodeEditPart connectionPart = (ConnectionNodeEditPart)getHost(); int minY = getMovableTopPosition(connectionPart, true); int maxY = getMovableBottomPosition(connectionPart); return null; } @Override protected List createManualHandles() { return Collections.emptyList(); } private static final int MARGIN = ApexSequenceDiagramConstants.VERTICAL_MARGIN; private static final int PADDING = ApexSequenceDiagramConstants.EXECUTION_PADDING; private static final int NEAR_LINE_MARGIN = ApexSequenceDiagramConstants.NEAR_LINE_MARGIN; private static final int NEAR_LINE_TOLERANCE = ApexSequenceDiagramConstants.NEAR_LINE_TOLERANCE; /** * 이동하려는 상단에 ExecutionSpecification이 있는 경우 */ private static boolean flexiblePrev = false; private PolylineConnection feedback; private PolylineConnection guideFeedback; /** * bounds를 변경하는 Command를 생성 * @param gep * @param oldBounds * @param newBounds * @param isPreserveAnchorsPosition true이면 gep만 단독으로 변경하는 Command 생성, false이면 Request에 의해 변경하는 Command 그룹 생성 * @return */ private static Command createChangeBoundsCommand(IGraphicalEditPart gep, Rectangle oldBounds, Rectangle newBounds, boolean isPreserveAnchorsPosition) { Command command = null; if (oldBounds.x == newBounds.x && oldBounds.y == newBounds.y && oldBounds.width == newBounds.width && oldBounds.height == newBounds.height) { return null; } if (!isPreserveAnchorsPosition) { TransactionalEditingDomain editingDomain = gep.getEditingDomain(); Rectangle parentBounds = gep.getFigure().getParent().getBounds(); // newBounds를 parent(Lifeline)을 기준으로 한 상대좌표로 변경 gep.getFigure().translateToRelative(newBounds); newBounds.translate(-parentBounds.x, -parentBounds.y); // end command = new ICommandProxy( new SetBoundsCommand(editingDomain, "", gep, newBounds) ); } else { ChangeBoundsRequest request = createChangeBoundsRequest(oldBounds, newBounds); command = gep.getCommand(request); } return command; } /** * bounds를 변경하는 ChangeBoundsRequest 생성 * @param oldBounds * @param newBounds * @return */ private static ChangeBoundsRequest createChangeBoundsRequest(Rectangle oldBounds, Rectangle newBounds) { ChangeBoundsRequest request = new ChangeBoundsRequest(REQ_RESIZE); Point moveDelta = new Point(newBounds.x - oldBounds.x, newBounds.y - oldBounds.y); Dimension sizeDelta = new Dimension(newBounds.width - oldBounds.width, newBounds.height - oldBounds.height); request.setMoveDelta(moveDelta); request.setSizeDelta(sizeDelta); // execution 이동에 의해 editpart들이 겹치는 현상 방지 request.getExtendedData().put(SequenceRequestConstant.DO_NOT_MOVE_EDIT_PARTS, true); if (oldBounds.y == newBounds.y) request.setResizeDirection(PositionConstants.SOUTH); else if (oldBounds.bottom() == newBounds.bottom()) request.setResizeDirection(PositionConstants.NORTH); else request.setResizeDirection(PositionConstants.NORTH_SOUTH); return request; } /** * {@inheritDoc} */ @Override protected void showMoveLineSegFeedback(BendpointRequest request) { if (isConstrainedMove(request)) { showConstrainedMoveLineSegFeedback(request, false); return; } showReorderingFeedback(request); ConnectionNodeEditPart host = (ConnectionNodeEditPart)getHost(); Connection connection = host.getConnectionFigure(); Point location = request.getLocation().getCopy(); PointList pl = connection.getPoints().getCopy(); Point oldLocation = ApexSequenceUtil.getAbsoluteEdgeExtremity(host, true); PolylineConnection feedbackConnection = getDragSourceFeedbackFigure(); int dy = location.y - oldLocation.y; for (int i = 0; i < pl.size(); i++) { Point p = pl.getPoint(i); p.y += dy; pl.setPoint(p, i); } feedbackConnection.setForegroundColor(connection.getLocalForegroundColor()); feedbackConnection.setPoints(pl); } public void eraseSourceFeedback(Request request) { if (feedback != null) { removeFeedback(feedback); } feedback = null; if (guideFeedback != null) { removeFeedback(guideFeedback); } guideFeedback = null; super.eraseSourceFeedback(request); } private void showConstrainedMoveLineSegFeedback(BendpointRequest request, boolean isFlexible) { ConnectionNodeEditPart host = (ConnectionNodeEditPart)getHost(); Connection connection = host.getConnectionFigure(); Point location = request.getLocation().getCopy(); connection.translateToRelative(location); PointList pl = connection.getPoints().getCopy(); Point oldLocation = pl.getFirstPoint().getCopy(); PolylineConnection feedbackConnection = getDragSourceFeedbackFigure(); int dy = location.y - oldLocation.y; int minY = getMovableTopPosition(host, isFlexible); int maxY = getMovableBottomPosition(host); if (minY > location.y) { dy = minY - oldLocation.y; } else if (maxY < location.y) { dy = maxY - oldLocation.y; } for (int i = 0; i < pl.size(); i++) { Point p = pl.getPoint(i); p.y += dy; pl.setPoint(p, i); } feedbackConnection.setForegroundColor(ApexSequenceDiagramConstants.CONSTRAINED_MOVE_LINE_COLOR); feedbackConnection.setPoints(pl); } private void showReorderingFeedback(BendpointRequest request) { ConnectionNodeEditPart host = (ConnectionNodeEditPart)getHost(); Point location = request.getLocation().getCopy(); Integer[] reorderingLocations = ApexSequenceUtil.apexGetReorderingLocations(host, location); Integer nearLocation = null; for (Integer reorderingLocation : reorderingLocations) { if ((nearLocation != null && Math.abs(nearLocation + NEAR_LINE_MARGIN - location.y) > Math.abs(reorderingLocation + NEAR_LINE_MARGIN - location.y)) || (Math.abs(reorderingLocation + NEAR_LINE_MARGIN - location.y) < NEAR_LINE_TOLERANCE)) { nearLocation = reorderingLocation; } } if (!isReordering(request) || nearLocation == null) { if (guideFeedback != null) { removeFeedback(guideFeedback); } guideFeedback = null; } else { if (guideFeedback == null) { guideFeedback = createGuideLineFeedbackFigure(); } Control control = getHost().getViewer().getControl(); int viewPortXLocation = control.getBounds().x; int viewPortXExtent = control.getBounds().x + control.getBounds().width; if (control instanceof FigureCanvas) { FigureCanvas canvas = (FigureCanvas)control; Rectangle bounds = canvas.getViewport().getBounds().getCopy(); canvas.getViewport().translateFromParent(bounds); viewPortXLocation = bounds.x; viewPortXExtent = bounds.x + bounds.width - 1; nearLocation += bounds.y; } PointList pl = new PointList(); pl.addPoint(viewPortXLocation, nearLocation + NEAR_LINE_MARGIN); pl.addPoint(viewPortXExtent, nearLocation + NEAR_LINE_MARGIN); guideFeedback.setForegroundColor(ApexSequenceDiagramConstants.REORDERING_LINE_COLOR); guideFeedback.setPoints(pl); addFeedback(guideFeedback); } } /** * {@inheritDoc} */ @Override protected PolylineConnection createDragSourceFeedbackConnection() { PolylineConnection connection = new PolylineConnection(); connection.setLineWidth(1); connection.setLineStyle(Graphics.LINE_DASHDOT); connection.setForegroundColor(getConnection().getLocalForegroundColor()); return connection; } /** * Returns feedback figure * @return */ protected PolylineConnection getDragSourceFeedbackFigure() { if (feedback == null) { feedback = createDragSourceFeedbackConnection(); addFeedback(feedback); } return feedback; } /** * Return guide feedback figure * @return */ protected PolylineConnection createGuideLineFeedbackFigure() { PolylineConnection connection = new PolylineConnection(); connection.setLineWidth(1); connection.setLineStyle(Graphics.LINE_DASH); connection.setForegroundColor(getConnection().getLocalForegroundColor()); return connection; } private boolean isHorizontal() { Connection connection = getConnection(); RouterKind kind = RouterKind.getKind(connection, connection.getPoints()); if(kind.equals(RouterKind.HORIZONTAL)) { return true; } return false; } /** * @param request * @return SHIFT 키가 눌렸는지 여부 */ protected boolean isConstrainedMove(Request request) { return Boolean.TRUE.equals(request.getExtendedData() .get(ApexSequenceRequestConstants.APEX_MODIFIER_CONSTRAINED_MOVE)); } /** * @param request * @return CTRL 키가 눌렸는지 여부 */ protected boolean isReordering(Request request) { return Boolean.TRUE.equals(request.getExtendedData() .get(ApexSequenceRequestConstants.APEX_MODIFIER_REORDERING)); } /** * connectionPart가 이동가능한 최상단 y값을 구한다. 다른 EditPart가 있으면 이동이 불가능하다. * @param connectionPart * @param isFlexible * @return connectionPart가 이동가능한 최상단 y값 */ protected int getMovableTopPosition(ConnectionNodeEditPart connectionPart, boolean isFlexible) { int topMost = Integer.MIN_VALUE, movableTop = Integer.MIN_VALUE; List<IGraphicalEditPart> siblingParts = ApexSequenceUtil.apexGetPrevSiblingEditParts(connectionPart); List<IGraphicalEditPart> frontLinkedParts = ApexSequenceUtil.apexGetLinkedEditPartList(connectionPart, true, false, true); IGraphicalEditPart realPrevPart = frontLinkedParts.size() > 0 ? frontLinkedParts.get(frontLinkedParts.size() - 1) : null; for (IGraphicalEditPart siblingPart : siblingParts) { topMost = Math.max(topMost, ApexSequenceUtil.apexGetAbsolutePosition(siblingPart, SWT.BOTTOM) + MARGIN); movableTop = Math.max(movableTop, topMost); // Linked가 아닌 Message일 경우 if (siblingPart instanceof ConnectionNodeEditPart && !frontLinkedParts.contains(connectionPart)) { ConnectionNodeEditPart prevConnPart = (ConnectionNodeEditPart)siblingPart; EditPart prevSourcePart = prevConnPart.getSource(); EditPart prevTargetPart = prevConnPart.getTarget(); if ( prevSourcePart instanceof IGraphicalEditPart ) { EObject ePrevSrcPartObj = ((IGraphicalEditPart) prevSourcePart).resolveSemanticElement(); if (ePrevSrcPartObj instanceof ExecutionSpecification) { int ty = ApexSequenceUtil.apexGetAbsolutePosition((IGraphicalEditPart)prevSourcePart, SWT.BOTTOM) + MARGIN; if (movableTop < ty) { movableTop = ty; realPrevPart = (IGraphicalEditPart)prevSourcePart; } } } if ( prevTargetPart instanceof IGraphicalEditPart ) { EObject ePrevTgtPartObj = ((IGraphicalEditPart) prevTargetPart).resolveSemanticElement(); if (ePrevTgtPartObj instanceof ExecutionSpecification) { int ty = ApexSequenceUtil.apexGetAbsolutePosition((IGraphicalEditPart)prevTargetPart, SWT.BOTTOM) + MARGIN; if (movableTop < ty) { movableTop = ty; realPrevPart = (IGraphicalEditPart)prevTargetPart; } } } } } if (siblingParts.size() == 0) { EditPart sourcePart = connectionPart.getSource(); LifelineEditPart srcLifelinePart = SequenceUtil.getParentLifelinePart(sourcePart); IFigure dotLine = ((IApexLifelineEditPart)srcLifelinePart).getNodeFigure(); Rectangle dotLineBounds = dotLine.getBounds().getCopy(); dotLine.translateToAbsolute(dotLineBounds); topMost = dotLineBounds.y() + MARGIN; movableTop = topMost; return movableTop; } if (!isFlexible || realPrevPart == null || realPrevPart.resolveSemanticElement() instanceof ExecutionSpecification) { topMost = movableTop; } else { Dimension minSize = realPrevPart.getFigure().getMinimumSize(); int bottom = ApexSequenceUtil.apexGetAbsolutePosition(realPrevPart, SWT.TOP) + minSize.height(); topMost = Math.max(topMost, bottom); } return topMost; } protected int getMovableBottomPosition(ConnectionNodeEditPart connectionPart) { int movableBottom = Integer.MAX_VALUE; List<IGraphicalEditPart> siblingParts = ApexSequenceUtil.apexGetNextSiblingEditParts(connectionPart); List<IGraphicalEditPart> linkedParts = ApexSequenceUtil.apexGetLinkedEditPartList(connectionPart, true, true, false); Rectangle linkedBounds = ApexSequenceUtil.apexGetAbsoluteRectangle(connectionPart); for (IGraphicalEditPart linkedPart : linkedParts) { linkedBounds.union(ApexSequenceUtil.apexGetAbsoluteRectangle(linkedPart)); } for (IGraphicalEditPart siblingPart : siblingParts) { if (!linkedParts.contains(siblingPart)) { movableBottom = Math.min(movableBottom, ApexSequenceUtil.apexGetAbsolutePosition(siblingPart, SWT.TOP) - MARGIN); } } return movableBottom - linkedBounds.height; } }