blob: 868a956aaab7547d37ecbcc6341b9ebbbdb75b8e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2021 THALES GLOBAL SERVICES.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.editparts.LayerManager;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateConnectionRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gmf.runtime.diagram.ui.commands.CommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
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.commands.core.command.CompositeTransactionalCommand;
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
import org.eclipse.gmf.runtime.emf.type.core.commands.DestroyElementCommand;
import org.eclipse.gmf.runtime.emf.type.core.requests.DestroyElementRequest;
import org.eclipse.sirius.diagram.DNode;
import org.eclipse.sirius.diagram.description.NodeMapping;
import org.eclipse.sirius.diagram.description.style.NodeStyleDescription;
import org.eclipse.sirius.diagram.description.style.SquareDescription;
import org.eclipse.sirius.diagram.description.style.WorkspaceImageDescription;
import org.eclipse.sirius.diagram.sequence.business.internal.RangeHelper;
import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment;
import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement;
import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent;
import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram;
import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants;
import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper;
import org.eclipse.sirius.diagram.sequence.business.internal.tool.ToolCommandBuilder;
import org.eclipse.sirius.diagram.sequence.description.EndOfLifeMapping;
import org.eclipse.sirius.diagram.sequence.ordering.EventEnd;
import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd;
import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations;
import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ExecutionEditPart;
import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ISequenceEventEditPart;
import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.LifelineEditPart;
import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.RangeGuide;
import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.SequenceGraphicalHelper;
import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.EditPartsHelper;
import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery;
import org.eclipse.sirius.diagram.sequence.util.Range;
import org.eclipse.sirius.diagram.ui.internal.edit.policies.DNode2ItemSemanticEditPolicy;
import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutUtils;
import org.eclipse.sirius.diagram.ui.tools.internal.util.EditPartQuery;
import org.eclipse.sirius.ext.gmf.runtime.editparts.GraphicalHelper;
import com.google.common.collect.Iterables;
/**
* A specialized semantic edit policy for sequence diagram elements.
* <ul>
* <li>Overrides the REQ_CONNECTION_END to invoke the appropriate message creation tool with the additional variables
* required for its insertion at the right place in the semantic model.</li>
* <li>Shows horizontal feedback lines when an execution is moved/resized.</li>
* </ul>
*
* @author pcdavid
*/
public class ExecutionSemanticEditPolicy extends DNode2ItemSemanticEditPolicy {
private RangeGuide forbiddenRangeArea;
/**
* Overridden to handle the REQ_CONNECTION_END specially as the location at which a message is created has a
* semantic meaning.
* <p>
* {@inheritDoc}
*/
@Override
public Command getCommand(final Request request) {
Command superCommand = super.getCommand(request);
Command command = customizeEndOfLifeCreationCommand(request, superCommand);
return command;
}
private Command customizeEndOfLifeCreationCommand(final Request request, Command superCommand) {
Command command;
command = superCommand;
if (request instanceof CreateViewRequest) {
CreateViewRequest cvr = (CreateViewRequest) request;
EndOfLifeMapping endOfLifeMapping = getEndOfLifeMapping(cvr);
if (cvr.getLocation().x == -1 && cvr.getLocation().y == -1 && endOfLifeMapping != null) {
// adding an EndOfLifeEditPart will resize the RootExecution
// to
// keep the same global size on the lifeline
String label = superCommand != null ? superCommand.getLabel() : ""; //$NON-NLS-1$
CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(getEditingDomain(), label);
if (superCommand != null) {
ctc.compose(new CommandProxy(superCommand));
}
ChangeBoundsRequest cbr = new ChangeBoundsRequest(org.eclipse.gef.RequestConstants.REQ_RESIZE);
cbr.setResizeDirection(PositionConstants.SOUTH);
cbr.getSizeDelta().height = -calculateHalfSizeOfEndOfLifeEditPartToCreate(endOfLifeMapping);
ctc.compose(new CommandProxy(getHost().getCommand(cbr)));
ICommandProxy iCommandProxy = new ICommandProxy(ctc);
iCommandProxy.setLabel(ctc.getLabel());
command = iCommandProxy;
}
}
return command;
}
/**
* Calculate the half size of the EndOfLifeEditPart that will be created.
*
* @param request
* the request to create a new connection targeting an
* {@link org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.LifelineEditPart}
* @return half the size of the EndOfLifeEditPart that will be created
*/
private int calculateHalfSizeOfEndOfLifeEditPartToCreate(EndOfLifeMapping endOfLifeMapping) {
NodeStyleDescription style = endOfLifeMapping.getStyle();
int halfsize = 0;
if (style instanceof SquareDescription) {
SquareDescription sd = (SquareDescription) style;
halfsize = (sd.getHeight() * LayoutUtils.SCALE) / 2;
if (halfsize < LayoutUtils.SCALE) {
// minimum size displayed
halfsize = LayoutUtils.SCALE;
}
} else if (style instanceof WorkspaceImageDescription) {
WorkspaceImageDescription wid = (WorkspaceImageDescription) style;
String sizeComputationExpression = wid.getSizeComputationExpression();
Integer sizeComputationInteger = Integer.valueOf(sizeComputationExpression);
if (sizeComputationInteger != null) {
halfsize = (sizeComputationInteger * LayoutUtils.SCALE) / 2;
}
}
return halfsize;
}
/**
* Overridden to have feedback on forbidden combined fragment header range.
*
* {@inheritDoc}
*/
@Override
public void showTargetFeedback(Request request) {
removeForbiddenRangeFeedback();
if (request instanceof CreateRequest && ((CreateRequest) request).getLocation() != null) {
ISequenceEventEditPart host = (ISequenceEventEditPart) getHost();
ISequenceEvent sequenceEvent = host.getISequenceEvent();
SequenceDiagram sequenceDiagram = sequenceEvent.getDiagram();
CreateRequest createRequest = (CreateRequest) request;
Point location = createRequest.getLocation().getCopy();
GraphicalHelper.screen2logical(location, (IGraphicalEditPart) getHost());
EventEnd startingEndPredecessor = SequenceGraphicalHelper.getEndBefore(sequenceDiagram.getSequenceDDiagram(), location.y);
if (ToolCommandBuilder.isStartingEventEndOfCombinedFragment(sequenceDiagram, startingEndPredecessor) && startingEndPredecessor instanceof SingleEventEnd) {
IFigure layer = getFeedbackLayer();
// show forbidden range
SingleEventEnd singleEventEnd = (SingleEventEnd) startingEndPredecessor;
ISequenceEvent ise = EventEndHelper.findISequenceEvent(singleEventEnd, sequenceDiagram);
if (layer != null && ise instanceof CombinedFragment) {
Range verticalRange = ise.getVerticalRange();
Rectangle screenRange = new Rectangle(0, verticalRange.getLowerBound(), 0, LayoutConstants.COMBINED_FRAGMENT_TITLE_HEIGHT);
screenRange.performScale(GraphicalHelper.getZoom(getHost()));
Range forbiddenRange = RangeHelper.verticalRange(screenRange);
forbiddenRangeArea = new RangeGuide(SequenceInteractionFeedBackBuilder.CONFLICT_FEEDBACK_COLOR, forbiddenRange, true);
Rectangle bounds = layer.getBounds().getCopy();
bounds.height = forbiddenRange.width();
bounds.y = forbiddenRange.getLowerBound();
forbiddenRangeArea.setBounds(bounds);
layer.add(forbiddenRangeArea);
}
} else {
super.showTargetFeedback(request);
}
} else {
super.showTargetFeedback(request);
}
}
/**
* Overridden to erase feedback of forbidden combined fragment header range.
*
* {@inheritDoc}
*/
@Override
public void eraseTargetFeedback(Request request) {
removeForbiddenRangeFeedback();
super.eraseTargetFeedback(request);
}
/**
* {@inheritDoc}
*/
@Override
public EditPart getTargetEditPart(Request request) {
EditPart result = super.getTargetEditPart(request);
RequestQuery requestQuery = new RequestQuery(request);
if (request instanceof CreateConnectionRequest && requestQuery.isStandardMessageCreation() && getHost() instanceof ISequenceEventEditPart
&& org.eclipse.gef.RequestConstants.REQ_CONNECTION_END.equals(request.getType())) {
CreateConnectionRequest createRequest = (CreateConnectionRequest) request;
ISequenceEventEditPart ise = (ISequenceEventEditPart) getHost();
if (ise instanceof ExecutionEditPart) {
ise = new EditPartQuery(ise).getFirstAncestorOfType(LifelineEditPart.class);
} else if (!(ise instanceof LifelineEditPart)) {
ise = null;
}
EditPart source = createRequest.getSourceEditPart();
if (source instanceof ExecutionEditPart) {
source = new EditPartQuery((IGraphicalEditPart) source).getFirstAncestorOfType(LifelineEditPart.class);
} else if (!(source instanceof LifelineEditPart)) {
source = null;
}
// if ise lifeline equals source lifeline : reflexive message
if (ise != null && !ise.equals(source)) {
Point firstClickLocation = SequenceEditPartsOperations.getConnectionSourceLocation(createRequest, ise);
if (firstClickLocation != null) {
GraphicalHelper.screen2logical(firstClickLocation, ise);
ISequenceEvent sequenceEvent = ise.getISequenceEvent();
Range targetRange = sequenceEvent.getVerticalRange();
for (ExecutionEditPart potentialTarget : EditPartsHelper.getAllExecutions(ise)) {
Range range = potentialTarget.getISequenceEvent().getVerticalRange();
if (targetRange.includes(range) && range.includes(firstClickLocation.y)) {
targetRange = range;
result = potentialTarget;
}
}
}
}
}
return result;
}
/**
* Investigate the request to return the EndOfLifeMapping if existent
*
* @param createViewRequest
* the current request
* @return the EndOfLifeMapping if existent
*/
private EndOfLifeMapping getEndOfLifeMapping(CreateViewRequest createViewRequest) {
Iterable<ViewDescriptor> filter = Iterables.filter(createViewRequest.getViewDescriptors(), ViewDescriptor.class);
for (ViewDescriptor viewDescriptor : filter) {
IAdaptable elementAdapter = viewDescriptor.getElementAdapter();
if (elementAdapter instanceof EObjectAdapter) {
EObjectAdapter eobjectAdapter = (EObjectAdapter) elementAdapter;
Object realObject = eobjectAdapter.getRealObject();
if (realObject instanceof DNode) {
DNode dns = (DNode) realObject;
NodeMapping actualMapping = dns.getActualMapping();
if (actualMapping instanceof EndOfLifeMapping) {
return (EndOfLifeMapping) actualMapping;
}
}
}
}
return null;
}
/**
* .
*
* @param sequenceElement
* .
* @param sequenceDiagram
* .
* @param firstClickLocation
* .
* @param secondClickLocation
* .
* @return .
*/
public static boolean isCombinedFragmentTitleRangeEdgeCreation(ISequenceElement sequenceElement, SequenceDiagram sequenceDiagram, Point firstClickLocation, Point secondClickLocation) {
boolean result = false;
EventEnd startingEndPredecessor = SequenceGraphicalHelper.getEndBefore(sequenceDiagram.getSequenceDDiagram(), firstClickLocation.y);
if (ToolCommandBuilder.isStartingEventEndOfCombinedFragment(sequenceDiagram, startingEndPredecessor)) {
result = true;
}
EventEnd finishingEndPredecessor = SequenceGraphicalHelper.getEndBefore(sequenceDiagram.getSequenceDDiagram(), secondClickLocation.y);
if (ToolCommandBuilder.isStartingEventEndOfCombinedFragment(sequenceDiagram, finishingEndPredecessor)) {
result = true;
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
protected Command getDestroyElementCommand(DestroyElementRequest req) {
CompoundCommand cc = new CompoundCommand();
addDestroyChildNodesCommand(cc);
addDestroyShortcutsCommand(cc);
cc.add(getGEFWrapper(new DestroyElementCommand(req)));
return cc.unwrap();
}
private void removeForbiddenRangeFeedback() {
IFigure layer = getFeedbackLayer();
if (forbiddenRangeArea != null && layer.getChildren().contains(forbiddenRangeArea)) {
layer.remove(forbiddenRangeArea);
}
}
private IFigure getFeedbackLayer() {
IFigure layer = LayerManager.Helper.find(getHost()).getLayer(LayerConstants.FEEDBACK_LAYER);
return layer;
}
}