diff options
Diffstat (limited to 'plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui')
107 files changed, 20073 insertions, 0 deletions
diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/SequenceDiagramPlugin.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/SequenceDiagramPlugin.java new file mode 100644 index 0000000000..cd6ffd4b4c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/SequenceDiagramPlugin.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +import org.eclipse.sirius.common.tools.api.interpreter.EvaluationException; + +/** + * The activator class controls the plug-in life cycle. + * + * @author pcdavid + */ +public class SequenceDiagramPlugin extends AbstractUIPlugin { + /** + * The plug-in ID. + */ + public static final String PLUGIN_ID = "org.eclipse.sirius.diagram.sequence.ui"; + + /** + * The shared instance. + */ + private static SequenceDiagramPlugin plugin; + + /** + * The constructor. + */ + public SequenceDiagramPlugin() { + } + + /** + * {@inheritDoc} + */ + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /** + * {@inheritDoc} + */ + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance. + * + * @return the shared instance. + */ + public static SequenceDiagramPlugin getDefault() { + return plugin; + } + + /** + * Logs a warning. + * + * @param message + * the message. + * @param e + * the exception. + */ + public void warning(String message, EvaluationException e) { + IStatus status = new Status(IStatus.WARNING, PLUGIN_ID, message, e); + getLog().log(status); + } + + /** + * Logs an error. + * + * @param message + * the message. + * @param e + * the exception. + */ + public void error(String message, EvaluationException e) { + IStatus status = new Status(IStatus.ERROR, PLUGIN_ID, message, e); + getLog().log(status); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/api/diagramtype/SequenceDiagramInterpretedExpressionSwitch.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/api/diagramtype/SequenceDiagramInterpretedExpressionSwitch.java new file mode 100644 index 0000000000..e67faebedc --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/api/diagramtype/SequenceDiagramInterpretedExpressionSwitch.java @@ -0,0 +1,268 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.business.api.diagramtype; + +import java.util.Collection; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; + +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.business.api.dialect.description.IInterpretedExpressionTargetSwitch; +import org.eclipse.sirius.description.RepresentationDescription; +import org.eclipse.sirius.description.RepresentationElementMapping; +import org.eclipse.sirius.diagram.sequence.description.DelimitedEventMapping; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.description.FrameMapping; +import org.eclipse.sirius.diagram.sequence.description.MessageMapping; +import org.eclipse.sirius.diagram.sequence.description.ReturnMessageMapping; +import org.eclipse.sirius.diagram.sequence.description.SequenceDiagramDescription; +import org.eclipse.sirius.diagram.sequence.description.util.DescriptionSwitch; + +/** + * A switch that will return the Target Types associated to a given element + * (here all elements are sequence diagram-specific) and feature corresponding + * to an Interpreted Expression. For example, for a NodeMapping : + * <p> + * <li>if the feature is semantic candidate expression, we return the domain + * class of the first valid container (representation element mapping or + * representation description).</li> + * <li>if the feature is any other interpreted expression, we return the domain + * class associated to this mapping</li> + * </p> + * + * Can return {@link Options#newNone()} if the given expression does not require + * any target type (for example, a Popup menu contribution only uses variables + * in its expressions). + * + * @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a> + * + */ +public class SequenceDiagramInterpretedExpressionSwitch extends DescriptionSwitch<Option<Collection<String>>> { + + /** + * Constant used in switches on feature id to consider the case when the + * feature must not be considered. + */ + private static final int DO_NOT_CONSIDER_FEATURE = -1; + + /** + * The feature containing the Interpreted expression. + */ + protected EStructuralFeature feature; + + /** + * Indicates if the feature must be considered. + */ + protected boolean considereFeature; + + /** + * The global switch to delegate the doSwitch method to. + */ + protected IInterpretedExpressionTargetSwitch globalSwitch; + + /** + * Default constructor. + * + * @param feature + * representationDescription + * @param targetSwitch + * the global switch + */ + public SequenceDiagramInterpretedExpressionSwitch(EStructuralFeature feature, IInterpretedExpressionTargetSwitch targetSwitch) { + super(); + this.feature = feature; + this.globalSwitch = targetSwitch; + } + + /** + * {@inheritDoc} + */ + @Override + public Option<Collection<String>> doSwitch(EObject theEObject) { + Option<Collection<String>> doSwitch = super.doSwitch(theEObject); + if (doSwitch != null) { + return doSwitch; + } + Collection<String> defaultResult = Sets.newLinkedHashSet(); + return Options.newSome(defaultResult); + } + + /** + * Changes the behavior of this switch : if true, then the feature will be + * considered to calculate target types ; if false, then the feature will be + * ignored. + * + * @param considerFeature + * true if the feature should be considered, false otherwise + */ + public void setConsiderFeature(boolean considerFeature) { + this.considereFeature = considerFeature; + } + + private int getFeatureId(EClass eClass) { + int featureID = DO_NOT_CONSIDER_FEATURE; + if (considereFeature && feature != null) { + featureID = eClass.getFeatureID(feature); + } + return featureID; + } + + /** + * Returns the {@link RepresentationDescription} that contains the given + * element. + * + * @param element + * the element to get the {@link RepresentationDescription} from + * @return the {@link RepresentationDescription} that contains the given + * element, null if none found + */ + protected EObject getRepresentationDescription(EObject element) { + EObject container = element.eContainer(); + while (!(container instanceof RepresentationDescription)) { + container = container.eContainer(); + } + return container; + } + + /** + * Returns the first relevant for the given EObject, i.e. the first + * container from which a domain class can be determined. + * <p> + * For example, for a given NodeMapping will return the first + * ContainerMapping or DiagramRepresentationDescription that contains this + * mapping. + * </p> + * + * @param element + * the element to get the container from + * @return the first relevant for the given EObject, i.e. the first + * container from which a domain class can be determined + */ + protected EObject getFirstRelevantContainer(EObject element) { + EObject container = element.eContainer(); + while ((!(container instanceof RepresentationDescription)) && (!(container instanceof RepresentationElementMapping))) { + container = container.eContainer(); + } + return container; + } + + /** + * {@inheritDoc} + */ + @Override + public Option<Collection<String>> caseSequenceDiagramDescription(SequenceDiagramDescription object) { + Option<Collection<String>> result = null; + switch (getFeatureId(object.eClass())) { + case DescriptionPackage.SEQUENCE_DIAGRAM_DESCRIPTION__ENDS_ORDERING: + case DescriptionPackage.SEQUENCE_DIAGRAM_DESCRIPTION__INSTANCE_ROLES_ORDERING: + Collection<String> target = Sets.newLinkedHashSet(); + target.add(object.getDomainClass()); + result = Options.newSome(target); + break; + default: + break; + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public Option<Collection<String>> caseDelimitedEventMapping(DelimitedEventMapping object) { + Option<Collection<String>> result = null; + switch (getFeatureId(DescriptionPackage.eINSTANCE.getDelimitedEventMapping())) { + case DescriptionPackage.DELIMITED_EVENT_MAPPING__STARTING_END_FINDER_EXPRESSION: + case DescriptionPackage.DELIMITED_EVENT_MAPPING__FINISHING_END_FINDER_EXPRESSION: + Collection<String> target = Sets.newLinkedHashSet(); + // LEt the global swith return the same types than precondition. + Option<Collection<String>> types = globalSwitch.doSwitch(object, false); + if (types.some()) { + target.addAll(types.get()); + } + result = Options.newSome(target); + break; + default: + break; + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public Option<Collection<String>> caseFrameMapping(FrameMapping object) { + Option<Collection<String>> result = null; + switch (getFeatureId(DescriptionPackage.eINSTANCE.getFrameMapping())) { + case DescriptionPackage.FRAME_MAPPING__CENTER_LABEL_EXPRESSION: + case DescriptionPackage.FRAME_MAPPING__COVERED_LIFELINES_EXPRESSION: + case DO_NOT_CONSIDER_FEATURE: + Collection<String> target = Sets.newLinkedHashSet(); + target.add(object.getDomainClass()); + result = Options.newSome(target); + break; + default: + break; + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public Option<Collection<String>> caseMessageMapping(MessageMapping object) { + Option<Collection<String>> result = null; + switch (getFeatureId(DescriptionPackage.eINSTANCE.getMessageMapping())) { + case DescriptionPackage.MESSAGE_MAPPING__RECEIVING_END_FINDER_EXPRESSION: + case DescriptionPackage.MESSAGE_MAPPING__SENDING_END_FINDER_EXPRESSION: + Collection<String> target = Sets.newLinkedHashSet(); + // LEt the global swith return the same types than precondition. + Option<Collection<String>> types = globalSwitch.doSwitch(object, false); + if (types.some()) { + target.addAll(types.get()); + } + result = Options.newSome(target); + break; + default: + break; + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public Option<Collection<String>> caseReturnMessageMapping(ReturnMessageMapping object) { + Option<Collection<String>> result = null; + switch (getFeatureId(DescriptionPackage.eINSTANCE.getReturnMessageMapping())) { + case DescriptionPackage.RETURN_MESSAGE_MAPPING__INVOCATION_MESSAGE_FINDER_EXPRESSION: + Collection<String> target = Sets.newLinkedHashSet(); + // LEt the global switch return the same types than precondition. + Option<Collection<String>> types = globalSwitch.doSwitch(object, false); + if (types.some()) { + target.addAll(types.get()); + } + result = Options.newSome(target); + break; + default: + break; + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/api/diagramtype/SequenceDiagramTypeProvider.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/api/diagramtype/SequenceDiagramTypeProvider.java new file mode 100644 index 0000000000..0fa8ba3175 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/api/diagramtype/SequenceDiagramTypeProvider.java @@ -0,0 +1,434 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.business.api.diagramtype; + +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; + +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.ecore.EAnnotation; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.edit.command.CommandParameter; +import org.eclipse.emf.edit.provider.ComposedAdapterFactory; +import org.eclipse.gmf.runtime.notation.Diagram; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.business.api.diagramtype.HeaderData; +import org.eclipse.sirius.business.api.diagramtype.ICollapseUpdater; +import org.eclipse.sirius.business.api.diagramtype.IDiagramDescriptionProvider; +import org.eclipse.sirius.business.api.dialect.description.DefaultInterpretedExpressionTargetSwitch; +import org.eclipse.sirius.business.api.dialect.description.IInterpretedExpressionTargetSwitch; +import org.eclipse.sirius.business.api.query.DiagramElementMappingQuery; +import org.eclipse.sirius.description.DiagramDescription; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.description.tool.AbstractVariable; +import org.eclipse.sirius.description.tool.ToolPackage; +import org.eclipse.sirius.diagram.business.api.query.DDiagramGraphicalQuery; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.LostMessageEnd; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.query.InstanceRoleQuery; +import org.eclipse.sirius.diagram.sequence.description.DescriptionFactory; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.description.provider.DescriptionItemProviderAdapterFactory; +import org.eclipse.sirius.diagram.sequence.description.tool.CombinedFragmentCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ExecutionCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InstanceRoleCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InstanceRoleReorderTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InteractionUseCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.MessageCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ObservationPointCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.OperandCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ReorderTool; +import org.eclipse.sirius.diagram.sequence.description.tool.SequenceDiagramToolDescription; +import org.eclipse.sirius.diagram.sequence.description.tool.StateCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ToolFactory; +import org.eclipse.sirius.diagram.sequence.description.tool.provider.ToolItemProviderAdapterFactory; +import org.eclipse.sirius.diagram.sequence.ordering.provider.OrderingItemProviderAdapterFactory; +import org.eclipse.sirius.diagram.sequence.provider.SequenceItemProviderAdapterFactory; +import org.eclipse.sirius.diagram.sequence.template.provider.TemplateItemProviderAdapterFactory; +import org.eclipse.sirius.diagram.sequence.ui.business.internal.diagramtype.SequenceCollapseUpdater; +import org.eclipse.sirius.diagram.sequence.ui.business.internal.diagramtype.SequenceToolInterpretedExpressionSwitch; + +/** + * Provides diagram description for Sequence diagram. + * + * @author ymortier, pcdavid + */ +public class SequenceDiagramTypeProvider implements IDiagramDescriptionProvider { + + private final Predicate<DSemanticDecorator> isSequenceSemanticDecorator = new Predicate<DSemanticDecorator>() { + public boolean apply(DSemanticDecorator input) { + boolean result = false; + if (input instanceof DDiagram) { + result = checkSequenceDescriptionPackage(((DDiagram) input).getDescription().eClass()); + } else if (input instanceof DDiagramElement) { + result = isSequenceDDiagramElement.apply((DDiagramElement) input); + } + return result; + } + }; + + private final Predicate<DDiagramElement> isSequenceDDiagramElement = new Predicate<DDiagramElement>() { + public boolean apply(DDiagramElement input) { + // check that input has a Sequence mapping or is a simple node + // connected to a sequence message : a lost end. + DiagramElementMapping mappingToCheck = new DiagramElementMappingQuery(input.getDiagramElementMapping()).getRootMapping(); + return checkSequenceDescriptionPackage(mappingToCheck.eClass()) || LostMessageEnd.viewpointElementPredicate().apply(input); + } + }; + + private boolean checkSequenceDescriptionPackage(EClass eClass) { + return DescriptionPackage.eINSTANCE.equals(eClass.getEPackage()); + } + + /** + * {@inheritDoc} + */ + public DiagramDescription createDiagramDescription() { + return DescriptionFactory.eINSTANCE.createSequenceDiagramDescription(); + } + + /** + * {@inheritDoc} + */ + public Collection<? extends CommandParameter> collectToolCommands(EObject context) { + Collection<CommandParameter> result = Lists.newArrayList(); + Collection<EReference> refs = Arrays.asList(ToolPackage.Literals.TOOL_SECTION__OWNED_TOOLS, ToolPackage.Literals.TOOL_GROUP__TOOLS); + for (EReference ref : refs) { + if (ref.getEContainingClass().equals(context.eClass())) { + collectInstanceRoleCreation(result, ref); + collectExecutionCreation(result, ref); + collectStateCreation(result, ref); + collectMessageCreation(result, ref); + collectInteractionUse(result, ref); + collectCombinedFragmentCreation(result, ref); + collectOperandCreation(result, ref); + collectReorderCreations(result, ref); + collectObservationPointCreation(result, ref); + } + } + return result; + } + + /** + * {@inheritDoc} + */ + public Collection<? extends CommandParameter> collectMappingsCommands() { + Collection<CommandParameter> result = Lists.newArrayList(); + // Nodes + result.add(new CommandParameter(null, org.eclipse.sirius.description.DescriptionPackage.Literals.LAYER__NODE_MAPPINGS, DescriptionFactory.eINSTANCE.createInstanceRoleMapping())); + result.add(new CommandParameter(null, org.eclipse.sirius.description.DescriptionPackage.Literals.LAYER__NODE_MAPPINGS, DescriptionFactory.eINSTANCE.createExecutionMapping())); + result.add(new CommandParameter(null, org.eclipse.sirius.description.DescriptionPackage.Literals.LAYER__NODE_MAPPINGS, DescriptionFactory.eINSTANCE.createStateMapping())); + result.add(new CommandParameter(null, org.eclipse.sirius.description.DescriptionPackage.Literals.LAYER__NODE_MAPPINGS, DescriptionFactory.eINSTANCE.createEndOfLifeMapping())); + result.add(new CommandParameter(null, org.eclipse.sirius.description.DescriptionPackage.Literals.LAYER__NODE_MAPPINGS, DescriptionFactory.eINSTANCE.createObservationPointMapping())); + // Containers + result.add(new CommandParameter(null, org.eclipse.sirius.description.DescriptionPackage.Literals.LAYER__CONTAINER_MAPPINGS, DescriptionFactory.eINSTANCE.createInteractionUseMapping())); + result.add(new CommandParameter(null, org.eclipse.sirius.description.DescriptionPackage.Literals.LAYER__CONTAINER_MAPPINGS, DescriptionFactory.eINSTANCE.createCombinedFragmentMapping())); + result.add(new CommandParameter(null, org.eclipse.sirius.description.DescriptionPackage.Literals.LAYER__CONTAINER_MAPPINGS, DescriptionFactory.eINSTANCE.createOperandMapping())); + // Edges + result.add(new CommandParameter(null, org.eclipse.sirius.description.DescriptionPackage.Literals.LAYER__EDGE_MAPPINGS, DescriptionFactory.eINSTANCE.createBasicMessageMapping())); + result.add(new CommandParameter(null, org.eclipse.sirius.description.DescriptionPackage.Literals.LAYER__EDGE_MAPPINGS, DescriptionFactory.eINSTANCE.createReturnMessageMapping())); + result.add(new CommandParameter(null, org.eclipse.sirius.description.DescriptionPackage.Literals.LAYER__EDGE_MAPPINGS, DescriptionFactory.eINSTANCE.createCreationMessageMapping())); + result.add(new CommandParameter(null, org.eclipse.sirius.description.DescriptionPackage.Literals.LAYER__EDGE_MAPPINGS, DescriptionFactory.eINSTANCE.createDestructionMessageMapping())); + return result; + } + + /** + * {@inheritDoc} + */ + public AdapterFactory getAdapterFactory() { + ComposedAdapterFactory composed = new ComposedAdapterFactory(); + composed.addAdapterFactory(new SequenceItemProviderAdapterFactory()); + composed.addAdapterFactory(new TemplateItemProviderAdapterFactory()); + composed.addAdapterFactory(new DescriptionItemProviderAdapterFactory()); + composed.addAdapterFactory(new OrderingItemProviderAdapterFactory()); + composed.addAdapterFactory(new ToolItemProviderAdapterFactory()); + return composed; + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.sirius.business.api.diagramtype.IDiagramDescriptionProvider#handles(org.eclipse.emf.ecore.EPackage) + */ + public boolean handles(EPackage ePackage) { + return DescriptionPackage.eINSTANCE.getNsURI().equals(ePackage.getNsURI()) + || org.eclipse.sirius.diagram.sequence.description.tool.ToolPackage.eINSTANCE.getNsURI().equals(ePackage.getNsURI()); + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.sirius.business.api.diagramtype.IDiagramDescriptionProvider#createInterpretedExpressionSwitch(org.eclipse.emf.ecore.EObject, + * org.eclipse.emf.ecore.EStructuralFeature) + */ + public IInterpretedExpressionTargetSwitch createInterpretedExpressionSwitch(EStructuralFeature feature, IInterpretedExpressionTargetSwitch parentSwitch) { + return new SequenceGlobalInterpretedTargetSwitch(feature, parentSwitch); + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.sirius.business.api.diagramtype.IDiagramDescriptionProvider#allowsLayoutingModeActivation() + */ + public boolean allowsLayoutingModeActivation() { + return false; + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.sirius.business.api.diagramtype.IDiagramDescriptionProvider#allowsPinUnpin() + */ + public Predicate<DDiagramElement> allowsPinUnpin() { + return Predicates.not(isSequenceDDiagramElement); + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.sirius.business.api.diagramtype.IDiagramDescriptionProvider#allowsHideReveal() + */ + public Predicate<DDiagramElement> allowsHideReveal() { + return Predicates.not(isSequenceDDiagramElement); + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.sirius.business.api.diagramtype.IDiagramDescriptionProvider#allowsCopyPasteLayout() + */ + public Predicate<DSemanticDecorator> allowsCopyPasteLayout() { + return Predicates.not(isSequenceSemanticDecorator); + } + + private void collectInstanceRoleCreation(Collection<CommandParameter> result, EReference ref) { + InstanceRoleCreationTool instanceRoleCreationTool = ToolFactory.eINSTANCE.createInstanceRoleCreationTool(); + addVariables(instanceRoleCreationTool); + result.add(new CommandParameter(null, ref, instanceRoleCreationTool)); + } + + private void collectExecutionCreation(Collection<CommandParameter> result, EReference ref) { + ExecutionCreationTool executionCreationTool = ToolFactory.eINSTANCE.createExecutionCreationTool(); + addVariables(executionCreationTool); + result.add(new CommandParameter(null, ref, executionCreationTool)); + } + + private void collectStateCreation(Collection<CommandParameter> result, EReference ref) { + StateCreationTool stateCreationTool = ToolFactory.eINSTANCE.createStateCreationTool(); + addVariables(stateCreationTool); + result.add(new CommandParameter(null, ref, stateCreationTool)); + } + + private void collectMessageCreation(Collection<CommandParameter> result, EReference ref) { + MessageCreationTool messageCreationTool = ToolFactory.eINSTANCE.createMessageCreationTool(); + addVariables(messageCreationTool); + result.add(new CommandParameter(null, ref, messageCreationTool)); + } + + private void collectCombinedFragmentCreation(Collection<CommandParameter> result, EReference ref) { + CombinedFragmentCreationTool combinedFragmentCreationTool = ToolFactory.eINSTANCE.createCombinedFragmentCreationTool(); + addVariables(combinedFragmentCreationTool); + result.add(new CommandParameter(null, ref, combinedFragmentCreationTool)); + } + + private void collectOperandCreation(Collection<CommandParameter> result, EReference ref) { + OperandCreationTool operandCreationTool = ToolFactory.eINSTANCE.createOperandCreationTool(); + addVariables(operandCreationTool); + result.add(new CommandParameter(null, ref, operandCreationTool)); + } + + private void collectReorderCreations(Collection<CommandParameter> result, EReference ref) { + ReorderTool reorderCreationTool = ToolFactory.eINSTANCE.createReorderTool(); + addVariables(reorderCreationTool); + result.add(new CommandParameter(null, ref, reorderCreationTool)); + + InstanceRoleReorderTool irReorderCreationTool = ToolFactory.eINSTANCE.createInstanceRoleReorderTool(); + addVariables(irReorderCreationTool); + result.add(new CommandParameter(null, ref, irReorderCreationTool)); + } + + private void collectInteractionUse(Collection<CommandParameter> result, EReference ref) { + InteractionUseCreationTool interactionUseCreationTool = ToolFactory.eINSTANCE.createInteractionUseCreationTool(); + addVariables(interactionUseCreationTool); + result.add(new CommandParameter(null, ref, interactionUseCreationTool)); + } + + private void collectObservationPointCreation(Collection<CommandParameter> result, EReference ref) { + ObservationPointCreationTool obsPointCreationTool = ToolFactory.eINSTANCE.createObservationPointCreationTool(); + addVariables(obsPointCreationTool); + result.add(new CommandParameter(null, ref, obsPointCreationTool)); + } + + private void addVariables(SequenceDiagramToolDescription sequenceDiagramTool) { + for (EReference ref : sequenceDiagramTool.eClass().getEAllReferences()) { + if (ref.isContainment() && ref.getEType() instanceof EClass) { + EClass k = (EClass) ref.getEType(); + EClass variable = org.eclipse.sirius.description.tool.ToolPackage.eINSTANCE.getAbstractVariable(); + if (variable.isSuperTypeOf(k)) { + AbstractVariable var = (AbstractVariable) k.getEPackage().getEFactoryInstance().create(k); + EAnnotation annotation = ref.getEAnnotation("toolVariable"); + if (annotation != null) { + var.setName(annotation.getDetails().get("name")); + } else { + var.setName(ref.getName()); + } + sequenceDiagramTool.eSet(ref, var); + } + } + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.business.api.diagramtype.IDiagramDescriptionProvider#supportHeader() + */ + public boolean supportHeader() { + return true; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.business.api.diagramtype.IDiagramDescriptionProvider#getHeaderData() + */ + public LinkedList<HeaderData> getHeaderData(DDiagram diagram) { + LinkedList<HeaderData> result = Lists.newLinkedList(); + if (diagram instanceof SequenceDDiagram) { + Diagram gmfDiagram = getGmfDiagram(diagram); + + if (gmfDiagram != null) { + Option<SequenceDiagram> optionalSequenceDiagram = ISequenceElementAccessor.getSequenceDiagram(gmfDiagram); + if (optionalSequenceDiagram.some()) { + Collection<InstanceRole> instanceRoles = optionalSequenceDiagram.get().getSortedInstanceRole(); + for (InstanceRole instanceRole : instanceRoles) { + result.add(new InstanceRoleQuery(instanceRole).getHeaderData()); + } + } + + } + } + return result; + } + + private Diagram getGmfDiagram(DDiagram diagram) { + Diagram gmfDiagram = null; + SequenceDDiagram sequenceDDiagram = (SequenceDDiagram) diagram; + Option<Diagram> optionDiagram = new DDiagramGraphicalQuery(sequenceDDiagram).getAssociatedGMFDiagram(); + if (optionDiagram.some()) { + gmfDiagram = optionDiagram.get(); + } else { + Resource eResource = diagram.eResource(); + if (eResource != null) { + for (Diagram diag : Iterables.filter(eResource.getContents(), Diagram.class)) { + if (diagram.equals(diag.getElement())) { + gmfDiagram = diag; + break; + } + } + } + } + return gmfDiagram; + } + + /** + * An {@link IInterpretedExpressionTargetSwitch} that delegates to the + * defaultSwitch or the diagram specific switch, according to the package of + * the considered element. + * + * @author <a href="mailto:maxime.porhel@obeo.fr">Maxime Porhel</a> + * + */ + private class SequenceGlobalInterpretedTargetSwitch implements IInterpretedExpressionTargetSwitch { + + private DefaultInterpretedExpressionTargetSwitch defaultSwitch; + + private SequenceDiagramInterpretedExpressionSwitch sequenceSwitch; + + private SequenceToolInterpretedExpressionSwitch toolSwitch; + + public SequenceGlobalInterpretedTargetSwitch(EStructuralFeature feature, IInterpretedExpressionTargetSwitch parentSwitch) { + defaultSwitch = new DefaultInterpretedExpressionTargetSwitch(feature, parentSwitch); + sequenceSwitch = new SequenceDiagramInterpretedExpressionSwitch(feature, parentSwitch); + toolSwitch = new SequenceToolInterpretedExpressionSwitch(feature, parentSwitch); + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.sirius.business.api.dialect.description.IInterpretedExpressionTargetSwitch#doSwitch(org.eclipse.emf.ecore.EObject) + */ + public Option<Collection<String>> doSwitch(EObject target, boolean considerFeature) { + Collection<String> targetTypes = Sets.newLinkedHashSet(); + Option<Collection<String>> expressionTarget = Options.newSome(targetTypes); + if (target != null) { + // Step 1 : apply the sequence diagram specific switch + sequenceSwitch.setConsiderFeature(considerFeature); + expressionTarget = sequenceSwitch.doSwitch(target); + + // If no result has been found + if (expressionTarget.some() && expressionTarget.get().isEmpty()) { + // Step 2 : apply the sequence tool specific switch + toolSwitch.setConsiderFeature(considerFeature); + expressionTarget = toolSwitch.doSwitch(target); + } + + // If no result has been found + if (expressionTarget.some() && expressionTarget.get().isEmpty()) { + // Step 3 : we use the default switch + expressionTarget = defaultSwitch.doSwitch(target, considerFeature); + } + } + return expressionTarget; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.business.api.diagramtype.IDiagramDescriptionProvider + * #getCollapseUpdater(org.eclipse.sirius.DDiagram) + */ + public Option<? extends ICollapseUpdater> getCollapseUpdater(DDiagram diagram) { + if (diagram != null && diagram.getDescription() != null && handles(diagram.getDescription().eClass().getEPackage())) { + return Options.newSome(new SequenceCollapseUpdater()); + } + return Options.newNone(); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/EObjectQuery.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/EObjectQuery.java new file mode 100644 index 0000000000..fbecdea807 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/EObjectQuery.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.business.internal; + +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.diagram.sequence.template.TSequenceDiagram; + +/** + * Class responsible for querying a model starting from an EObject. + * + * @author cbrun + * + */ +public class EObjectQuery { + + private EObject start; + + /** + * Create the query. + * + * @param start + * the EObject to start the query from. + */ + public EObjectQuery(EObject start) { + this.start = start; + } + + /** + * return the first TSequenceDiagram being parent of the current EObject. + * + * @return the first TSequenceDiagram being parent of the current EObject. + */ + public Option<TSequenceDiagram> getParentSequenceDiagramTemplate() { + EObject cur = start; + while (cur != null) { + if (cur instanceof TSequenceDiagram) { + return Options.newSome((TSequenceDiagram) cur); + } + cur = cur.eContainer(); + } + return Options.newNone(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/SequenceDiagramTemplateEdit.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/SequenceDiagramTemplateEdit.java new file mode 100644 index 0000000000..6acd04dac8 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/SequenceDiagramTemplateEdit.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.business.internal; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EStructuralFeature.Setting; +import org.eclipse.emf.ecore.util.ECrossReferenceAdapter; +import org.eclipse.emf.edit.command.CommandParameter; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.description.DescriptionPackage; +import org.eclipse.sirius.description.RepresentationTemplate; +import org.eclipse.sirius.diagram.sequence.template.TSequenceDiagram; +import org.eclipse.sirius.diagram.sequence.template.TemplateFactory; +import org.eclipse.sirius.diagram.sequence.template.TemplatePackage; +import org.eclipse.sirius.diagram.sequence.template.TemplateToDiagramDescriptionTransformer; +import org.eclipse.sirius.ui.business.api.template.RepresentationTemplateEdit; + +/** + * Implementation of a representation template Edit for the sequence diagram + * template. + * + * @author cbrun + * + */ +public class SequenceDiagramTemplateEdit implements RepresentationTemplateEdit { + /** + * {@inheritDoc} + */ + public Object getNewChildDescriptor() { + return new CommandParameter(null, DescriptionPackage.Literals.VIEWPOINT__OWNED_TEMPLATES, TemplateFactory.eINSTANCE.createTSequenceDiagram()); + } + + /** + * {@inheritDoc} + */ + public EObject getSourceElement(EObject vsmObject) { + Option<TSequenceDiagram> result = new EObjectQuery(vsmObject).getParentSequenceDiagramTemplate(); + if (result.some()) { + TSequenceDiagram template = result.get(); + ECrossReferenceAdapter crosser = getOrCreateCrossReferencer(template); + for (Setting setting : crosser.getInverseReferences(vsmObject)) { + if (setting.getEStructuralFeature() == TemplatePackage.eINSTANCE.getTTransformer_Outputs()) { + return setting.getEObject(); + } + + } + } + return null; + } + + /** + * {@inheritDoc} + */ + public void update(RepresentationTemplate template) { + if (template instanceof TSequenceDiagram) { + TemplateToDiagramDescriptionTransformer transformer = new TemplateToDiagramDescriptionTransformer((TSequenceDiagram) template); + transformer.refresh(); + } + } + + /** + * {@inheritDoc} + */ + public boolean isGenerated(EObject vsmObject) { + return getSourceElement(vsmObject) != null; + } + + private ECrossReferenceAdapter getOrCreateCrossReferencer(TSequenceDiagram template) { + ECrossReferenceAdapter crosser = ECrossReferenceAdapter.getCrossReferenceAdapter(template); + if (crosser == null) { + crosser = new ECrossReferenceAdapter(); + template.eAdapters().add(crosser); + } + return crosser; + } + + /** + * {@inheritDoc} + */ + public boolean isOverriden(EObject eObj, EStructuralFeature feature) { + Option<TSequenceDiagram> result = new EObjectQuery(eObj).getParentSequenceDiagramTemplate(); + if (result.some()) { + TSequenceDiagram template = result.get(); + return new TemplateToDiagramDescriptionTransformer(template).isOverriding(eObj, feature); + } + return false; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/diagramtype/SequenceCollapseUpdater.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/diagramtype/SequenceCollapseUpdater.java new file mode 100644 index 0000000000..b979b7dc8d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/diagramtype/SequenceCollapseUpdater.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.business.internal.diagramtype; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationFactory; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.CollapseFilter; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.business.api.helper.graphicalfilters.CollapseUpdater; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ObservationPoint; +import org.eclipse.sirius.diagram.ui.tools.internal.figure.ICollapseMode; + +/** + * A specific @link + * {@link org.eclipse.sirius.business.api.diagramtype.ICollapseUpdater} to + * manage correctly the + * {@link org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution} + * . + * + * @author lredor + */ +public class SequenceCollapseUpdater extends CollapseUpdater { + + private Predicate<DDiagramElement> specificCollapsePredicate = Predicates.or(AbstractNodeEvent.viewpointElementPredicate(), Lifeline.viewpointElementPredicate(), + ObservationPoint.viewpointElementPredicate()); + + /** + * Default constructor. + */ + public SequenceCollapseUpdater() { + } + + /** + * {@inheritDoc} + */ + protected void synchronizeCollapseFiltersAndGMFBounds(DDiagramElement element, Option<Node> optionalNode, boolean add, Class<? extends CollapseFilter> kindOfFilter) { + + if (InstanceRole.viewpointElementPredicate().apply(element)) + return; + + super.synchronizeCollapseFiltersAndGMFBounds(element, optionalNode, add, kindOfFilter); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.helper.graphicalfilters.CollapseUpdater#collapseBounds(org.eclipse.gmf.runtime.notation.Node, + * org.eclipse.sirius.DDiagramElement) + */ + public void collapseBounds(Node node, DDiagramElement element) { + if (!specificCollapsePredicate.apply(element)) { + super.collapseBounds(node, element); + } else { + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Bounds) { + Bounds bounds = (Bounds) layoutConstraint; + Bounds newBounds = NotationFactory.eINSTANCE.createBounds(); + // Initialize the collapsed bounds + newBounds.setX(bounds.getX() + bounds.getWidth() / 2); + newBounds.setWidth(ICollapseMode.COLLAPSED_DIMENSION.width); + + int newY = bounds.getY(); + int newHeight = bounds.getHeight(); + if (ObservationPoint.notationPredicate().apply(node)) { + newY = bounds.getY() + bounds.getHeight() / 2; + newHeight = ICollapseMode.COLLAPSED_DIMENSION.height; + } + newBounds.setY(newY); + newBounds.setHeight(newHeight); + node.setLayoutConstraint(newBounds); + } + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.helper.graphicalfilters.CollapseUpdater#getExpandedBounds(org.eclipse.gmf.runtime.notation.Node, + * org.eclipse.sirius.DDiagramElement) + */ + @Override + public Option<Bounds> getExpandedBounds(Node node, DDiagramElement element) { + Option<Bounds> optionalBounds; + if (!specificCollapsePredicate.apply(element)) { + optionalBounds = super.getExpandedBounds(node, element); + } else { + CollapseFilter filter = Iterables.getFirst(Iterables.filter(element.getGraphicalFilters(), CollapseFilter.class), null); + if (filter != null) { + optionalBounds = getExpandedBounds(node, new Dimension(filter.getWidth(), filter.getHeight())); + } else { + optionalBounds = Options.newNone(); + } + } + return optionalBounds; + } + + /** + * Get the expanded bounds of this node (the bound of this node must be in + * collapsed state before calling this method). + * + * @param node + * The node to expand. + * @param expandedSize + * The expanded size of this node + * @return An optional bounds. The bounds can be null. For example, if the + * current layout constraint of the <code>node</code> is not a + * Bounds. + */ + public Option<Bounds> getExpandedBounds(Node node, Dimension expandedSize) { + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Bounds) { + Bounds bounds = (Bounds) layoutConstraint; + Bounds newBounds = NotationFactory.eINSTANCE.createBounds(); + newBounds.setX(bounds.getX() - expandedSize.width / 2); + int newY = bounds.getY(); + if (ObservationPoint.notationPredicate().apply(node)) { + newY = bounds.getY() - expandedSize.height / 2; + } + newBounds.setY(newY); + newBounds.setWidth(expandedSize.width); + newBounds.setHeight(expandedSize.height); + return Options.newSome(newBounds); + } + return Options.newNone(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/diagramtype/SequenceToolInterpretedExpressionSwitch.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/diagramtype/SequenceToolInterpretedExpressionSwitch.java new file mode 100644 index 0000000000..969c2e4e0f --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/diagramtype/SequenceToolInterpretedExpressionSwitch.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.business.internal.diagramtype; + +import java.util.Collection; +import java.util.Collections; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.business.api.dialect.description.IInterpretedExpressionTargetSwitch; +import org.eclipse.sirius.description.RepresentationDescription; +import org.eclipse.sirius.description.RepresentationElementMapping; +import org.eclipse.sirius.description.tool.AbstractToolDescription; +import org.eclipse.sirius.description.tool.ChangeContext; +import org.eclipse.sirius.diagram.sequence.description.EventMapping; +import org.eclipse.sirius.diagram.sequence.description.InstanceRoleMapping; +import org.eclipse.sirius.diagram.sequence.description.tool.InstanceRoleReorderTool; +import org.eclipse.sirius.diagram.sequence.description.tool.MessageCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ReorderTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ToolPackage; +import org.eclipse.sirius.diagram.sequence.description.tool.util.ToolSwitch; + +/** + * A switch that will return the Target Types associated to a given element + * (part of the {@link ToolPackage}) and feature corresponding to an Interpreted + * Expression. For example, for a NodeMapping : + * <p> + * <li>if the feature is semantic candidate expression, we return the domain + * class of the first valid container (representation element mapping or + * representation description).</li> + * <li>if the feature is any other interpreted expression, we return the domain + * class associated to this mapping</li> + * </p> + * + * Can return {@link Options#newNone()} if the given expression does not require + * any target type (for example, a Popup menu contribution only uses variables + * in its expressions). + * + * @author <a href="mailto:maxime.porhel@obeo.fr">Maxime Porhel</a> + */ +public class SequenceToolInterpretedExpressionSwitch extends ToolSwitch<Option<Collection<String>>> { + + /** + * Constant used in switches on feature id to consider the case when the + * feature must not be considered. + */ + private static final int DO_NOT_CONSIDER_FEATURE = -1; + + /** + * The ID of the feature containing the Interpreted expression. + */ + protected EStructuralFeature feature; + + /** + * Indicates if the feature should be considered. + */ + protected boolean considerFeature; + + private IInterpretedExpressionTargetSwitch globalSwitch; + + /** + * Default constructor. + * + * @param feature + * the feature containing the Interpreted expression + * @param defaultInterpretedExpressionTargetSwitch + * the global switch to use + */ + public SequenceToolInterpretedExpressionSwitch(EStructuralFeature feature, IInterpretedExpressionTargetSwitch defaultInterpretedExpressionTargetSwitch) { + super(); + this.feature = feature; + this.globalSwitch = defaultInterpretedExpressionTargetSwitch; + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.sirius.description.tool.util.ToolSwitch#doSwitch(org.eclipse.emf.ecore.EObject) + */ + @Override + public Option<Collection<String>> doSwitch(EObject theEObject) { + Option<Collection<String>> doSwitch = super.doSwitch(theEObject); + if (doSwitch != null) { + return doSwitch; + } + + Collection<String> targets = Collections.emptySet(); + return Options.newSome(targets); + } + + /** + * Changes the behavior of this switch : if true, then the feature will be + * considered to calculate target types ; if false, then the feature will be + * ignored. + * + * @param considerFeature + * true if the feature should be considered, false otherwise + */ + public void setConsiderFeature(boolean considerFeature) { + this.considerFeature = considerFeature; + } + + private int getFeatureId(EClass eClass) { + int featureID = DO_NOT_CONSIDER_FEATURE; + if (considerFeature && feature != null) { + featureID = eClass.getFeatureID(feature); + } + return featureID; + } + + /** + * Returns the first element that changes the context of expressions. For + * example : for a given operation, will return the first ChangeContext or + * AbstractTool that contains it. + * + * @param element + * the element to get the container from + * @return the first relevant for the given EObject, i.e. the first + * container from which a domain class can be determined + */ + protected EObject getFirstContextChangingContainer(EObject element) { + EObject container = element.eContainer(); + while ((!(container instanceof RepresentationDescription)) && (!(container instanceof ChangeContext)) + && (!(container instanceof AbstractToolDescription) && (!(container instanceof RepresentationElementMapping)))) { + container = container.eContainer(); + } + return container; + } + + /** + * {@inheritDoc} + */ + @Override + public Option<Collection<String>> caseMessageCreationTool(MessageCreationTool object) { + Option<Collection<String>> result = null; + switch (getFeatureId(ToolPackage.eINSTANCE.getMessageCreationTool())) { + case ToolPackage.MESSAGE_CREATION_TOOL__CONNECTION_START_PRECONDITION: + case ToolPackage.MESSAGE_CREATION_TOOL__PRECONDITION: + case DO_NOT_CONSIDER_FEATURE: + Collection<String> targets = Sets.newLinkedHashSet(); + for (RepresentationElementMapping correspondingMapping : Iterables.concat(object.getEdgeMappings(), object.getExtraSourceMappings())) { + Option<Collection<String>> targetsFromMapping = globalSwitch.doSwitch(correspondingMapping, false); + if (targetsFromMapping.some()) { + targets.addAll(targetsFromMapping.get()); + } + } + result = Options.newSome(targets); + break; + default: + break; + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public Option<Collection<String>> caseReorderTool(ReorderTool object) { + Option<Collection<String>> result = null; + switch (getFeatureId(ToolPackage.eINSTANCE.getReorderTool())) { + case ToolPackage.REORDER_TOOL__PRECONDITION: + case DO_NOT_CONSIDER_FEATURE: + Collection<String> targets = Sets.newLinkedHashSet(); + for (EventMapping correspondingMapping : object.getMappings()) { + Option<Collection<String>> targetsFromMapping = globalSwitch.doSwitch(correspondingMapping, false); + if (targetsFromMapping.some()) { + targets.addAll(targetsFromMapping.get()); + } + } + result = Options.newSome(targets); + break; + default: + break; + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public Option<Collection<String>> caseInstanceRoleReorderTool(InstanceRoleReorderTool object) { + Option<Collection<String>> result = null; + switch (getFeatureId(ToolPackage.eINSTANCE.getInstanceRoleReorderTool())) { + case ToolPackage.INSTANCE_ROLE_REORDER_TOOL__PRECONDITION: + case DO_NOT_CONSIDER_FEATURE: + Collection<String> targets = Sets.newLinkedHashSet(); + for (InstanceRoleMapping correspondingMapping : object.getMappings()) { + Option<Collection<String>> targetsFromMapping = globalSwitch.doSwitch(correspondingMapping, false); + if (targetsFromMapping.some()) { + targets.addAll(targetsFromMapping.get()); + } + } + result = Options.newSome(targets); + break; + default: + break; + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/migration/SequenceDiagramRepresentationsFileMigrationParticipant.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/migration/SequenceDiagramRepresentationsFileMigrationParticipant.java new file mode 100644 index 0000000000..682b711729 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/migration/SequenceDiagramRepresentationsFileMigrationParticipant.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * Copyright (c) 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.business.internal.migration; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Node; +import org.osgi.framework.Version; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.AbstractDNode; +import org.eclipse.sirius.CollapseFilter; +import org.eclipse.sirius.DAnalysis; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DNodeContainer; +import org.eclipse.sirius.DNodeList; +import org.eclipse.sirius.DView; +import org.eclipse.sirius.GraphicalFilter; +import org.eclipse.sirius.IndirectlyCollapseFilter; +import org.eclipse.sirius.SiriusFactory; +import org.eclipse.sirius.business.api.migration.AbstractRepresentationsFileMigrationParticipant; +import org.eclipse.sirius.diagram.business.api.query.DDiagramGraphicalQuery; +import org.eclipse.sirius.diagram.business.api.query.NodeQuery; +import org.eclipse.sirius.diagram.internal.edit.parts.DNode2EditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNode3EditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNode4EditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeContainer2EditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeContainerEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeList2EditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeListEditPart; +import org.eclipse.sirius.diagram.part.SiriusVisualIDRegistry; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.ui.business.internal.diagramtype.SequenceCollapseUpdater; + +/** + * Migration contribution for sequence diagram part of representations file. + * + * @author mporhel + */ +public class SequenceDiagramRepresentationsFileMigrationParticipant extends AbstractRepresentationsFileMigrationParticipant { + /** + * The latest VP version for this participant. + */ + private static final Version MIGRATION_VERSION = new Version("6.5.3.201301221200"); + + private Predicate<Node> isNode = new IsNode(); + + private Predicate<Node> isCollapsedNode = new IsCollapsedNode(); + + private Predicate<Node> isDirectlyCollapsedNode = new IsDirectlyCollapsedNode(); + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.business.api.migration.IMigrationParticipant#getMigrationVersion() + */ + public Version getMigrationVersion() { + return MIGRATION_VERSION; + } + + /** + * {@inheritDoc} + */ + public void postLoad(DAnalysis dAnalysis, Version loadedVersion) { + super.postLoad(dAnalysis, loadedVersion); + + List<Diagram> sequenceDiagrams = getGMFSequenceDiagrams(dAnalysis); + if (!sequenceDiagrams.isEmpty()) { + migrateGMFBoundsOfCollapsedBorderedNode(sequenceDiagrams); + } + } + + /** + * Retrieve all GMF diagrams of the {@link DAnalysis}. + * + * @param dAnalysis + * The analysis of the resource to migrate + * @return Sequence GMF diagrams of this {@link DAnalysis} + */ + protected List<Diagram> getGMFSequenceDiagrams(DAnalysis dAnalysis) { + List<Diagram> diagrams = new ArrayList<Diagram>(); + + for (DView view : dAnalysis.getOwnedViews()) { + for (SequenceDDiagram diagram : Iterables.filter(view.getOwnedRepresentations(), SequenceDDiagram.class)) { + DDiagramGraphicalQuery query = new DDiagramGraphicalQuery((DDiagram) diagram); + Option<Diagram> option = query.getAssociatedGMFDiagram(); + if (option.some()) { + diagrams.add(option.get()); + } + } + } + + return diagrams; + } + + private void migrateGMFBoundsOfCollapsedBorderedNode(List<Diagram> sequenceDiagrams) { + for (Diagram diagram : sequenceDiagrams) { + // 1-Add new IndirectlyCollapseFilter + migrateChildrenOfCollapsedNode(diagram); + // 2-Set width and height of graphical filters of collapsed nodes + // (directly or not) with GMF size and set GMF bounds. + Iterator<Node> viewIterator = Iterators.filter(Iterators.filter(diagram.eAllContents(), Node.class), Predicates.and(isNode, isCollapsedNode)); + while (viewIterator.hasNext()) { + Node node = viewIterator.next(); + DNode dNode = (DNode) node.getElement(); + + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Bounds) { + Bounds bounds = (Bounds) layoutConstraint; + // The GMF node size must be stored in collapse filter (to + // can + // set this size when this node is expanded). + for (GraphicalFilter graphicalFilter : dNode.getGraphicalFilters()) { + if (graphicalFilter instanceof CollapseFilter) { + ((CollapseFilter) graphicalFilter).setWidth(bounds.getWidth()); + ((CollapseFilter) graphicalFilter).setHeight(bounds.getHeight()); + } + } + // Set new collapsed GMF bounds + SequenceCollapseUpdater scbu = new SequenceCollapseUpdater(); + scbu.collapseBounds(node, dNode); + } + } + } + } + + /** + * Add a {@link IndirectlyCollapsedFilter} to the children of CollapsedNode + * (to retrieve the same behavior as before). The migration of GMF bounds of + * this indirectly collapsed nodes, if they are bordered nodes, are deal + * later in method {{@link #migrateGMFBoundsOfBorderedNodes(List)}. + * + * @param diagram + * GMF Diagram to migrate. + */ + private void migrateChildrenOfCollapsedNode(Diagram diagram) { + List<DDiagramElement> indirectlyCollaspedDDEs = Lists.newArrayList(); + Iterator<Node> viewIterator = Iterators.filter(Iterators.filter(diagram.eAllContents(), Node.class), isDirectlyCollapsedNode); + while (viewIterator.hasNext()) { + final Node node = viewIterator.next(); + if (node.getElement() instanceof AbstractDNode) { + AbstractDNode abstractDNode = (AbstractDNode) node.getElement(); + indirectlyCollaspedDDEs.addAll(abstractDNode.getOwnedBorderedNodes()); + if (abstractDNode instanceof DNodeContainer) { + DNodeContainer dDiagramElementContainer = (DNodeContainer) abstractDNode; + indirectlyCollaspedDDEs.addAll(dDiagramElementContainer.getOwnedDiagramElements()); + } else if (abstractDNode instanceof DNodeList) { + DNodeList dNodeList = (DNodeList) abstractDNode; + indirectlyCollaspedDDEs.addAll(dNodeList.getOwnedElements()); + } + } + } + for (DDiagramElement indirectlyCollaspedDDE : indirectlyCollaspedDDEs) { + IndirectlyCollapseFilter indirectlyCollapseFilter = SiriusFactory.eINSTANCE.createIndirectlyCollapseFilter(); + indirectlyCollaspedDDE.getGraphicalFilters().add(indirectlyCollapseFilter); + } + } + + /** + * Predicate that checks that : + * <UL> + * <LI>The input is a GMF Node,</LI> + * <LI>and this Node is a viewpoint node.</LI> + * </UL> + */ + private static class IsNode implements Predicate<Node> { + public boolean apply(Node input) { + int type = SiriusVisualIDRegistry.getVisualID(input.getType()); + return type == DNodeEditPart.VISUAL_ID || type == DNode2EditPart.VISUAL_ID || type == DNode3EditPart.VISUAL_ID || type == DNode4EditPart.VISUAL_ID; + } + } + + /** + * Predicate that checks that this GMF Node is collapsed (directly or not). + * + * No check is done on the border position of a node because we need to + * handle ObservationPoints. + */ + private static class IsCollapsedNode implements Predicate<Node> { + public boolean apply(Node input) { + return new NodeQuery(input).isCollapsed(); + } + } + + /** + * Predicate that checks that this GMF Node is directly collapsed. + * + * No check is done on the border position of a node because we need to + * handle ObservationPoints. + */ + private static class IsDirectlyCollapsedNode implements Predicate<Node> { + public boolean apply(Node input) { + boolean apply = false; + + int type = SiriusVisualIDRegistry.getVisualID(input.getType()); + boolean result = type == DNode2EditPart.VISUAL_ID || type == DNode4EditPart.VISUAL_ID; + result = result || type == DNodeEditPart.VISUAL_ID || type == DNode3EditPart.VISUAL_ID; + result = result || type == DNodeContainerEditPart.VISUAL_ID || type == DNodeContainer2EditPart.VISUAL_ID; + result = result || type == DNodeListEditPart.VISUAL_ID || type == DNodeList2EditPart.VISUAL_ID; + + if (result) { + return new NodeQuery(input).isDirectlyCollapsed(); + } + return apply; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/refresh/VisibilityEventHandler.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/refresh/VisibilityEventHandler.java new file mode 100644 index 0000000000..bb4cd1b89c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/business/internal/refresh/VisibilityEventHandler.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.business.internal.refresh; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.transaction.ResourceSetChangeEvent; +import org.eclipse.emf.transaction.ResourceSetListenerImpl; +import org.eclipse.emf.transaction.RollbackException; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.ui.SequenceDiagramPlugin; +import org.eclipse.sirius.diagram.sequence.ui.business.api.diagramtype.SequenceDiagramTypeProvider; +import org.eclipse.sirius.diagram.sequence.util.NotificationQuery; + +/** + * A listener which refreshes and synchronizes the global ordering of elements + * after a command has been executed. + * + * @author pcdavid + */ +public class VisibilityEventHandler extends ResourceSetListenerImpl { + @Override + public boolean isPrecommitOnly() { + return true; + } + + @Override + public boolean isPostcommitOnly() { + return false; + } + + @Override + public boolean isAggregatePrecommitListener() { + return true; + } + + /** + * Aborts any transaction which change the visibility of elements, as this + * is not supported in sequence diagrams. + * <p> + * {@inheritDoc} + */ + @Override + public Command transactionAboutToCommit(ResourceSetChangeEvent event) throws RollbackException { + if (containsVisibilityEvent(event)) { + throw new RollbackException(new Status(IStatus.ERROR, SequenceDiagramPlugin.PLUGIN_ID, "Hide/Reveal is not supported in Sequence Diagrams")); + } + return null; + } + + private boolean containsVisibilityEvent(ResourceSetChangeEvent event) { + Predicate<Notification> isVisibilityEvent = new Predicate<Notification>() { + public boolean apply(Notification input) { + NotificationQuery nq = new NotificationQuery(input); + return nq.isViewBecomingInvisibleEvent() || nq.isHideFilterAddEvent(); + } + }; + + Predicate<Notification> isAlwaysVisibleSequenceElement = new Predicate<Notification>() { + public boolean apply(Notification input) { + Object notifier = input.getNotifier(); + if (notifier instanceof DDiagramElement) { + DDiagramElement dde = (DDiagramElement) notifier; + DDiagram parentDiagram = dde.getParentDiagram(); + return parentDiagram instanceof SequenceDDiagram && !(new SequenceDiagramTypeProvider().allowsHideReveal().apply(dde)); + } + return false; + } + }; + return Iterables.any(Iterables.filter(event.getNotifications(), Notification.class), Predicates.and(isAlwaysVisibleSequenceElement, isVisibilityEvent)); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/api/SequenceDiagramLayout.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/api/SequenceDiagramLayout.java new file mode 100644 index 0000000000..6ea13852bd --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/api/SequenceDiagramLayout.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.api; + +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.diagram.sequence.business.internal.layout.SequenceLayout; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceDiagramEditPart; + +/** + * API class to allow clients to control some aspects of the layout of sequence + * diagrams graphically. + * + * @author pcdavid + */ +public class SequenceDiagramLayout { + private final DiagramEditPart sequenceDiagram; + + /** + * Constructor. + * + * @param sequenceDiagram + * the sequence diagram to manage. + */ + public SequenceDiagramLayout(DiagramEditPart sequenceDiagram) { + this.sequenceDiagram = Preconditions.checkNotNull(sequenceDiagram); + } + + /** + * Updates the layout of the diagram so that the graphical coverage of + * Interaction Uses and Combined Fragments match the corresponding semantic + * coverage. This method should be called in the context of a transaction + * when the semantic model has changed in a way which may modify the + * semantic coverage of the elements on the diagram. + */ + public void refreshGraphicalCoverage() { + if (sequenceDiagram instanceof SequenceDiagramEditPart) { + new SequenceLayout(sequenceDiagram.getDiagramView()).horizontalLayout(false); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/command/SequenceDelegatingCommandFactory.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/command/SequenceDelegatingCommandFactory.java new file mode 100644 index 0000000000..0a338b2659 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/command/SequenceDelegatingCommandFactory.java @@ -0,0 +1,384 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.command; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.description.tool.ContainerCreationDescription; +import org.eclipse.sirius.description.tool.NodeCreationDescription; +import org.eclipse.sirius.description.tool.PaneBasedSelectionWizardDescription; +import org.eclipse.sirius.description.tool.SelectionWizardDescription; +import org.eclipse.sirius.description.tool.ToolDescription; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.RefreshSemanticOrderingsOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.tool.ToolCommandBuilder; +import org.eclipse.sirius.diagram.sequence.description.tool.CombinedFragmentCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ExecutionCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InstanceRoleCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InteractionUseCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ObservationPointCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.OperandCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.StateCreationTool; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.DelegatingDiagramCommandFactory; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactory; + +/** + * A specialized diagram command factory which knows how to handle the + * additional variables passed to sequence tools. + * + * + * @author mporhel + */ +public final class SequenceDelegatingCommandFactory extends DelegatingDiagramCommandFactory { + /** The editing domain. */ + private final TransactionalEditingDomain domain; + + private final SequenceDiagram seqDiag; + + private EObject predecessor; + + private EventEnd startingEndPredecessor; + + private EventEnd finishingEndPredecessor; + + private EventEnd endBefore; + + private List<EObject> coverage; + + private Point location; + + /** + * Constructor. + * + * @param baseFactory + * the base command factory. + * @param domain + * the current editing domain. + * @param seqDiag + * the current sequence diagram. + */ + private SequenceDelegatingCommandFactory(IDiagramCommandFactory baseFactory, TransactionalEditingDomain domain, SequenceDiagram seqDiag) { + super(baseFactory); + this.domain = domain; + this.seqDiag = seqDiag; + } + + /** + * Constructor. + * + * @param baseFactory + * the base command factory. + * @param domain + * the current editing domain. + * @param seqDiag + * the current sequence diagram. + * @param startPredecessor + * the first click end predecessor. + * @param finishPredecessor + * the second click end predecessor. + */ + public SequenceDelegatingCommandFactory(IDiagramCommandFactory baseFactory, TransactionalEditingDomain domain, SequenceDiagram seqDiag, EventEnd startPredecessor, EventEnd finishPredecessor) { + this(baseFactory, domain, seqDiag); + this.startingEndPredecessor = startPredecessor; + this.finishingEndPredecessor = finishPredecessor; + } + + /** + * Constructor. + * + * @param baseFactory + * the base command factory. + * @param domain + * the current editing domain. + * @param seqDiag + * the current sequence diagram. + * @param startingEndPredecessor + * the first click end predecessor. + * @param finishingEndPredecessor + * the second click end predecessor. + * @param coverage + * the semantic coverage. + */ + public SequenceDelegatingCommandFactory(IDiagramCommandFactory baseFactory, TransactionalEditingDomain domain, SequenceDiagram seqDiag, EventEnd startingEndPredecessor, + EventEnd finishingEndPredecessor, List<EObject> coverage) { + this(baseFactory, domain, seqDiag, startingEndPredecessor, finishingEndPredecessor); + this.coverage = coverage; + + } + + /** + * Constructor. + * + * @param baseFactory + * the base command factory. + * @param domain + * the current editing domain. + * @param seqDiag + * the current sequence diagram. + * @param startingEndPredecessor + * the first click end predecessor. + * @param finishingEndPredecessor + * the second click end predecessor. + * @param location + * the clic location. + */ + public SequenceDelegatingCommandFactory(IDiagramCommandFactory baseFactory, TransactionalEditingDomain domain, SequenceDiagram seqDiag, EventEnd startingEndPredecessor, + EventEnd finishingEndPredecessor, Point location) { + this(baseFactory, domain, seqDiag, startingEndPredecessor, finishingEndPredecessor); + this.location = location; + } + + /** + * Constructor. + * + * @param baseFactory + * the base command factory. + * @param domain + * the current editing domain. + * @param seqDiag + * the current sequence diagram. + * @param endBefore + * the end before the click. + * @param location + * the clic location. + * + */ + public SequenceDelegatingCommandFactory(IDiagramCommandFactory baseFactory, TransactionalEditingDomain domain, SequenceDiagram seqDiag, EventEnd endBefore, Point location) { + this(baseFactory, domain, seqDiag); + this.endBefore = endBefore; + this.location = location; + } + + /** + * Constructor. + * + * @param baseFactory + * the base command factory. + * @param domain + * the current editing domain. + * @param seqDiag + * the current sequence diagram. + * @param predecessor + * the semantic EObject before the click before the click. + * @param location + * the clic location. + * + */ + public SequenceDelegatingCommandFactory(IDiagramCommandFactory baseFactory, TransactionalEditingDomain domain, SequenceDiagram seqDiag, EObject predecessor, Point location) { + this(baseFactory, domain, seqDiag); + this.predecessor = predecessor; + this.location = location; + } + + /** + * {@inheritDoc} + */ + @Override + public org.eclipse.emf.common.command.Command buildCreateContainerCommandFromTool(DDiagram diagram, ContainerCreationDescription tool) { + final org.eclipse.emf.common.command.Command result; + if (ToolCommandBuilder.isStartingEventEndOfCombinedFragment(seqDiag, startingEndPredecessor)) { + result = org.eclipse.emf.common.command.UnexecutableCommand.INSTANCE; + } else if (tool instanceof InteractionUseCreationTool) { + result = buildInteractionUseCreationFromTool(diagram, (InteractionUseCreationTool) tool); + } else if (tool instanceof CombinedFragmentCreationTool) { + result = buildCombinedFragmentCreationFromTool(diagram, (CombinedFragmentCreationTool) tool); + } else { + result = super.buildCreateContainerCommandFromTool(diagram, tool); + } + return result; + } + + private org.eclipse.emf.common.command.Command buildInteractionUseCreationFromTool(DDiagram diagram, InteractionUseCreationTool tool) { + org.eclipse.emf.common.command.Command emfCommand = ToolCommandBuilder.buildCreateInteractionUseCommandFromTool(diagram, tool, startingEndPredecessor, finishingEndPredecessor, coverage); + emfCommand = emfCommand.chain(CommandFactory.createRecordingCommand(domain, new RefreshSemanticOrderingsOperation((SequenceDDiagram) diagram))); + return emfCommand; + } + + private org.eclipse.emf.common.command.Command buildCombinedFragmentCreationFromTool(DDiagram diagram, CombinedFragmentCreationTool tool) { + org.eclipse.emf.common.command.Command emfCommand = ToolCommandBuilder.buildCreateCombinedFragmentCommandFromTool(diagram, tool, startingEndPredecessor, finishingEndPredecessor, coverage); + emfCommand = emfCommand.chain(CommandFactory.createRecordingCommand(domain, new RefreshSemanticOrderingsOperation((SequenceDDiagram) diagram))); + return emfCommand; + } + + /** + * {@inheritDoc} + */ + @Override + public Command buildGenericToolCommandFromTool(EObject containerView, ToolDescription tool) { + final org.eclipse.emf.common.command.Command result; + if (ToolCommandBuilder.isStartingEventEndOfCombinedFragment(seqDiag, endBefore)) { + result = org.eclipse.emf.common.command.UnexecutableCommand.INSTANCE; + } else { + result = buildSequenceGenericToolCommandFromTool(containerView, tool); + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public Command buildSelectionWizardCommandFromTool(SelectionWizardDescription tool, DSemanticDecorator dContainer, Collection<EObject> selectedElement) { + final org.eclipse.emf.common.command.Command result; + if (ToolCommandBuilder.isStartingEventEndOfCombinedFragment(seqDiag, endBefore)) { + result = org.eclipse.emf.common.command.UnexecutableCommand.INSTANCE; + } else { + result = buildSequenceSelectionWizardCommandFromTool(tool, dContainer, selectedElement); + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public Command buildPaneBasedSelectionWizardCommandFromTool(PaneBasedSelectionWizardDescription tool, DSemanticDecorator dContainer, Collection<EObject> selectedElement) { + final org.eclipse.emf.common.command.Command result; + if (ToolCommandBuilder.isStartingEventEndOfCombinedFragment(seqDiag, endBefore)) { + result = org.eclipse.emf.common.command.UnexecutableCommand.INSTANCE; + } else { + result = buildSequencePaneBasedSelectionWizardCommandFromTool(tool, dContainer, selectedElement); + } + return result; + } + + private Command buildSequenceGenericToolCommandFromTool(EObject containerView, ToolDescription tool) { + org.eclipse.emf.common.command.Command emfCommand = ToolCommandBuilder.buildSequenceGenericToolCommandFromTool(containerView, tool, endBefore, location, seqDiag.getNotationDiagram()); + emfCommand = emfCommand.chain(CommandFactory.createRecordingCommand(domain, new RefreshSemanticOrderingsOperation(seqDiag.getSequenceDDiagram()))); + return emfCommand; + } + + private Command buildSequenceSelectionWizardCommandFromTool(SelectionWizardDescription tool, DSemanticDecorator dContainer, Collection<EObject> selectedElement) { + org.eclipse.emf.common.command.Command emfCommand = ToolCommandBuilder.buildSequenceSelectionWizardCommandFromTool(tool, dContainer, selectedElement, endBefore, location); + emfCommand = emfCommand.chain(CommandFactory.createRecordingCommand(domain, new RefreshSemanticOrderingsOperation(seqDiag.getSequenceDDiagram()))); + return emfCommand; + } + + private Command buildSequencePaneBasedSelectionWizardCommandFromTool(PaneBasedSelectionWizardDescription tool, DSemanticDecorator dContainer, Collection<EObject> selectedElement) { + org.eclipse.emf.common.command.Command emfCommand = ToolCommandBuilder.buildSequencePaneBasedSelectionWizardCommandFromTool(tool, dContainer, selectedElement, endBefore, location); + emfCommand = emfCommand.chain(CommandFactory.createRecordingCommand(domain, new RefreshSemanticOrderingsOperation(seqDiag.getSequenceDDiagram()))); + return emfCommand; + } + + @Override + public Command buildCreateNodeCommandFromTool(DDiagramElementContainer container, NodeCreationDescription tool) { + final org.eclipse.emf.common.command.Command result; + if (ToolCommandBuilder.isStartingEventEndOfCombinedFragment(seqDiag, startingEndPredecessor)) { + result = org.eclipse.emf.common.command.UnexecutableCommand.INSTANCE; + } else if (tool instanceof ObservationPointCreationTool) { + result = buildObservationPointCreationCommandFromTool(container, (ObservationPointCreationTool) tool); + } else { + result = super.buildCreateNodeCommandFromTool(container, tool); + } + return result; + } + + @Override + public org.eclipse.emf.common.command.Command buildCreateNodeCommandFromTool(DNode node, NodeCreationDescription tool) { + final org.eclipse.emf.common.command.Command result; + if (ToolCommandBuilder.isStartingEventEndOfCombinedFragment(seqDiag, startingEndPredecessor)) { + result = org.eclipse.emf.common.command.UnexecutableCommand.INSTANCE; + } else if (tool instanceof InstanceRoleCreationTool) { + result = buildInstanceRoleCreationCommandFromTool(seqDiag.getSequenceDDiagram(), (InstanceRoleCreationTool) tool); + } else if (tool instanceof ExecutionCreationTool) { + result = buildExecutionCreationCommandFromTool(node, (ExecutionCreationTool) tool); + } else if (tool instanceof StateCreationTool) { + result = buildStateCreationCommandFromTool(node, (StateCreationTool) tool); + } else if (tool instanceof ObservationPointCreationTool) { + result = buildObservationPointCreationCommandFromTool(node, (ObservationPointCreationTool) tool); + } else { + result = super.buildCreateNodeCommandFromTool(node, tool); + } + return result; + } + + @Override + public org.eclipse.emf.common.command.Command buildCreateNodeCommandFromTool(DDiagram diagram, NodeCreationDescription tool) { + final org.eclipse.emf.common.command.Command result; + if (ToolCommandBuilder.isStartingEventEndOfCombinedFragment(seqDiag, startingEndPredecessor) || diagram != seqDiag.getSequenceDDiagram()) { + result = org.eclipse.emf.common.command.UnexecutableCommand.INSTANCE; + } else if (tool instanceof InstanceRoleCreationTool) { + result = buildInstanceRoleCreationCommandFromTool(seqDiag.getSequenceDDiagram(), (InstanceRoleCreationTool) tool); + } else if (tool instanceof ObservationPointCreationTool) { + result = buildObservationPointCreationCommandFromTool(seqDiag.getSequenceDDiagram(), (ObservationPointCreationTool) tool); + } else { + result = super.buildCreateNodeCommandFromTool(diagram, tool); + } + return result; + } + + @Override + public org.eclipse.emf.common.command.Command buildCreateContainerCommandFromTool(DDiagramElementContainer nodeContainer, ContainerCreationDescription tool) { + final org.eclipse.emf.common.command.Command result; + if (ToolCommandBuilder.isStartingEventEndOfCombinedFragment(seqDiag, startingEndPredecessor)) { + result = org.eclipse.emf.common.command.UnexecutableCommand.INSTANCE; + } else if (tool instanceof OperandCreationTool) { + result = buildOperandCreationCommandFromTool(nodeContainer, (OperandCreationTool) tool); + } else { + result = super.buildCreateContainerCommandFromTool(nodeContainer, tool); + } + return result; + } + + private Command buildInstanceRoleCreationCommandFromTool(SequenceDDiagram diagram, InstanceRoleCreationTool tool) { + org.eclipse.emf.common.command.Command emfCommand = ToolCommandBuilder.buildCreateInstanceRoleCommandFromTool(diagram, tool, predecessor, location); + emfCommand = emfCommand.chain(CommandFactory.createRecordingCommand(domain, new RefreshSemanticOrderingsOperation(diagram))); + return emfCommand; + } + + private org.eclipse.emf.common.command.Command buildExecutionCreationCommandFromTool(DNode node, ExecutionCreationTool tool) { + org.eclipse.emf.common.command.Command emfCommand = ToolCommandBuilder.buildCreateExecutionCommandFromTool(node, tool, startingEndPredecessor, finishingEndPredecessor, location); + SequenceDDiagram diagram = (SequenceDDiagram) node.getParentDiagram(); + emfCommand = emfCommand.chain(CommandFactory.createRecordingCommand(domain, new RefreshSemanticOrderingsOperation(diagram))); + return emfCommand; + } + + private org.eclipse.emf.common.command.Command buildObservationPointCreationCommandFromTool(DDiagramElement diagramElement, ObservationPointCreationTool tool) { + org.eclipse.emf.common.command.Command emfCommand = ToolCommandBuilder.buildCreateObservationPointCommandFromTool(diagramElement, tool, startingEndPredecessor, finishingEndPredecessor); + SequenceDDiagram diagram = (SequenceDDiagram) diagramElement.getParentDiagram(); + emfCommand = emfCommand.chain(CommandFactory.createRecordingCommand(domain, new RefreshSemanticOrderingsOperation(diagram))); + return emfCommand; + } + + private org.eclipse.emf.common.command.Command buildObservationPointCreationCommandFromTool(SequenceDDiagram diag, ObservationPointCreationTool tool) { + org.eclipse.emf.common.command.Command emfCommand = ToolCommandBuilder.buildCreateObservationPointCommandFromTool(diag, tool, startingEndPredecessor, finishingEndPredecessor); + emfCommand = emfCommand.chain(CommandFactory.createRecordingCommand(domain, new RefreshSemanticOrderingsOperation(diag))); + return emfCommand; + } + + private org.eclipse.emf.common.command.Command buildStateCreationCommandFromTool(DNode node, StateCreationTool tool) { + org.eclipse.emf.common.command.Command emfCommand = ToolCommandBuilder.buildCreateStateCommandFromTool(node, tool, startingEndPredecessor, finishingEndPredecessor); + SequenceDDiagram diagram = (SequenceDDiagram) node.getParentDiagram(); + emfCommand = emfCommand.chain(CommandFactory.createRecordingCommand(domain, new RefreshSemanticOrderingsOperation(diagram))); + return emfCommand; + } + + private org.eclipse.emf.common.command.Command buildOperandCreationCommandFromTool(DDiagramElementContainer nodeContainer, OperandCreationTool tool) { + org.eclipse.emf.common.command.Command emfCommand = ToolCommandBuilder.buildCreateOperantCommandFromTool(nodeContainer, tool, startingEndPredecessor, finishingEndPredecessor); + SequenceDDiagram diagram = (SequenceDDiagram) nodeContainer.getParentDiagram(); + emfCommand = emfCommand.chain(CommandFactory.createRecordingCommand(domain, new RefreshSemanticOrderingsOperation(diagram))); + return emfCommand; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/command/SequenceEMFCommandFactory.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/command/SequenceEMFCommandFactory.java new file mode 100644 index 0000000000..130df4ae96 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/command/SequenceEMFCommandFactory.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.command; + +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.command.CompoundCommand; +import org.eclipse.emf.common.command.IdentityCommand; +import org.eclipse.emf.common.command.UnexecutableCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.FixGraphicalOrderingOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.RefreshGraphicalOrderingOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.RefreshSemanticOrderingsOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SetVerticalRangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SynchronizeISequenceEventsSemanticOrderingOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ExecutionOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ExecutionEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.OperandEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceDiagramEditPart; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; +import org.eclipse.sirius.tools.internal.command.UndoRedoCapableEMFCommandFactory; + +/** + * A custom EMF Command Factory to specialize the delete behavior for sequence + * diagram elements. + * + * @author pcdavid + */ +public final class SequenceEMFCommandFactory extends UndoRedoCapableEMFCommandFactory { + + private final SequenceDiagramEditPart sdep; + + /** + * Constructor. + * + * The factory will be initialized with the TransactionalEditingDomain of + * the current SequenceDiagramEditPart. + * + * @param sdep + * the sequence diagram. + */ + public SequenceEMFCommandFactory(SequenceDiagramEditPart sdep) { + super(sdep.getEditingDomain()); + this.sdep = sdep; + } + + /** + * Overridden to reconnect the children of an execution about to be deleted. + * <p> + * {@inheritDoc} + */ + @Override + public Command buildDeleteDiagramElement(DDiagramElement element) { + Command result = super.buildDeleteDiagramElement(element); + if (AbstractNodeEvent.viewpointElementPredicate().apply(element)) { + Object part = sdep.getViewer().getEditPartRegistry().get(element); + if (part instanceof ExecutionEditPart) { + ExecutionEditPart executionPart = (ExecutionEditPart) part; + result = getDeleteExecutionCommand(executionPart, result); + } + } else if (Operand.viewpointElementPredicate().apply(element)) { + Object part = sdep.getViewer().getEditPartRegistry().get(element); + if (part instanceof OperandEditPart) { + OperandEditPart operandPart = (OperandEditPart) part; + result = getDeleteOperandCommand(operandPart, result); + } + } + return result; + } + + private Command getDeleteExecutionCommand(ExecutionEditPart executionPart, Command basicDelete) { + TransactionalEditingDomain ted = sdep.getEditingDomain(); + + CompoundCommand cc = new CompoundCommand(); + cc.append(ExecutionOperations.getReconnectSubExecutionsToParentCommand(executionPart)); + cc.append(ExecutionOperations.getReconnectEdgesToParentCommand(executionPart)); + cc.append(basicDelete); + cc.append(CommandFactory.createRecordingCommand(ted, new RefreshSemanticOrderingsOperation((SequenceDDiagram) sdep.resolveSemanticElement()))); + cc.append(CommandFactory.createRecordingCommand(ted, new FixGraphicalOrderingOperation((SequenceDDiagram) sdep.resolveSemanticElement()))); + return cc; + } + + private Command getDeleteOperandCommand(OperandEditPart operandPart, Command basicDelete) { + if (operandPart.getViewer().getSelectedEditParts().contains(operandPart.getParentCombinedFragmentEditPart())) { + // The operand container is also beeing deleted. Therefore we do not + // need to add delete operation on operand. + return IdentityCommand.INSTANCE; + } else { + return buildDeleteOperandCommand(operandPart, basicDelete); + } + } + + private Command buildDeleteOperandCommand(OperandEditPart operandPart, Command basicDelete) { + TransactionalEditingDomain ted = sdep.getEditingDomain(); + CompoundCommand cc = new CompoundCommand(); + cc.append(basicDelete); + + Operand deletedOperand = (Operand) operandPart.getISequenceEvent(); + if (deletedOperand.getCombinedFragment().getOperands().size() == 1 || operandPart.getViewer().getSelectedEditParts().size() != 1) { + /* + * The last remaining operand can not be deleted. It is also not + * possible to delete 2 operand to avoid NPE in + * SetVerticalRangeOperation. + */ + return UnexecutableCommand.INSTANCE; + } + Option<Operand> absorbingOperand = getAbsorbingOperand(deletedOperand); + assert absorbingOperand.some(); + if (absorbingOperand.some()) { + Range expandedRange = absorbingOperand.get().getVerticalRange().union(deletedOperand.getVerticalRange()); + cc.append(CommandFactory.createRecordingCommand(ted, new SetVerticalRangeOperation(absorbingOperand.get(), expandedRange))); + cc.append(CommandFactory.createRecordingCommand(ted, new RefreshSemanticOrderingsOperation((SequenceDDiagram) sdep.resolveSemanticElement()))); + cc.append(CommandFactory.createRecordingCommand(ted, new RefreshGraphicalOrderingOperation(sdep.getSequenceDiagram()))); + cc.append(CommandFactory.createRecordingCommand(ted, new SynchronizeISequenceEventsSemanticOrderingOperation(absorbingOperand.get()))); + } + + return cc; + } + + /** + * Returns the sibling operand which should absorb the space previously + * occupied by the operand about to be deleted. + */ + private Option<Operand> getAbsorbingOperand(Operand deletedOperand) { + assert deletedOperand != null; + if (deletedOperand.isFirstOperand()) { + return deletedOperand.getFollowingOperand(); + } else { + return deletedOperand.getPreviousOperand(); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ConnectionAnchorOperation.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ConnectionAnchorOperation.java new file mode 100644 index 0000000000..78148e8800 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ConnectionAnchorOperation.java @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation; + +import org.eclipse.draw2d.ConnectionAnchor; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PrecisionPoint; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.Request; +import org.eclipse.gef.requests.CreateConnectionRequest; +import org.eclipse.gef.requests.DropRequest; +import org.eclipse.gef.requests.ReconnectRequest; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.internal.editparts.NoteAttachmentEditPart; +import org.eclipse.gmf.runtime.draw2d.ui.figures.BaseSlidableAnchor; +import org.eclipse.gmf.runtime.gef.ui.figures.SlidableAnchor; + +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.diagram.internal.view.factories.ViewLocationHint; +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.SequenceSlidableAnchor; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.EditPartsHelper; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; + +/** + * Helper class to business code between different kinds of edit parts when + * inheritance is not possible or inconvenient. + * + * @author mporhel + */ +public final class ConnectionAnchorOperation { + private ConnectionAnchorOperation() { + // Prevent instantiation. + } + + /** + * If possible, returns an equivalent anchor to the one given, but + * horizontally centered on its owner. + * + * @param anchor + * the anchor. + * @return an equivalent anchor horizontally centered on its owner. + */ + public static ConnectionAnchor getHorizontallyCenteredAnchor(ConnectionAnchor anchor) { + return getCorrectedAnchor(anchor, null, 0.5); + } + + /** + * If possible, returns an equivalent anchor to the one given, but + * horizontally centered on its owner north face. + * + * @param anchor + * the anchor. + * @return an equivalent anchor horizontally centered on its owner north + * face. + */ + public static ConnectionAnchor getHorizontallyCenteredAndTopAnchor(ConnectionAnchor anchor) { + return getCorrectedAnchor(anchor, "(0.5,0)", 0.5); + } + + /** + * If possible, returns an equivalent anchor to the one given, but + * horizontally centered on its owner south face. + * + * @param anchor + * the anchor. + * @return an equivalent anchor horizontally centered on its owner north + * face. + */ + public static ConnectionAnchor getHorizontallyCenteredAndBottomAnchor(ConnectionAnchor anchor) { + return getCorrectedAnchor(anchor, "(0.5,1)", 0.5); + } + + private static ConnectionAnchor getCorrectedAnchor(ConnectionAnchor anchor, String defaultId, double preciseX) { + ConnectionAnchor result = anchor; + if (anchor instanceof SlidableAnchor) { + String id = ((SlidableAnchor) anchor).getTerminal(); + if (StringUtil.isEmpty(id) && !StringUtil.isEmpty(defaultId)) { + id = defaultId; + } + if (!StringUtil.isEmpty(id)) { + PrecisionPoint pp = BaseSlidableAnchor.parseTerminalString(id); + if (pp != null) { + pp.preciseX = preciseX; + result = new SequenceSlidableAnchor(anchor, pp); + } + } + } + return result; + } + + /** + * Adjusts the Y location of a request to match the Y location of the source + * anchor as stored by ViewLocationHint. This can be used before a call to + * {@link #getTargetConnectionAnchor(org.eclipse.gef.ConnectionEditPart)} to + * make sure a connection is horizontal. + * + * @param request + * the request to modify. + */ + public static void matchRequestYLocationWithSourceAnchor(DropRequest request) { + if (request instanceof Request && !new RequestQuery((Request) request).isSequenceMessageCreation()) { + return; + } else if (request.getLocation() != null) { + Object sourceLocation = ViewLocationHint.getInstance().getData(ViewLocationHint.SOURCE_ANCHOR_LOCATION); + if (sourceLocation instanceof Point && request instanceof CreateConnectionRequest) { + EditPart sourceEP = ((CreateConnectionRequest) request).getSourceEditPart(); + EditPart targetEP = ((CreateConnectionRequest) request).getTargetEditPart(); + Point sourceLocationPoint = ((Point) sourceLocation).getCopy(); + if (!sourceEP.equals(targetEP)) { + request.getLocation().y = sourceLocationPoint.y; + } else if (request.getLocation().y <= sourceLocationPoint.y) { + request.getLocation().y = sourceLocationPoint.y; + } + } + } + } + + /** + * Return an horizontally centered anchor. + * + * @param self + * . + * @param request + * . + * @param superSourceAnchor + * . + * @return . + */ + public static ConnectionAnchor getSourceConnectionAnchor(ISequenceEventEditPart self, Request request, ConnectionAnchor superSourceAnchor) { + ConnectionAnchor result = ConnectionAnchorOperation.getHorizontallyCenteredAnchor(superSourceAnchor); + + if (request instanceof CreateConnectionRequest) { + CreateConnectionRequest ccr = (CreateConnectionRequest) request; + if (ccr.getLocation() == null) { + result = ConnectionAnchorOperation.getHorizontallyCenteredAndBottomAnchor(superSourceAnchor); + } + } + return result; + } + + /** + * During message creation, use the same y location as the corresponding + * source connection anchor, stored in ViewLocationHint, to improve user + * feedback. + * + * @param self + * . + * @param request + * . + * @param superTargetAnchor + * . + * @return . + */ + public static ConnectionAnchor getTargetConnectionAnchor(ISequenceEventEditPart self, Request request, ConnectionAnchor superTargetAnchor) { + ConnectionAnchor result = ConnectionAnchorOperation.getHorizontallyCenteredAnchor(superTargetAnchor); + if (request instanceof CreateConnectionRequest) { + CreateConnectionRequest ccr = (CreateConnectionRequest) request; + if (ConnectionAnchorOperation.isCreateMessageToSelfRequest(ccr)) { + result = ConnectionAnchorOperation.getHorizontallyCenteredAndTopAnchor(superTargetAnchor); + } else { + result = ConnectionAnchorOperation.getHorizontallyCenteredAnchor(superTargetAnchor); + } + } else if (request instanceof ReconnectRequest && !(((ReconnectRequest) request).getConnectionEditPart() instanceof NoteAttachmentEditPart)) { + ReconnectRequest conn = (ReconnectRequest) request; + ConnectionEditPart cep = (ConnectionEditPart) conn.getConnectionEditPart(); + result = cep.getConnectionFigure().getTargetAnchor(); + } else { + result = superTargetAnchor; + } + return result; + } + + private static boolean isCreateMessageToSelfRequest(CreateConnectionRequest ccr) { + if ((ccr.getSourceEditPart() instanceof ExecutionEditPart || ccr.getSourceEditPart() instanceof LifelineEditPart) + && (ccr.getTargetEditPart() instanceof ExecutionEditPart || ccr.getTargetEditPart() instanceof LifelineEditPart)) { + LifelineEditPart sourceParentLifeline = EditPartsHelper.findParentLifeline((IGraphicalEditPart) ccr.getSourceEditPart()); + LifelineEditPart targetParentLifeline = EditPartsHelper.findParentLifeline((IGraphicalEditPart) ccr.getTargetEditPart()); + return sourceParentLifeline.equals(targetParentLifeline); + } + return false; + } + + /** + * Get an horizontally and vertically centered anchor. + * + * @param anchor + * an anchor. + * @return a centered anchor. + */ + public static ConnectionAnchor safeCenterAnchor(ConnectionAnchor anchor) { + ConnectionAnchor result = anchor; + if (result != null) { + result = new SequenceSlidableAnchor(anchor, new PrecisionPoint(0.5, 0.5)); + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/EndOfLifeOperations.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/EndOfLifeOperations.java new file mode 100644 index 0000000000..d6a479bb0f --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/EndOfLifeOperations.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation; + +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.Request; +import org.eclipse.gef.requests.BendpointRequest; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.EndOfLifeEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.LifelineEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceMessageEditPart; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; + +/** + * Helper class to factor common code for EndOfLife management. + * + * @author mporhel + */ +public final class EndOfLifeOperations { + private EndOfLifeOperations() { + // Prevent instantiations. + } + + /** + * Shows or updates the lifeline source feedback for the given request. + * + * @param request + * request describing the type of feedback. + * @param endOfLifeEditPart + * current {@link EndOfLifeEditPart}. + * @param moveSource + * editpart source fo the current move. + */ + public static void showEndOfLifeFeedback(Request request, EndOfLifeEditPart endOfLifeEditPart, IGraphicalEditPart moveSource) { + LifelineEditPart lifelineEditPart = (LifelineEditPart) endOfLifeEditPart.getParent(); + Point location = EndOfLifeOperations.getLocation(request); + + if (location == null) { + return; + } + + lifelineEditPart.showSourceFeedback(EndOfLifeOperations.getLifelineResizeRequest(endOfLifeEditPart, lifelineEditPart, location, moveSource)); + } + + /** + * Erases for the specified {@link Request}. + * {@link #showSourceFeedback(Request)}. + * + * @param request + * the type of feedback that is being erased + * @param lep + * the current {@link LifelineEditPart}. + */ + public static void eraseEndOfLifeFeedback(LifelineEditPart lep, Request request) { + Point location = null; + location = EndOfLifeOperations.getLocation(request); + + if (location == null) { + return; + } + + ChangeBoundsRequest cbr = new ChangeBoundsRequest(org.eclipse.gef.RequestConstants.REQ_RESIZE); + cbr.getMoveDelta().y = location.y - (lep.getFigure().getBounds().y + lep.getFigure().getBounds().height); + cbr.setEditParts(lep); + cbr.setLocation(location.getCopy()); + lep.eraseSourceFeedback(cbr); + } + + /** + * Compute a request to resize the lifeline, from a request on its end of + * life. + * + * @param request + * the type of feedback that is wanted. + * @param endOfLifeEditPart + * the current {@link EndOfLifeEditPart}. + * @param moveSource + * editpart source fo the current move. + * @return the computed request. + */ + public static ChangeBoundsRequest getLifelineResizeRequest(Request request, EndOfLifeEditPart endOfLifeEditPart, IGraphicalEditPart moveSource) { + Point location = EndOfLifeOperations.getLocation(request); + if (endOfLifeEditPart.getParent() instanceof LifelineEditPart && location != null) { + LifelineEditPart lifelineEditPart = (LifelineEditPart) endOfLifeEditPart.getParent(); + return EndOfLifeOperations.getLifelineResizeRequest(endOfLifeEditPart, lifelineEditPart, location, moveSource); + } + return null; + } + + private static ChangeBoundsRequest getLifelineResizeRequest(EndOfLifeEditPart endOfLifeEditPart, LifelineEditPart lifelineEditPart, Point location, IGraphicalEditPart moveSource) { + GraphicalHelper.screen2logical(location, lifelineEditPart); + ChangeBoundsRequest cbr = new ChangeBoundsRequest(org.eclipse.gef.RequestConstants.REQ_RESIZE); + cbr.setResizeDirection(PositionConstants.SOUTH); + cbr.getSizeDelta().height = EndOfLifeOperations.computeResizeDelta(endOfLifeEditPart, lifelineEditPart, location.y, moveSource); + cbr.setEditParts(lifelineEditPart); + cbr.setLocation(location.getCopy()); + return cbr; + } + + private static int computeResizeDelta(EndOfLifeEditPart endOfLifeEditPart, LifelineEditPart lifelineEditPart, int location, IGraphicalEditPart moveSource) { + double zoom = GraphicalHelper.getZoom(lifelineEditPart); + int lifelineLocation = lifelineEditPart.getFigure().getBounds().y + lifelineEditPart.getFigure().getBounds().height; + int eolMidSize = endOfLifeEditPart.getFigure().getBounds().height / 2; + int feedbackRangeLimit = EndOfLifeOperations.getFeedBackRangeLimit(location, moveSource, eolMidSize, EndOfLifeOperations.getLastEventPosition(lifelineEditPart)); + return (int) (EndOfLifeOperations.getResizeDelta(lifelineLocation, eolMidSize, feedbackRangeLimit) * zoom); + } + + private static int getFeedBackRangeLimit(int location, IGraphicalEditPart moveSource, int eolMidSize, int lastEventInTargetInstanceRole) { + Rectangle sourceBbounds = moveSource.getFigure().getBounds(); + int feedbackRangeLimit = location; + if (moveSource instanceof SequenceMessageEditPart) { + if (feedbackRangeLimit < sourceBbounds.y) { + feedbackRangeLimit = sourceBbounds.y; + } else if (lastEventInTargetInstanceRole + eolMidSize > location) { + feedbackRangeLimit = lastEventInTargetInstanceRole + eolMidSize; + } else if (location > sourceBbounds.y + sourceBbounds.height) { + feedbackRangeLimit = sourceBbounds.y + sourceBbounds.height; + } + } + return feedbackRangeLimit; + } + + // limit the vertical move to the first sequence event of the targeted + // instance role + private static int getLastEventPosition(LifelineEditPart lifelineEditPart) { + int lastMessageInTargetInstanceRole = Integer.MIN_VALUE; + Range occupiedRange = lifelineEditPart.getISequenceEvent().getOccupiedRange(); + if (!occupiedRange.isEmpty()) { + // limite the move to the first sequence event of the target + // lifeline + lastMessageInTargetInstanceRole = occupiedRange.getUpperBound() + LayoutConstants.EXECUTION_CHILDREN_MARGIN; + } + return lastMessageInTargetInstanceRole; + } + + private static int getResizeDelta(int lifelineLocation, int eolMidSize, int feedbackRangeLimit) { + int delta = feedbackRangeLimit; + delta -= lifelineLocation; + delta -= eolMidSize; + return delta; + } + + private static Point getLocation(Request request) { + Point location = null; + if (request instanceof BendpointRequest) { + BendpointRequest bendpointRequest = (BendpointRequest) request; + if (bendpointRequest.getLocation() != null) { + location = bendpointRequest.getLocation().getCopy(); + } + } else if (request instanceof ChangeBoundsRequest) { + ChangeBoundsRequest changeBoundsRequest = (ChangeBoundsRequest) request; + if (changeBoundsRequest.getLocation() != null) { + location = changeBoundsRequest.getLocation().getCopy(); + } + } + return location; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ExecutionOperations.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ExecutionOperations.java new file mode 100644 index 0000000000..4ef9286d72 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ExecutionOperations.java @@ -0,0 +1,274 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.command.CompoundCommand; +import org.eclipse.emf.common.command.IdentityCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.graphical.edit.policies.NodeCreationEditPolicy; +import org.eclipse.sirius.diagram.graphical.edit.policies.SiriusGraphicalNodeEditPolicy; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.LostMessageEnd; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.ReparentExecutionOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SetMessageRangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SetVerticalRangeOperation; +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.LostMessageEndEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceMessageEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceNodeCreationPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceSiriusGraphicalNodeEditPolicy; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.ui.tools.api.figure.AirDefaultSizeNodeFigure; +import org.eclipse.sirius.diagram.ui.tools.api.policy.CompoundEditPolicy; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; +import org.eclipse.sirius.diagram.ui.tools.internal.util.EditPartQuery; + +/** + * Helper class to factor common code between root executions (lifelines) and + * normal executions which can not be shared by inheritance. + * + * @author pcdavid, smonnier + */ +public final class ExecutionOperations { + private ExecutionOperations() { + // Prevent instantiations. + } + + /** + * Install/replace a NodeCreationEditPolicy on the CONTAINER_ROLE which is + * aware of the ExecutionCreationTool and calls it instead of the generic + * NodeCreationDescription. + * + * @param self + * the edit part. + */ + public static void installExecutionAwareNodeCreationPolicy(ISequenceEventEditPart self) { + ExecutionOperations.replaceEditPolicy(self, EditPolicy.CONTAINER_ROLE, new SequenceNodeCreationPolicy(), NodeCreationEditPolicy.class); + ExecutionOperations.replaceEditPolicy(self, EditPolicy.GRAPHICAL_NODE_ROLE, new SequenceSiriusGraphicalNodeEditPolicy(), SiriusGraphicalNodeEditPolicy.class); + } + + /** + * . + * + * @param self + * . + * @param role + * . + * @param editPolicy + * . + * @param policyType + * . + */ + public static void replaceEditPolicy(IGraphicalEditPart self, String role, EditPolicy editPolicy, Class<?> policyType) { + if (self.getEditPolicy(role) instanceof CompoundEditPolicy) { + /* + * See + * AbstractDiagramNodeEditPartOperation.createDefaultEditPolicies + * (IAbstractDiagramNodeEditPart self) for the expected structure of + * the policy on CONTAINER_ROLE. + */ + CompoundEditPolicy cep = (CompoundEditPolicy) self.getEditPolicy(role); + for (int i = 0; i < cep.getEditPolicies().size(); i++) { + if (policyType.isInstance(cep.getEditPolicies().get(i))) { + cep.getEditPolicies().set(i, editPolicy); + break; + } + } + self.installEditPolicy(role, cep); + } else { + self.installEditPolicy(role, editPolicy); + } + + } + + /** + * If possible, make the whole figure's area a valid location for a + * SlidableAnchor. Executions are usually very narrow vertically, and the + * default setting (0.5) makes the zone usable to anchor a message too small + * to be usable. + * + * @param figure + * the figure to adjust. + */ + public static void adjustFigureSlidableArea(NodeFigure figure) { + if (figure instanceof AirDefaultSizeNodeFigure) { + ((AirDefaultSizeNodeFigure) figure).setSlidableAnchorArea(1.0); + } + } + + /** + * Returns a command to reconnect any connection incident to the specified + * execution (which is about to be removed) to the parent of that execution + * while keeping the same vertical range. + * + * @param exec + * the execution which is about to be removed. + * @return a command to reconnect all the edges incident to that execution + * on its parent, while keeping the same vertical range. + */ + public static org.eclipse.emf.common.command.Command getReconnectEdgesToParentCommand(ExecutionEditPart exec) { + org.eclipse.emf.common.command.CompoundCommand result = new org.eclipse.emf.common.command.CompoundCommand(); + + ISequenceEventEditPart parentEP = new EditPartQuery(exec).getFirstAncestorOfType(ISequenceEventEditPart.class); + AbstractNodeEvent execution = (AbstractNodeEvent) exec.getISequenceEvent(); + ISequenceEvent parent = execution.getHierarchicalParentEvent(); + Range parentRange = parent.getVerticalRange(); + + /** + * Previously, we assumed that the call/return messages of an execution + * will be deleted with it ; and GMF could be confused if they were + * reconnected (leaving invalid edit parts on the diagram, leading to a + * grey cross appearing which can not be removed except by + * closing/reopening the diagram). + * <p> + * This assumption was highly specific to a particular semantic of the + * delete tool associated with the execution. Now the reconnection is + * done before the call of the delete tool. + */ + + for (SequenceMessageEditPart msg : Iterables.filter(exec.getSourceConnections(), SequenceMessageEditPart.class)) { + if (msg.getSource() != msg.getTarget()) { + result.append(ExecutionOperations.getSourceReconnectionCommand(parentEP, parentRange, msg)); + } + } + for (SequenceMessageEditPart msg : Iterables.filter(exec.getTargetConnections(), SequenceMessageEditPart.class)) { + if (msg.getSource() != msg.getTarget()) { + result.append(ExecutionOperations.getTargetReconnectionCommand(parentEP, parentRange, msg)); + } + } + for (SequenceMessageEditPart msg : Iterables.filter(exec.getSourceConnections(), SequenceMessageEditPart.class)) { + Message message = (Message) msg.getISequenceEvent(); + if (msg.getSource() == msg.getTarget()) { + result.append(ExecutionOperations.getReflectiveReconnectionCommand(parentEP.getISequenceEvent(), parentRange, message, parentEP.getEditingDomain())); + } + } + + if (result.isEmpty()) { + return IdentityCommand.INSTANCE; + } else { + return result; + } + } + + private static Command getReflectiveReconnectionCommand(ISequenceEvent parent, Range parentRange, Message msg, TransactionalEditingDomain domain) { + CompoundCommand result = new CompoundCommand(); + Range currentRange = msg.getVerticalRange(); + + SetMessageRangeOperation smrc = new SetMessageRangeOperation((Edge) msg.getNotationView(), currentRange, true); + // The source side is reconnected to the parent of the execution being + // deleted. + smrc.setSource(parent.getNotationView(), new Rectangle(0, parentRange.getLowerBound(), 0, parentRange.width())); + // The target side is reconnected to the parent of the execution being + // removed. + smrc.setTarget(parent.getNotationView(), new Rectangle(0, parentRange.getLowerBound(), 0, parentRange.width())); + result.append(CommandFactory.createRecordingCommand(domain, smrc)); + return result; + } + + private static Command getSourceReconnectionCommand(ISequenceEventEditPart parent, Range parentRange, SequenceMessageEditPart msg) { + CompoundCommand result = new CompoundCommand(); + Range currentRange = msg.getISequenceEvent().getVerticalRange(); + + SetMessageRangeOperation smrc = new SetMessageRangeOperation((Edge) msg.getNotationView(), currentRange, true); + // The source side is reconnected to the parent of the execution being + // deleted. + smrc.setSource(parent.getNotationView(), new Rectangle(0, parentRange.getLowerBound(), 0, parentRange.width())); + // The target side does not change but we need to compute its bounds and + // it may not be an ISequenceEvent (e.g. an InstanceRole) + IGraphicalEditPart target = (IGraphicalEditPart) msg.getTarget(); + Rectangle targetBounds; + if (target instanceof ISequenceEventEditPart) { + Range targetRange = SequenceEditPartsOperations.getVerticalRange((ISequenceEventEditPart) target); + targetBounds = new Rectangle(0, targetRange.getLowerBound(), 0, targetRange.width()); + } else { + targetBounds = target.getFigure().getBounds().getCopy(); + if (target.getFigure().getParent() != null) { + target.getFigure().getParent().translateToAbsolute(targetBounds); + } + } + smrc.setTarget(target.getNotationView(), targetBounds); + result.append(CommandFactory.createRecordingCommand(parent.getEditingDomain(), smrc)); + return result; + } + + private static Command getTargetReconnectionCommand(ISequenceEventEditPart parent, Range parentRange, SequenceMessageEditPart msg) { + CompoundCommand result = new CompoundCommand(); + Range currentRange = msg.getISequenceEvent().getVerticalRange(); + + SetMessageRangeOperation smrc = new SetMessageRangeOperation((Edge) msg.getNotationView(), currentRange, true); + // The source side does not change but we need to compute its bounds. + IGraphicalEditPart source = (IGraphicalEditPart) msg.getSource(); + Rectangle sourceBounds; + if (source instanceof ISequenceEventEditPart) { + Range sourceRange = SequenceEditPartsOperations.getVerticalRange((ISequenceEventEditPart) source); + sourceBounds = new Rectangle(0, sourceRange.getLowerBound(), 0, sourceRange.width()); + } else if (source instanceof LostMessageEndEditPart) { + LostMessageEnd lostMessageEnd = ((LostMessageEndEditPart) source).getLostMessageEnd(); + sourceBounds = lostMessageEnd.getProperLogicalBounds().getCopy(); + } else { + throw new RuntimeException("The source of a message should always be an ISequenceEvent but was: " + String.valueOf(source)); + } + smrc.setSource(source.getNotationView(), sourceBounds); + // The target side is reconnected to the parent of the execution being + // removed. + smrc.setTarget(parent.getNotationView(), new Rectangle(0, parentRange.getLowerBound(), 0, parentRange.width())); + result.append(CommandFactory.createRecordingCommand(parent.getEditingDomain(), smrc)); + return result; + } + + /** + * Returns a command to reconnect all the direct sub-executions of the + * specified execution (which is about to be removed) to the parent of that + * execution while keeping the same vertical range. + * + * @param removedExecEditPart + * the execution which is about to be removed. + * @return a command to reconnect all the direct sub-executions of that + * execution on its parent, while keeping the same vertical range. + */ + public static org.eclipse.emf.common.command.Command getReconnectSubExecutionsToParentCommand(final ExecutionEditPart removedExecEditPart) { + org.eclipse.emf.common.command.CompoundCommand result = new org.eclipse.emf.common.command.CompoundCommand(); + + ISequenceEvent removedExec = removedExecEditPart.getISequenceEvent(); + if (removedExec != null) { + ISequenceEvent futureParent = removedExec.getHierarchicalParentEvent(); + TransactionalEditingDomain domain = removedExecEditPart.getEditingDomain(); + for (View hierarchicalChild : Iterables.filter(Iterables.filter(removedExec.getNotationView().getChildren(), View.class), AbstractNodeEvent.notationPredicate())) { + Option<AbstractNodeEvent> child = ISequenceElementAccessor.getAbstractNodeEvent(hierarchicalChild); + if (child.some()) { + final Range childRange = child.get().getVerticalRange(); + result.append(CommandFactory.createRecordingCommand(domain, new ReparentExecutionOperation(child.get(), futureParent))); + result.append(CommandFactory.createRecordingCommand(domain, new SetVerticalRangeOperation(child.get(), childRange))); + } + } + } + if (result.isEmpty()) { + return IdentityCommand.INSTANCE; + } else { + return result; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/MoveViewOperation.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/MoveViewOperation.java new file mode 100644 index 0000000000..3f68b58f0e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/MoveViewOperation.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; + +/** + * Shift a view of a delta. + * + * @author edugueperoux + */ +public class MoveViewOperation extends AbstractModelChangeOperation<Void> { + + private IAdaptable adapter; + + private Point moveDelta; + + /** + * Default constructor. + * + * @param label + * label to display in the undo menu item + * @param adapter + * adapter for the view + * @param moveDelta + * delta to move the view + */ + public MoveViewOperation(String label, IAdaptable adapter, Point moveDelta) { + super(label); + Assert.isNotNull(adapter, "view cannot be null"); //$NON-NLS-1$ + Assert.isNotNull(moveDelta, "moveDelta cannot be null"); //$NON-NLS-1$ + this.adapter = adapter; + this.moveDelta = moveDelta; + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + if (adapter != null) { + View view = (View) adapter.getAdapter(View.class); + if (moveDelta != null) { + if (view instanceof Node) { + Node node = (Node) view; + LayoutConstraint constraint = node.getLayoutConstraint(); + if (constraint != null && NotationPackage.eINSTANCE.getLocation().isInstance(constraint)) { + Location location = (Location) constraint; + location.setX(location.getX() + moveDelta.x); + location.setY(location.getY() + moveDelta.y); + } + } + } + } + return null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ResizeMessagesOperation.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ResizeMessagesOperation.java new file mode 100644 index 0000000000..df13989be3 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ResizeMessagesOperation.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation; + +import java.util.Collection; + +import org.eclipse.gmf.runtime.notation.Edge; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; + +/** + * This operation is called to shift the given messages. It adjusts the GMF + * bendpoints of the messages to/from an execution (or any of its + * sub-executions). + * + * @author mporhel + */ +public class ResizeMessagesOperation extends ShiftMessagesOperation { + + private int resizeDelta; + + /** + * Constructor. + * + * @param messagesToShift + * name of the current Operation. + * @param movedElements + * name of the current Operation. + * @param deltaY + * the vertical amount the execution was moved. + * @param revert + * if true, revert the adjustments from source/target vectors + * @param move + * if true, the messages of any of its sub-executions will be + * shifted. If false, the parent part was resized and only direct + * sub messages will be shifted + * @param resizeDelta + * the resize delta + */ + public ResizeMessagesOperation(Collection<Message> messagesToShift, Collection<ISequenceEvent> movedElements, int deltaY, boolean revert, boolean move, int resizeDelta) { + super(messagesToShift, movedElements, deltaY, revert, move); + // this.resizeDelta = resizeDelta; + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ShiftMessagesOperation#getDeltaY(org.eclipse.gmf.runtime.notation.Edge, + * boolean) + */ + @Override + protected int getDeltaY(Edge edge, boolean source) { + return super.getDeltaY(edge, source) + resizeDelta; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ResizeViewOperation.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ResizeViewOperation.java new file mode 100644 index 0000000000..12faa8316a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ResizeViewOperation.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.Size; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; + +/** + * Resize a view of a delta. + * + * @author edugueperoux + */ +public class ResizeViewOperation extends AbstractModelChangeOperation<Void> { + + private IAdaptable adapter; + + private Dimension sizeDelta; + + /** + * Default constructor. + * + * @param label + * label to display in the undo menu item + * @param adapter + * adapter for the view + * @param sizeDelta + * delta to resize the view + */ + public ResizeViewOperation(String label, IAdaptable adapter, Dimension sizeDelta) { + super(label); + Assert.isNotNull(adapter, "view cannot be null"); //$NON-NLS-1$ + Assert.isNotNull(sizeDelta, "sizeDelta cannot be null"); //$NON-NLS-1$ + this.adapter = adapter; + this.sizeDelta = sizeDelta; + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + if (adapter != null) { + View view = (View) adapter.getAdapter(View.class); + if (sizeDelta != null) { + if (view instanceof Node) { + Node node = (Node) view; + LayoutConstraint constraint = node.getLayoutConstraint(); + if (constraint != null && NotationPackage.eINSTANCE.getSize().isInstance(constraint)) { + Size size = (Size) constraint; + size.setWidth(size.getWidth() + sizeDelta.width); + size.setHeight(size.getHeight() + sizeDelta.height); + } + } + } + } + return null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/SequenceEditPartsOperations.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/SequenceEditPartsOperations.java new file mode 100644 index 0000000000..ea58ebd4be --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/SequenceEditPartsOperations.java @@ -0,0 +1,367 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation; + +import java.util.Collection; +import java.util.Map; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gef.requests.CreateConnectionRequest; +import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; +import org.eclipse.gmf.runtime.diagram.ui.editparts.AbstractBorderItemEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.figures.IBorderItemLocator; +import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.description.tool.EdgeCreationDescription; +import org.eclipse.sirius.diagram.internal.view.factories.ViewLocationHint; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.RefreshGraphicalOrderingOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.RefreshSemanticOrderingsOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SynchronizeISequenceEventsSemanticOrderingOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SynchronizeInstanceRoleSemanticOrderingOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.tool.ToolCommandBuilder; +import org.eclipse.sirius.diagram.sequence.description.tool.MessageCreationTool; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ISequenceEventEditPart; +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.util.Range; +import org.eclipse.sirius.diagram.tools.api.command.GMFCommandWrapper; +import org.eclipse.sirius.diagram.ui.tools.api.figure.locator.DBorderItemLocator; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactoryProvider; + +/** + * Helper class to share code between different kinds of edit parts when + * inheritance is not possible or inconvenient. + * + * @author pcdavid, smonnier, mporhel + */ +public final class SequenceEditPartsOperations { + private SequenceEditPartsOperations() { + // Prevent instantiation. + } + + /** + * Add graphical and semantic synchronize commands to do a full refresh of + * the semantic and graphical orderings. + * + * @param self + * the edit part which created the base command. + * @param cc + * the compound command to complete. + */ + public static void appendFullRefresh(IGraphicalEditPart self, CompoundCommand cc) { + SequenceDiagram sequenceDiagram = EditPartsHelper.getSequenceDiagram(self); + SequenceDDiagram sequenceDDiagram = sequenceDiagram.getSequenceDDiagram(); + TransactionalEditingDomain domain = self.getEditingDomain(); + cc.add(new ICommandProxy(CommandFactory.createICommand(domain, new RefreshGraphicalOrderingOperation(sequenceDiagram)))); + cc.add(new ICommandProxy(CommandFactory.createICommand(domain, new RefreshSemanticOrderingsOperation(sequenceDDiagram)))); + } + + /** + * Add graphical and semantic synchronize commands to do a full refresh of + * the semantic and graphical orderings. + * + * @param self + * the edit part which created the base command. + * @param ctc + * the CompositeTransactionalCommand to complete. + */ + public static void appendFullRefresh(IGraphicalEditPart self, CompositeTransactionalCommand ctc) { + SequenceEditPartsOperations.addRefreshGraphicalOrderingCommand(ctc, self); + SequenceEditPartsOperations.addRefreshSemanticOrderingCommand(ctc, self); + } + + /** + * Append a command to refresh the graphical ordering of the sequence + * diagram containing the specified edit part. + * + * @param cc + * the target CompositeTransactionalCommand. + * @param self + * an edit part of the sequence diagram to refresh. + */ + public static void addRefreshGraphicalOrderingCommand(CompositeTransactionalCommand cc, IGraphicalEditPart self) { + SequenceDiagram sequenceDiagram = EditPartsHelper.getSequenceDiagram(self); + TransactionalEditingDomain domain = cc.getEditingDomain(); + cc.compose(CommandFactory.createICommand(domain, new RefreshGraphicalOrderingOperation(sequenceDiagram))); + } + + /** + * Append a command to refresh the semantic ordering of a sequence diagram. + * + * @param cc + * the target CompositeTransactionalCommand. + * @param self + * an edit part of the sequence diagram to refresh. + */ + public static void addRefreshSemanticOrderingCommand(CompositeTransactionalCommand cc, IGraphicalEditPart self) { + SequenceDiagram sequenceDiagram = EditPartsHelper.getSequenceDiagram(self); + SequenceDDiagram sequenceDDiagram = sequenceDiagram.getSequenceDDiagram(); + TransactionalEditingDomain domain = cc.getEditingDomain(); + cc.compose(CommandFactory.createICommand(domain, new RefreshSemanticOrderingsOperation(sequenceDDiagram))); + } + + /** + * Append a command to synchronize the semantic ordering of the specified + * event. + * + * @param cc + * the target CompositeTransactionalCommand. + * @param event + * the event which has moved graphically and whose semantic + * ordering should be updated to match the new position. + * @param set + */ + public static void addSynchronizeSemanticOrderingCommand(CompositeTransactionalCommand cc, ISequenceEvent event) { + cc.compose(CommandFactory.createICommand(cc.getEditingDomain(), new SynchronizeISequenceEventsSemanticOrderingOperation(event))); + } + + /** + * Append a command to synchronize the semantic ordering of the specified + * event. + * + * @param cc + * the target CompositeTransactionalCommand. + * @param event + * the event which has moved graphically and whose semantic + * ordering should be updated to match the new position. + * @param selection + * additionnal events to reorder. + */ + public static void addSynchronizeSemanticOrderingCommand(CompositeTransactionalCommand cc, ISequenceEvent event, Collection<ISequenceEvent> selection) { + cc.compose(CommandFactory.createICommand(cc.getEditingDomain(), new SynchronizeISequenceEventsSemanticOrderingOperation(event, selection))); + } + + /** + * Append a command to synchronize the semantic ordering of the specified + * event. + * + * @param cc + * the target CompositeTransactionalCommand. + * @param instanceRole + * the instance role which has moved graphically and whose + * semantic ordering should be updated to match the new position. + * @param set + */ + public static void addSynchronizeSemanticOrderingCommand(CompositeTransactionalCommand cc, InstanceRole instanceRole) { + cc.compose(CommandFactory.createICommand(cc.getEditingDomain(), new SynchronizeInstanceRoleSemanticOrderingOperation(instanceRole))); + } + + /** + * Append a command to synchronize the semantic ordering of the specified + * event. + * + * @param cc + * the target CompositeTransactionalCommand. + * @param instanceRole + * the instance role which has moved graphically and whose + * semantic ordering should be updated to match the new position. + * @param selection + * additionnal instance roles to reorder. + */ + public static void addSynchronizeSemanticOrderingCommand(CompositeTransactionalCommand cc, InstanceRole instanceRole, Collection<InstanceRole> selection) { + cc.compose(CommandFactory.createICommand(cc.getEditingDomain(), new SynchronizeInstanceRoleSemanticOrderingOperation(instanceRole, selection))); + } + + /** + * Registers an edit part in the viewer's registry using the specified + * <code>DDiagramElement</code> of <code>DDiagram</code> as key. + * + * @param self + * the edit part to register. + * @param element + * the key to use. + */ + @SuppressWarnings("unchecked") + public static void registerDiagramElement(IGraphicalEditPart self, EObject element) { + self.getViewer().getEditPartRegistry().put(element, self); + } + + /** + * Unregisters an edit part in the viewer's registry using the specified + * <code>DDiagramElement</code> of <code>DDiagram</code> as key. + * + * @param self + * the edit part to unregister. + * @param element + * the key of the registry entry to remove. + */ + public static void unregisterDiagramElement(IGraphicalEditPart self, EObject element) { + @SuppressWarnings("unchecked") + Map<Object, Object> registry = self.getViewer().getEditPartRegistry(); + if (registry.get(element) == self) { + registry.remove(element); + } + } + + /** + * Configures a bordered node to appear on the given side and at the given + * offset of its parent part. + * + * @param self + * the bordered node. + * @param side + * the side of the parent on which the node should be. + * @param offset + * the offset (relative to the parent) at which the node should + * be placed. + */ + public static void setBorderItemLocation(AbstractBorderItemEditPart self, int side, Dimension offset) { + IBorderItemLocator borderItemLocator = self.getBorderItemLocator(); + if (borderItemLocator instanceof DBorderItemLocator) { + DBorderItemLocator dbil = (DBorderItemLocator) borderItemLocator; + dbil.setCurrentSideOfParent(side); + dbil.setBorderItemOffset(offset); + } + } + + /** + * Shared implementation of most of + * SiriusBaseItemSemanticEditPolicy.buildCreateEdgeCommand() for use in + * ExecutionSemanticEditPolicy and InstanceRoleSemanticEditPolicy. + * + * @param self + * the edit part. + * @param result + * the command to modify. + * @param request + * the original request. + * @param source + * the source of the edge to create. + * @param target + * the target of the edge to create. + * @param edgeCreationDescription + * the tool to use to create the edge. + * @param cmdFactoryProvider + * the command factory provider to use to build a command from + * the tool. + */ + public static void buildCreateEdgeCommand(IGraphicalEditPart self, CompoundCommand result, CreateConnectionRequest request, EdgeTarget source, EdgeTarget target, + EdgeCreationDescription edgeCreationDescription, IDiagramCommandFactoryProvider cmdFactoryProvider) { + org.eclipse.emf.common.command.Command emfCommand; + TransactionalEditingDomain domain = self.getEditingDomain(); + if (edgeCreationDescription instanceof MessageCreationTool && ((DDiagramElement) source).getParentDiagram() instanceof SequenceDDiagram) { + Point sourceLocation = request.getLocation().getCopy(); + Point targetLocation = request.getLocation().getCopy(); + GraphicalHelper.screen2logical(sourceLocation, self); + GraphicalHelper.screen2logical(targetLocation, self); + + Point srcLocationHint = SequenceEditPartsOperations.getConnectionSourceLocation(request, self); + Point tgtLocationHint = SequenceEditPartsOperations.getConnectionTargetLocation(request, self); + + if (srcLocationHint != null) { + sourceLocation = srcLocationHint; + } + + if (tgtLocationHint != null) { + targetLocation = tgtLocationHint; + } + + // Avoid deferred message, accept only messageToSelf + if (source != target) { + targetLocation = sourceLocation; + } + + SequenceDiagram sequenceDiagram = EditPartsHelper.getSequenceDiagram(self); + SequenceDDiagram diagram = sequenceDiagram.getSequenceDDiagram(); + EventEnd startingEndBefore = SequenceGraphicalHelper.getEndBefore(diagram, sourceLocation.y); + EventEnd finishingEndBefore = SequenceGraphicalHelper.getEndBefore(diagram, targetLocation.y); + emfCommand = ToolCommandBuilder.buildCreateMessageCommand(source, target, (MessageCreationTool) edgeCreationDescription, startingEndBefore, finishingEndBefore); + + org.eclipse.emf.common.command.CompoundCommand cc = new org.eclipse.emf.common.command.CompoundCommand(); + cc.append(emfCommand); + cc.append(CommandFactory.createRecordingCommand(domain, new RefreshSemanticOrderingsOperation(diagram))); + emfCommand = cc; + } else { + emfCommand = cmdFactoryProvider.getCommandFactory(domain).buildCreateEdgeCommandFromTool(source, target, edgeCreationDescription); + } + result.add(new ICommandProxy(new GMFCommandWrapper(domain, emfCommand))); + } + + /** + * Common implementation of + * {@link ISequenceEventEditPart#getVerticalRange()}. + * + * @param self + * the event. + * @return the maximal range occupied by children of the event. + */ + public static Range getVerticalRange(ISequenceEventEditPart self) { + ISequenceEvent iSequenceEvent = self != null ? self.getISequenceEvent() : null; + return iSequenceEvent != null ? iSequenceEvent.getVerticalRange() : Range.emptyRange(); + } + + /** + * Common implementation of + * {@link ISequenceEventEditPart#setVerticalRange()}. + * + * @param self + * the event. + * @param newRange + * the new range + */ + public static void setVerticalRange(ISequenceEventEditPart self, Range newRange) { + ISequenceEvent iSequenceEvent = self != null ? self.getISequenceEvent() : null; + if (iSequenceEvent != null) { + iSequenceEvent.setVerticalRange(newRange); + } + } + + /** + * Return the source connection anchor location from view location hint. + * + * @param request + * the current request. + * @param self + * an edit part to compute logical location. + * @return the source location. + */ + public static Point getConnectionSourceLocation(CreateConnectionRequest request, IGraphicalEditPart self) { + return SequenceEditPartsOperations.getConnectionLocation(self, ViewLocationHint.SOURCE_ANCHOR_LOCATION); + } + + /** + * Return the target connection anchor location from view location hint. + * + * @param request + * the current request. + * @param self + * an edit part to compute logical location. + * @return the target location. + */ + public static Point getConnectionTargetLocation(CreateConnectionRequest request, IGraphicalEditPart self) { + return SequenceEditPartsOperations.getConnectionLocation(self, ViewLocationHint.TARGET_ANCHOR_LOCATION); + } + + private static Point getConnectionLocation(IGraphicalEditPart self, String key) { + Object hint = ViewLocationHint.getInstance().getData(key); + if (hint instanceof Point) { + Point location = ((Point) hint).getCopy(); + GraphicalHelper.screen2logical(location, self); + return location; + } else { + return null; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ShiftDescendantMessagesOperation.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ShiftDescendantMessagesOperation.java new file mode 100644 index 0000000000..7b0dee2f8c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ShiftDescendantMessagesOperation.java @@ -0,0 +1,269 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.draw2d.geometry.PrecisionPoint; +import org.eclipse.gmf.runtime.draw2d.ui.figures.BaseSlidableAnchor; +import org.eclipse.gmf.runtime.gef.ui.figures.SlidableAnchor; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.IdentityAnchor; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.ISequenceEventsTreeIterator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.SequenceGraphicalHelper; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.ui.tools.api.util.GMFNotationHelper; + +/** + * This operation is called when an execution is moved or resized vertically. It + * adjusts the GMF bendpoints of the messages to/from an execution (or any of + * its sub-executions) so that the messages are moved along with the execution + * of the same amount. + * + * @author pcdavid, mporhel, smonnier + */ +public class ShiftDescendantMessagesOperation extends ShiftMessagesOperation { + private ISequenceEvent parent; + + private final boolean fromTop; + + /** + * Used for execution reparent. + */ + private boolean ignoreContainedReflexiveMessage; + + private Range oldParentRange; + + private Range newParentRange; + + private ISequenceEvent finalGrandParent; + + /** + * Constructor to shift all sub messages after move. + * + * @param parent + * the execution whose descendant messages must be adjusted. + * @param deltaY + * the vertical amount the execution was moved. + */ + public ShiftDescendantMessagesOperation(ISequenceEvent parent, int deltaY) { + this(parent, deltaY, false, true, true); + } + + /** + * Constructor. + * + * @param parent + * the execution whose descendant messages must be adjusted. + * @param deltaY + * the vertical amount the execution was moved. + * @param revert + * if true, revert the adjustments from source/target vectors + * @param move + * if true, the messages of any of its sub-executions will be + * shifted. If false, the parent part was resized and only direct + * sub messages will be shifted + * @param fromTop + * Used if move = false (resize) to know from where the + * parentPart is resized. + */ + public ShiftDescendantMessagesOperation(ISequenceEvent parent, int deltaY, boolean revert, boolean move, boolean fromTop) { + super("Shift sub-messages bendpoints by " + deltaY, deltaY, revert, move); + this.parent = parent; + this.fromTop = fromTop; + this.oldParentRange = parent.getVerticalRange(); + this.newParentRange = getNewParentRange(); + this.finalGrandParent = null; + } + + /** + * Constructor. + * + * @param parent + * the execution whose descendant messages must be adjusted. + * @param finalGrandParent + * the actual grandparent of the "executionEditPart" replacement + * (after a refresh) at the time of command execution. + * @param deltaY + * the vertical amount the execution was moved. + * @param ignoreContainedReflexiveMessage + * the parameter to decide if we need to ignore the contained + * reflexive messages. + */ + public ShiftDescendantMessagesOperation(ISequenceEvent parent, ISequenceEvent finalGrandParent, int deltaY, boolean ignoreContainedReflexiveMessage) { + this(parent, deltaY); + this.finalGrandParent = finalGrandParent; + this.ignoreContainedReflexiveMessage = ignoreContainedReflexiveMessage; + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + final Set<ISequenceEvent> descendants = Sets.newHashSet(); + populateMessageToShift(descendants); + + // Handle messages. + super.execute(); + + // Handle notes. + Set<Edge> allConnections = Sets.newHashSet(); + + Iterator<ISequenceEvent> iter = new ISequenceEventsTreeIterator(parent, true); + while (iter.hasNext()) { + ISequenceEvent iSequenceEvent = iter.next(); + populateConnections(allConnections, iSequenceEvent.getNotationView()); + } + + for (Edge conn : allConnections) { + if (!descendants.contains(conn) && (isNoteAttachment(conn) || isNonSequenceEdgeAttachment(conn))) { + shiftAnchor(conn); + } + } + return null; + } + + private void populateMessageToShift(Set<ISequenceEvent> descendants) { + if (finalGrandParent != null) { + final View model = parent.getNotationView(); + for (final ISequenceEvent child : finalGrandParent.getSubEvents()) { + if (child.getNotationView() == model) { + parent = child; + break; + } + } + } + + populate(descendants); + + final Predicate<Message> filterReflexiveMessage = new Predicate<Message>() { + public boolean apply(final Message input) { + return !input.isReflective(); + } + }; + + if (ignoreContainedReflexiveMessage) { + Iterables.addAll(messagesToShift, Iterables.filter(Iterables.filter(descendants, Message.class), filterReflexiveMessage)); + } else { + Iterables.addAll(messagesToShift, Iterables.filter(descendants, Message.class)); + } + } + + private void populateConnections(Set<Edge> allConnections, View part) { + Iterables.addAll(allConnections, Iterables.filter(part.getSourceEdges(), Edge.class)); + Iterables.addAll(allConnections, Iterables.filter(part.getTargetEdges(), Edge.class)); + } + + private boolean isNoteAttachment(Edge conn) { + return conn != null && GMFNotationHelper.isNoteAttachment(conn); + } + + private boolean isNonSequenceEdgeAttachment(Edge conn) { + return conn != null && conn.getElement() instanceof DEdge && !ISequenceElementAccessor.getMessage(conn).some(); + } + + private void shiftAnchor(Edge edge) { + boolean isOutgoing = Iterables.contains(Iterables.transform(movedElements, ISequenceElement.NOTATION_VIEW), edge.getSource()); + + if (isOutgoing) { + IdentityAnchor sourceAnchor = (IdentityAnchor) edge.getSourceAnchor(); + int sourceAnchorLocation = SequenceGraphicalHelper.getAnchorAbsolutePosition(sourceAnchor, oldParentRange); + PrecisionPoint position = BaseSlidableAnchor.parseTerminalString(sourceAnchor.getId()); + position.preciseY = newParentRange.getProportionalLocation(sourceAnchorLocation); + String terminal = new SlidableAnchor(null, position).getTerminal(); + sourceAnchor.setId(terminal); + } else { + IdentityAnchor targetAnchor = (IdentityAnchor) edge.getTargetAnchor(); + int targetAnchorLocation = SequenceGraphicalHelper.getAnchorAbsolutePosition(targetAnchor, oldParentRange); + PrecisionPoint position = BaseSlidableAnchor.parseTerminalString(targetAnchor.getId()); + position.preciseY = newParentRange.getProportionalLocation(targetAnchorLocation); + String terminal = new SlidableAnchor(null, position).getTerminal(); + targetAnchor.setId(terminal); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected int getDeltaY(Edge edge, boolean source) { + if (!move) { + IdentityAnchor anchor; + if (source) { + anchor = (IdentityAnchor) edge.getSourceAnchor(); + } else { + anchor = (IdentityAnchor) edge.getTargetAnchor(); + } + return getDeltaY(anchor); + } else { + return super.getDeltaY(edge, source); + } + } + + private int getDeltaY(IdentityAnchor anchor) { + double oldAnchorLocation = SequenceGraphicalHelper.getAnchorAbsolutePosition(anchor, oldParentRange); + double newAnchorLocation = SequenceGraphicalHelper.getAnchorAbsolutePosition(anchor, newParentRange); + return (int) (oldAnchorLocation - newAnchorLocation); + } + + private void populate(Set<ISequenceEvent> descendants) { + if (move) { + descendants.addAll(new ISequenceEventQuery(parent).getAllDescendants(true)); + Iterables.addAll(movedElements, Iterables.filter(descendants, AbstractNodeEvent.class)); + } else { + // descendants.addAll(parent.getSubEvents()); + descendants.addAll(new ISequenceEventQuery(parent).getAllDescendants(true)); + movedElements.add(parent); + } + // Finds compounds events of each ExecutionEditPart found in descendants + ArrayList<ISequenceEvent> compoundEvents = Lists.newArrayList(); + for (AbstractNodeEvent eep : Iterables.filter(descendants, AbstractNodeEvent.class)) { + compoundEvents.addAll(EventEndHelper.getCompoundEvents(eep)); + } + Iterables.addAll(descendants, compoundEvents); + if (parent instanceof Lifeline) { + movedElements.add(parent); + } + } + + private Range getNewParentRange() { + int newLowerBound = oldParentRange.getLowerBound(); + int newUppeRBound = oldParentRange.getUpperBound(); + if (move) { + newLowerBound += deltaY; + newUppeRBound += deltaY; + } else if (fromTop) { + newLowerBound += deltaY; + } else { + newUppeRBound += deltaY; + } + return new Range(newLowerBound, newUppeRBound); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ShiftMessagesOperation.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ShiftMessagesOperation.java new file mode 100644 index 0000000000..fb6b4091cc --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/operation/ShiftMessagesOperation.java @@ -0,0 +1,195 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.RelativeBendpoints; +import org.eclipse.gmf.runtime.notation.datatype.RelativeBendpoint; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; + +/** + * This operation is called to shift the given messages. It adjusts the GMF + * bendpoints of the messages to/from an execution (or any of its + * sub-executions). + * + * @author mporhel + */ +public class ShiftMessagesOperation extends AbstractModelChangeOperation<Void> { + /** + * Indicates if messages to shift did really move. + */ + protected final boolean move; + + /** + * Message to shift. + */ + protected final Collection<Message> messagesToShift = Lists.newArrayList(); + + /** + * Moved elements. + */ + protected final Collection<ISequenceEvent> movedElements = Sets.newHashSet(); + + /** + * Vertical move to handle. + */ + protected final int deltaY; + + private final boolean revert; + + /** + * Constructor. + * + * @param name + * name of the current Operation. + * @param deltaY + * the vertical amount the execution was moved. + * @param revert + * if true, revert the adjustments from source/target vectors + * @param move + * if true, the messages of any of its sub-executions will be + * shifted. If false, the parent part was resized and only direct + * sub messages will be shifted + */ + protected ShiftMessagesOperation(String name, int deltaY, boolean revert, boolean move) { + super(name); + this.deltaY = deltaY; + this.revert = revert; + this.move = move; + } + + /** + * Constructor. + * + * @param messagesToShift + * name of the current Operation. + * @param movedElements + * name of the current Operation. + * @param deltaY + * the vertical amount the execution was moved. + * @param revert + * if true, revert the adjustments from source/target vectors + * @param move + * if true, the messages of any of its sub-executions will be + * shifted. If false, the parent part was resized and only direct + * sub messages will be shifted + */ + public ShiftMessagesOperation(Collection<Message> messagesToShift, Collection<ISequenceEvent> movedElements, int deltaY, boolean revert, boolean move) { + this("Shift given message", deltaY, revert, move); + Preconditions.checkNotNull(messagesToShift); + Preconditions.checkNotNull(movedElements); + this.messagesToShift.addAll(messagesToShift); + this.movedElements.addAll(movedElements); + } + + /** + * {@inheritDoc} + */ + @Override + public Void execute() { + for (Message smep : messagesToShift) { + shiftBendpoints(smep); + } + + return null; + } + + private void shiftBendpoints(Message message) { + boolean needShiftFromSrc = needShift(message, true); + boolean needShiftFromTgt = needShift(message, false); + + Edge edge = (Edge) message.getNotationView(); + int currentSourceDeltaY = 0; + int currentTargetDeltaY = 0; + + /* + * If the message starts from the execution being moved (or any of its + * sub-executions), its sourceX/sourceY vectors are still valid as they + * are relative to the source anchor which moved along with the + * execution. However, the target execution/lifeline on the other side + * did not move, so we need to adjust the target vector. + */ + + /* + * The current message is moved by its target, so we need to adjust the + * source vector. + */ + int srcShift = getDeltaY(edge, false); + if (needShiftFromSrc) { + currentSourceDeltaY = revert ? 0 : srcShift; + currentTargetDeltaY = revert ? srcShift : 0; + } + /* + * The current message is moved by its source, so we need to adjust the + * target vector. + */ + int tgtShift = getDeltaY(edge, true); + if (needShiftFromTgt) { + currentTargetDeltaY = revert ? currentTargetDeltaY : tgtShift; + currentSourceDeltaY = revert ? tgtShift : currentSourceDeltaY; + } + + RelativeBendpoints bp = (RelativeBendpoints) edge.getBendpoints(); + List<?> oldPoints = bp.getPoints(); + List<RelativeBendpoint> newPoints = Lists.newArrayList(); + for (int i = 0; i < oldPoints.size(); i++) { + RelativeBendpoint old = (RelativeBendpoint) oldPoints.get(i); + newPoints.add(new RelativeBendpoint(old.getSourceX(), old.getSourceY() + currentSourceDeltaY, old.getTargetX(), old.getTargetY() + currentTargetDeltaY)); + } + bp.setPoints(newPoints); + } + + /** + * Check the direction of the given Message regarding the current context. + * + * @param message + * the message to check. + * @return true if the given message source lifeline is the actual context + * lifeline. + */ + private boolean needShift(Message message, boolean source) { + boolean movedBySrc = movedElements.contains(message.getSourceElement()); + boolean movedByTgt = movedElements.contains(message.getTargetElement()); + boolean moved = movedBySrc || movedByTgt; + + if (move && !revert) { + moved = moved || movedElements.contains(message); + } + + boolean shiftedEnd = source ? !movedBySrc : !movedByTgt; + boolean reverted = revert && movedBySrc && movedByTgt; + + return moved && (shiftedEnd || reverted); + } + + /** + * Get deltaY for the request end of the current edge. + * + * @param edge + * the given edge. + * @param source + * the requested end. + * @return the deltaY. + */ + protected int getDeltaY(Edge edge, boolean source) { + return deltaY; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/AbstractSequenceBorderedEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/AbstractSequenceBorderedEditPart.java new file mode 100644 index 0000000000..0469ae3671 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/AbstractSequenceBorderedEditPart.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import java.util.Map; +import java.util.Set; + +import org.eclipse.draw2d.ConnectionAnchor; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.Request; +import org.eclipse.gef.RequestConstants; +import org.eclipse.gef.requests.CreateRequest; +import org.eclipse.gef.requests.DropRequest; +import org.eclipse.gmf.runtime.diagram.ui.editpolicies.EditPolicyRoles; +import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.collect.Sets; + +import org.eclipse.sirius.diagram.internal.edit.parts.DNode2EditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.util.EventFinder; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ConnectionAnchorOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ExecutionOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.ExecutionSemanticEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceLaunchToolEditPolicy; +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.tools.api.layout.GraphicalHelper; + +/** + * Special edit part for common behavior of Lifelines, Executions and States. + * They are treated as bordered nodes. + * + * @author mporhel + */ +public abstract class AbstractSequenceBorderedEditPart extends DNode2EditPart implements ISequenceEventEditPart { + + /** + * Constructor. + * + * @param view + * the view. + */ + public AbstractSequenceBorderedEditPart(final View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + public void addNotify() { + SequenceEditPartsOperations.registerDiagramElement(this, resolveDiagramElement()); + super.addNotify(); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeNotify() { + super.removeNotify(); + SequenceEditPartsOperations.unregisterDiagramElement(this, resolveDiagramElement()); + } + + /** + * Overridden to install a custom semantic edit policy and node creation + * policy. + * <p> + * {@inheritDoc} + */ + @Override + protected void createDefaultEditPolicies() { + super.createDefaultEditPolicies(); + installEditPolicy(EditPolicyRoles.SEMANTIC_ROLE, new ExecutionSemanticEditPolicy()); + ExecutionOperations.installExecutionAwareNodeCreationPolicy(this); + + // Handle $endBefore for launch tools. + installEditPolicy(org.eclipse.sirius.diagram.tools.api.requests.RequestConstants.REQ_LAUNCH_TOOL, new SequenceLaunchToolEditPolicy()); + } + + /** + * Overridden to make the whole figure's area a valid location for a + * SlidableAnchor. Executions are usually very narrow vertically, and the + * default setting makes the zone usable to anchor a message too small to be + * usable. + * <p> + * {@inheritDoc} + */ + @Override + protected NodeFigure createMainFigure() { + NodeFigure figure = super.createMainFigure(); + ExecutionOperations.adjustFigureSlidableArea(figure); + return figure; + } + + /** + * {@inheritDoc} + */ + @Override + public ConnectionAnchor getSourceConnectionAnchor(Request request) { + ConnectionAnchor anchor = super.getSourceConnectionAnchor(request); + if (new RequestQuery(request).isSequenceMessageCreation()) { + anchor = ConnectionAnchorOperation.getSourceConnectionAnchor(this, request, anchor); + } + return anchor; + } + + /** + * Use the same y location as the corresponding source connection anchor, + * stored in ViewLocationHint, to improve user feedback. + * <p> + * {@inheritDoc} + */ + @Override + public ConnectionAnchor getTargetConnectionAnchor(Request request) { + boolean sequenceMessageCreation = new RequestQuery(request).isSequenceMessageCreation(); + if (sequenceMessageCreation && request instanceof DropRequest) { + ConnectionAnchorOperation.matchRequestYLocationWithSourceAnchor((DropRequest) request); + } + + ConnectionAnchor anchor = super.getTargetConnectionAnchor(request); + + if (sequenceMessageCreation) { + anchor = ConnectionAnchorOperation.getTargetConnectionAnchor(this, request, anchor); + } + return anchor; + } + + /** + * Overridden to have execution creation on existing execution and in same + * range of child execution, redirected to the most nested child execution. + * + * {@inheritDoc} + */ + @Override + public EditPart getTargetEditPart(Request request) { + Set<String> retargetTypes = Sets.newHashSet(RequestConstants.REQ_CREATE, RequestConstants.REQ_CONNECTION_START, RequestConstants.REQ_CONNECTION_END); + if (retargetTypes.contains(request.getType()) && request instanceof CreateRequest) { + CreateRequest createRequest = (CreateRequest) request; + Point location = createRequest.getLocation().getCopy(); + GraphicalHelper.screen2logical(location, this); + Range insertionPoint = new Range(location.y, location.y); + ISequenceEvent sequenceEvent = getISequenceEvent(); + EventFinder eventFinder = new EventFinder(sequenceEvent, sequenceEvent.getLifeline().get()); + ISequenceEvent mostSpecificSequenceEvent = eventFinder.findMostSpecificEvent(insertionPoint); + + if (mostSpecificSequenceEvent instanceof AbstractNodeEvent && !sequenceEvent.equals(mostSpecificSequenceEvent)) { + Map<?, ?> editPartRegistry = getTopGraphicEditPart().getViewer().getEditPartRegistry(); + Object obj = editPartRegistry.get(mostSpecificSequenceEvent.getNotationView()); + if (obj instanceof ExecutionEditPart) { + ExecutionEditPart targetEditPart = (ExecutionEditPart) obj; + return targetEditPart; + } + } + } + return super.getTargetEditPart(request); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/CombinedFragmentCompartmentEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/CombinedFragmentCompartmentEditPart.java new file mode 100644 index 0000000000..40a10c9818 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/CombinedFragmentCompartmentEditPart.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.LayoutAnimator; +import org.eclipse.draw2d.MarginBorder; +import org.eclipse.draw2d.ScrollPane; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gmf.runtime.diagram.ui.figures.ResizableCompartmentFigure; +import org.eclipse.gmf.runtime.diagram.ui.figures.ShapeCompartmentFigure; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeContainerViewNodeContainerCompartmentEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceLaunchToolEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.CombinedFragmentInvisibleResizableCompartmentFigure; + +/** + * A specific DNodeContainerViewNodeContainerCompartmentEditPart to remove the + * scroll bars. + * + * @author smonnier + */ +public class CombinedFragmentCompartmentEditPart extends DNodeContainerViewNodeContainerCompartmentEditPart { + /** + * The visual ID. Same as a normal container compartment. + * + * @see DNodeContainerViewNodeContainerCompartmentEditPart.VISUAL_ID. + */ + public static final int VISUAL_ID = 7001; + + /** + * Constructor. + * + * @param view + * the view <code>controlled</code> by this edit part. + */ + public CombinedFragmentCompartmentEditPart(View view) { + super(view); + } + + /** + * Overridden to install a specific edit policy managing the moving and + * resizing requests on combined fragment. + * <p> + * {@inheritDoc} + */ + @Override + public void installEditPolicy(Object key, EditPolicy editPolicy) { + if (!EditPolicy.CONTAINER_ROLE.equals(key)) { + super.installEditPolicy(key, editPolicy); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void createDefaultEditPolicies() { + super.createDefaultEditPolicies(); + + // Handle $endBefore for launch tools. + installEditPolicy(org.eclipse.sirius.diagram.tools.api.requests.RequestConstants.REQ_LAUNCH_TOOL, new SequenceLaunchToolEditPolicy()); + } + + /** + * Overridden to remove the scroll bars. + * <p> + * {@inheritDoc} + */ + @Override + public IFigure createFigure() { + ShapeCompartmentFigure scf = new CombinedFragmentInvisibleResizableCompartmentFigure(getCompartmentName(), getMapMode()); + // Remove the shadow border to avoid unwanted spacing + scf.setBorder(null); + scf.getContentPane().setLayoutManager(getLayoutManager()); + scf.getContentPane().addLayoutListener(LayoutAnimator.getDefault()); + scf.setTitleVisibility(false); + scf.setToolTip((IFigure) null); + scf.getScrollPane().setHorizontalScrollBarVisibility(ScrollPane.NEVER); + scf.getScrollPane().setVerticalScrollBarVisibility(ScrollPane.NEVER); + return scf; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.editparts.ResizableCompartmentEditPart#refreshVisuals() + */ + protected void refreshVisuals() { + super.refreshVisuals(); + + // TODO: Remove this when Sequence will be based on generic region + // support. + if (getFigure() instanceof ResizableCompartmentFigure) { + ResizableCompartmentFigure rcf = (ResizableCompartmentFigure) getFigure(); + if (rcf.getScrollPane() != null) { + int mb = getMapMode().DPtoLP(0); + rcf.getScrollPane().setBorder(new MarginBorder(mb, mb, mb, mb)); + } + } + + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/CombinedFragmentEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/CombinedFragmentEditPart.java new file mode 100644 index 0000000000..363a1c13b9 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/CombinedFragmentEditPart.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gmf.runtime.gef.ui.figures.DefaultSizeNodeFigure; +import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.FlatContainerStyle; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeContainerEditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.CombinedFragmentResizableEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceLaunchToolEditPolicy; + +/** + * Special edit part for combined fragments. + * + * @author pcdavid + */ +public class CombinedFragmentEditPart extends DNodeContainerEditPart implements ISequenceEventEditPart { + /** + * Standard constructor, as expected by GMF. + * + * @param view + * the view. + */ + public CombinedFragmentEditPart(View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + public void addNotify() { + SequenceEditPartsOperations.registerDiagramElement(this, resolveDiagramElement()); + super.addNotify(); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeNotify() { + super.removeNotify(); + SequenceEditPartsOperations.unregisterDiagramElement(this, resolveDiagramElement()); + } + + /** + * Overridden to install a specific edit policy managing the moving and + * resizing requests on combined fragment. + * <p> + * {@inheritDoc} + */ + @Override + public void installEditPolicy(Object key, EditPolicy editPolicy) { + if (EditPolicy.PRIMARY_DRAG_ROLE.equals(key)) { + super.installEditPolicy(key, new CombinedFragmentResizableEditPolicy()); + } else { + super.installEditPolicy(key, editPolicy); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void createDefaultEditPolicies() { + super.createDefaultEditPolicies(); + + // Handle $endBefore for launch tools. + installEditPolicy(org.eclipse.sirius.diagram.tools.api.requests.RequestConstants.REQ_LAUNCH_TOOL, new SequenceLaunchToolEditPolicy()); + } + + /** + * {@inheritDoc} + */ + @Override + protected NodeFigure createMainFigure() { + NodeFigure figure = super.createMainFigure(); + // Remove the shadow border to avoid unwanted spacing + figure.setBorder(null); + forceCombinedFragmentDefaultSize(figure); + return figure; + } + + private void forceCombinedFragmentDefaultSize(NodeFigure figure) { + if (figure instanceof DefaultSizeNodeFigure) { + EObject eObj = this.resolveSemanticElement(); + if (eObj instanceof DDiagramElementContainer) { + DDiagramElementContainer container = (DDiagramElementContainer) eObj; + if (container.getOwnedStyle() instanceof FlatContainerStyle) { + ((DefaultSizeNodeFigure) figure).setDefaultSize(LayoutConstants.DEFAULT_COMBINED_FRAGMENT_WIDTH, LayoutConstants.DEFAULT_COMBINED_FRAGMENT_HEIGHT); + } + } + } + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getISequenceEvent() { + return ISequenceElementAccessor.getCombinedFragment(getNotationView()).get(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/EndOfLifeEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/EndOfLifeEditPart.java new file mode 100644 index 0000000000..5fbd92c09d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/EndOfLifeEditPart.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.draw2d.ConnectionAnchor; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gef.Request; +import org.eclipse.gef.editpolicies.ResizableEditPolicy; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.diagram.edit.internal.part.DiagramBorderNodeEditPartOperation; +import org.eclipse.sirius.diagram.internal.edit.parts.DNode2EditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ConnectionAnchorOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.EndOfLifeSelectionPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceLaunchToolEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.LayoutEditPartConstants; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; + +/** + * The edit part for lifeline end of lives. Implemented as a bordered node on + * the lifeline, and constrained on the south side of the lifeline. Such a node + * can be used either to represent the actual "End of Life" of the lifeline when + * it is explicitly destroyed or a "handle" which can be used to resize the + * lifeline vertically. + * + * @author pcdavid + */ +public class EndOfLifeEditPart extends DNode2EditPart { + /** + * Standard constructor, as expected by GMF. + * + * @param view + * the view. + */ + public EndOfLifeEditPart(View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + protected void createDefaultEditPolicies() { + super.createDefaultEditPolicies(); + + // Handle $endBefore for launch tools. + installEditPolicy(org.eclipse.sirius.diagram.tools.api.requests.RequestConstants.REQ_LAUNCH_TOOL, new SequenceLaunchToolEditPolicy()); + } + + /** + * {@inheritDoc} + */ + @Override + public EditPolicy getPrimaryDragEditPolicy() { + ResizableEditPolicy result = new EndOfLifeSelectionPolicy(); + DDiagramElement dde = resolveDiagramElement(); + if (dde instanceof DNode) { + DNode node = (DNode) dde; + DiagramBorderNodeEditPartOperation.updateResizeKind(result, node); + } + return result; + } + + /** + * This method is overridden to have the EndOfLife (bordered node) starting + * from the border of the Instance Role. + * <p> + * {@inheritDoc} + */ + @Override + public void refresh() { + super.refresh(); + SequenceEditPartsOperations.setBorderItemLocation(this, LayoutEditPartConstants.EOL_SIDE, LayoutEditPartConstants.EOL_BORDER_ITEM_OFFSET); + } + + /** + * Overridden to register the diagram element. + * <p> + * {@inheritDoc} + */ + @Override + public void addNotify() { + SequenceEditPartsOperations.registerDiagramElement(this, resolveDiagramElement()); + super.addNotify(); + } + + /** + * Overridden to unregister the diagram element. + * <p> + * {@inheritDoc} + */ + @Override + public void removeNotify() { + super.removeNotify(); + SequenceEditPartsOperations.unregisterDiagramElement(this, resolveDiagramElement()); + } + + /** + * {@inheritDoc} + */ + @Override + public ConnectionAnchor getTargetConnectionAnchor(Request request) { + ConnectionAnchor anchor = super.getTargetConnectionAnchor(request); + if (anchor != null && new RequestQuery(request).isSequenceMessageCreation()) { + anchor = ConnectionAnchorOperation.safeCenterAnchor(anchor); + } + return anchor; + } + + /** + * {@inheritDoc} + */ + @Override + public ConnectionAnchor getSourceConnectionAnchor(Request request) { + ConnectionAnchor anchor = super.getSourceConnectionAnchor(request); + if (anchor != null && new RequestQuery(request).isSequenceMessageCreation()) { + anchor = ConnectionAnchorOperation.safeCenterAnchor(anchor); + } + return anchor; + } + + /*** + * Get the lifeline edit part of the current end of life edit part. + * + * @return the lifeline edit part. + */ + public LifelineEditPart getLifelineEditPart() { + EditPart parent = getParent(); + if (parent instanceof LifelineEditPart) { + return (LifelineEditPart) parent; + } + return null; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/ExecutionEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/ExecutionEditPart.java new file mode 100644 index 0000000000..19da599c8b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/ExecutionEditPart.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gef.editpolicies.ResizableEditPolicy; +import org.eclipse.gmf.runtime.diagram.ui.figures.BorderItemLocator; +import org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator; +import org.eclipse.gmf.runtime.gef.ui.figures.DefaultSizeNodeFigure; +import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DStylizable; +import org.eclipse.sirius.business.api.query.DDiagramElementQuery; +import org.eclipse.sirius.diagram.edit.internal.part.DiagramBorderNodeEditPartOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.ExecutionSelectionEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.ExecutionItemLocator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.SequenceNodeFigure; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IStyleConfigurationRegistry; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.StyleConfiguration; +import org.eclipse.sirius.diagram.ui.tools.api.figure.anchor.AnchorProvider; +import org.eclipse.sirius.diagram.ui.tools.internal.figure.ICollapseMode; +import org.eclipse.sirius.diagram.ui.tools.internal.util.EditPartQuery; + +/** + * Special edit part for Executions. Implemented as bordered nodes, either + * directly on a lifeline parts or on another execution. + * + * @author pcdavid, smonnier + */ +public class ExecutionEditPart extends AbstractSequenceBorderedEditPart { + /** + * Constructor. + * + * @param view + * the view. + */ + public ExecutionEditPart(View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + public EditPolicy getPrimaryDragEditPolicy() { + final ResizableEditPolicy result = new ExecutionSelectionEditPolicy(); + DDiagramElement dde = this.resolveDiagramElement(); + if (dde instanceof DNode) { + DNode node = (DNode) dde; + DiagramBorderNodeEditPartOperation.updateResizeKind(result, node); + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + protected void handleNotificationEvent(Notification notification) { + super.handleNotificationEvent(notification); + if (notification.getEventType() == Notification.SET && notification.getNotifier() instanceof Bounds) { + final EditPart parentInstanceRole = new EditPartQuery(this).getFirstAncestorOfType(InstanceRoleEditPart.class); + if (parentInstanceRole != null) { + parentInstanceRole.refresh(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + protected NodeFigure createNodePlate() { + DefaultSizeNodeFigure result = null; + final EObject eObj = resolveSemanticElement(); + if (eObj instanceof DStylizable && eObj instanceof DDiagramElement) { + final DStylizable viewNode = (DStylizable) eObj; + final StyleConfiguration styleConfiguration = IStyleConfigurationRegistry.INSTANCE.getStyleConfiguration(((DDiagramElement) eObj).getDiagramElementMapping(), viewNode.getStyle()); + final AnchorProvider anchorProvider = styleConfiguration.getAnchorProvider(); + result = new SequenceNodeFigure(getMapMode().DPtoLP(5), getMapMode().DPtoLP(5), anchorProvider); + nodePlate = result; + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public IBorderItemLocator createBorderItemLocator(IFigure figure, DDiagramElement vpElementBorderItem) { + if (AbstractNodeEvent.viewpointElementPredicate().apply(vpElementBorderItem)) { + return new ExecutionItemLocator(this, figure); + } else { + return super.createBorderItemLocator(figure, vpElementBorderItem); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void refreshVisuals() { + super.refreshVisuals(); + collapsedExecutionRefreshVisuals(); + } + + private void collapsedExecutionRefreshVisuals() { + DDiagramElement dde = resolveDiagramElement(); + IBorderItemLocator bil = getBorderItemLocator(); + + if (dde == null || !(bil instanceof ExecutionItemLocator)) { + return; + } + + // Modify locator constraint to collapse only the width. + if (new DDiagramElementQuery(dde).isIndirectlyCollapsed()) { + final int x = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getLocation_X())).intValue(); + final int y = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getLocation_Y())).intValue(); + final int width = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getSize_Width())).intValue(); + final int height = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getSize_Height())).intValue(); + + bil.setConstraint(new Rectangle(x, y, width, height)); + + if (bil instanceof BorderItemLocator) { + ((BorderItemLocator) bil).setBorderItemOffset(ICollapseMode.COLLAPSE_DEFAULT_OFFSET); + } + + if (getPrimaryFigure() != null) { + refreshFigure(); + getPrimaryFigure().repaint(); + } + } + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getISequenceEvent() { + return ISequenceElementAccessor.getExecution(getNotationView()).get(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/ISequenceEventEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/ISequenceEventEditPart.java new file mode 100644 index 0000000000..5dc151078a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/ISequenceEventEditPart.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; + +/** + * Common interface for all the elements of a sequence diagram which represent + * an event associated to a (logical) time interval and thus a range of vertical + * coordinates. This includes lifelines (considered as a special case of + * executions), executions and messages. + * + * @author pcdavid, smonnier, mporhel + */ +public interface ISequenceEventEditPart extends IDiagramElementEditPart { + /** + * Returns the {@link ISequenceEvent} this {@link ISequenceEventEditPart}. + * + * @return the {@link ISequenceEvent} this {@link ISequenceEventEditPart}. + */ + ISequenceEvent getISequenceEvent(); +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/InstanceRoleEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/InstanceRoleEditPart.java new file mode 100644 index 0000000000..4ff27b4b18 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/InstanceRoleEditPart.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.draw2d.ConnectionAnchor; +import org.eclipse.draw2d.IFigure; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gef.Request; +import org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeEditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.description.ExecutionMapping; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ConnectionAnchorOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.InstanceRoleResizableEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.InstanceRoleSiriusGraphicalNodeEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceLaunchToolEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.SouthCenteredBorderItemLocator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.LayoutEditPartConstants; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; + +/** + * The edit part for lifeline instance roles. + * + * @author pcdavid, smonnier + */ +public class InstanceRoleEditPart extends DNodeEditPart { + /** + * Constructor. + * + * @param view + * the view. + */ + public InstanceRoleEditPart(View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + public void addNotify() { + SequenceEditPartsOperations.registerDiagramElement(this, resolveDiagramElement()); + super.addNotify(); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeNotify() { + super.removeNotify(); + SequenceEditPartsOperations.unregisterDiagramElement(this, resolveDiagramElement()); + } + + /** + * {@inheritDoc} + */ + @Override + public ConnectionAnchor getTargetConnectionAnchor(Request request) { + ConnectionAnchor anchor = super.getTargetConnectionAnchor(request); + if (anchor != null && new RequestQuery(request).isSequenceMessageCreation()) { + anchor = ConnectionAnchorOperation.safeCenterAnchor(anchor); + } + return anchor; + } + + /** + * {@inheritDoc} + */ + @Override + public ConnectionAnchor getSourceConnectionAnchor(Request request) { + ConnectionAnchor anchor = super.getSourceConnectionAnchor(request); + if (anchor != null && new RequestQuery(request).isSequenceMessageCreation()) { + anchor = ConnectionAnchorOperation.safeCenterAnchor(anchor); + } + return anchor; + } + + /** + * This method has been overridden to use a specific BorderItemLocator to + * place the lifeline properly. + * <p> + * {@inheritDoc} + */ + @Override + public IBorderItemLocator createBorderItemLocator(IFigure figure, DDiagramElement vpElementBorderItem) { + IBorderItemLocator result; + if (vpElementBorderItem.getMapping() instanceof ExecutionMapping) { + result = new SouthCenteredBorderItemLocator(figure, LayoutEditPartConstants.ROOT_EXECUTION_BORDER_ITEM_OFFSET); + } else { + result = super.createBorderItemLocator(figure, vpElementBorderItem); + } + return result; + } + + /** + * Overridden to install a specific edit policy managing the moving and + * resizing requests on lifelines. + * <p> + * {@inheritDoc} + */ + @Override + public void installEditPolicy(Object key, EditPolicy editPolicy) { + if (EditPolicy.PRIMARY_DRAG_ROLE.equals(key)) { + super.installEditPolicy(key, new InstanceRoleResizableEditPolicy()); + } else { + super.installEditPolicy(key, editPolicy); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void createDefaultEditPolicies() { + super.createDefaultEditPolicies(); + installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new InstanceRoleSiriusGraphicalNodeEditPolicy()); + + // Handle $endBefore for launch tools. + installEditPolicy(org.eclipse.sirius.diagram.tools.api.requests.RequestConstants.REQ_LAUNCH_TOOL, new SequenceLaunchToolEditPolicy()); + } + + public InstanceRole getInstanceRole() { + return ISequenceElementAccessor.getInstanceRole(getNotationView()).get(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/InteractionUseEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/InteractionUseEditPart.java new file mode 100644 index 0000000000..925a31a8e4 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/InteractionUseEditPart.java @@ -0,0 +1,264 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import java.util.Iterator; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gmf.runtime.diagram.core.listener.DiagramEventBroker; +import org.eclipse.gmf.runtime.diagram.core.listener.NotificationListener; +import org.eclipse.gmf.runtime.gef.ui.figures.DefaultSizeNodeFigure; +import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.swt.SWT; + +import org.eclipse.sirius.common.tools.api.interpreter.EvaluationException; +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DNodeContainer; +import org.eclipse.sirius.FlatContainerStyle; +import org.eclipse.sirius.business.api.logger.RuntimeLoggerManager; +import org.eclipse.sirius.description.ContainerMapping; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeContainerEditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.description.DescriptionPackage; +import org.eclipse.sirius.diagram.sequence.description.FrameMapping; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.InteractionUseResizableEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceLaunchToolEditPolicy; +import org.eclipse.sirius.diagram.ui.tools.api.figure.SiriusWrapLabel; +import org.eclipse.sirius.tools.api.interpreter.InterpreterUtil; + +/** + * Special edit part for interaction uses. + * + * @author pcdavid, smonnier + * + */ +public class InteractionUseEditPart extends DNodeContainerEditPart implements ISequenceEventEditPart { + /** + * The centered label. + */ + private SiriusWrapLabel fExpressionLabelFigure; + + /** + * Post commit listener to refresh the centered label on targeted + * interaction change. + */ + private NotificationListener usedInteractionLabelUpdater = new NotificationListener() { + + public void notifyChanged(Notification notification) { + if (!notification.isTouch()) { + refreshUsedInteractionLabel(); + } + } + }; + + /** + * Constructor. + * + * @param view + * the view. + */ + public InteractionUseEditPart(View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + public void addNotify() { + SequenceEditPartsOperations.registerDiagramElement(this, resolveDiagramElement()); + super.addNotify(); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeNotify() { + super.removeNotify(); + SequenceEditPartsOperations.unregisterDiagramElement(this, resolveDiagramElement()); + } + + /** + * Overridden to install a specific edit policy managing the moving and + * resizing requests on lifelines. + * <p> + * {@inheritDoc} + */ + @Override + public void installEditPolicy(Object key, EditPolicy editPolicy) { + if (EditPolicy.PRIMARY_DRAG_ROLE.equals(key)) { + super.installEditPolicy(key, new InteractionUseResizableEditPolicy()); + } else { + super.installEditPolicy(key, editPolicy); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void createDefaultEditPolicies() { + super.createDefaultEditPolicies(); + + // Handle $endBefore for launch tools. + installEditPolicy(org.eclipse.sirius.diagram.tools.api.requests.RequestConstants.REQ_LAUNCH_TOOL, new SequenceLaunchToolEditPolicy()); + } + + /** + * Computes the center label expression. + * + * @return the center label expression + */ + protected String computeCenterLabelExpression() { + String centeredLabelText = ""; + if (resolveSemanticElement() instanceof DNodeContainer) { + DNodeContainer dNodeContainer = (DNodeContainer) resolveSemanticElement(); + ContainerMapping actualMapping = dNodeContainer.getActualMapping(); + if (actualMapping instanceof FrameMapping) { + FrameMapping frameMapping = (FrameMapping) actualMapping; + String centerLabelExpression = frameMapping.getCenterLabelExpression(); + EObject eObject = dNodeContainer.getTarget(); + try { + centeredLabelText = evaluationExpression(eObject, centerLabelExpression); + } catch (EvaluationException e) { + RuntimeLoggerManager.INSTANCE.error(frameMapping, DescriptionPackage.eINSTANCE.getFrameMapping_CenterLabelExpression(), e); + } + } + } + return centeredLabelText; + } + + private String evaluationExpression(EObject self, String expression) throws EvaluationException { + IInterpreter interpreter = null; + if (self != null) { + interpreter = InterpreterUtil.getInterpreter(self); + if (interpreter != null) { + return interpreter.evaluateString(self, expression); + } + } + return ""; + } + + /** + * Refresh the centered label text. + */ + private void refreshUsedInteractionLabel() { + if (fExpressionLabelFigure != null) { + fExpressionLabelFigure.setText(computeCenterLabelExpression()); + // FIXME smonnier does not work on diagram opening + // ArrayList<AbstractDiagramNameEditPart> nameEditPartList = + // Lists.newArrayList(Iterables.filter(getChildren(), + // AbstractDiagramNameEditPart.class)); + // if (nameEditPartList.size() == 1) { + // AbstractDiagramNameEditPart nameEditPart = + // nameEditPartList.get(0); + // if (nameEditPart.getFigure() instanceof WrapLabel) { + // WrapLabel titleBlock = (WrapLabel) nameEditPart.getFigure(); + // fExpressionLabelFigure.setForegroundColor(titleBlock.getForegroundColor()); + // fExpressionLabelFigure.setFont(titleBlock.getFont()); + // } + // } + } + } + + /** + * {@inheritDoc} + * + * Overridden to add a post commit notification listener on all semantic + * elements for the centered label refresh. + */ + @Override + public void activate() { + super.activate(); + + DiagramEventBroker broker = null; + final TransactionalEditingDomain theEditingDomain = getEditingDomain(); + if (theEditingDomain != null) { + broker = DiagramEventBroker.getInstance(theEditingDomain); + } + + if (broker != null) { + final Iterator<EObject> iterSemanticElements = resolveAllSemanticElements().iterator(); + while (iterSemanticElements.hasNext()) { + final EObject semantic = iterSemanticElements.next(); + broker.addNotificationListener(semantic, usedInteractionLabelUpdater); + } + } + } + + /** + * {@inheritDoc} + * + * Overridden to remove the post commit notification listener on all + * semantic elements for the centered label refresh. + */ + @Override + public void deactivate() { + super.deactivate(); + + DiagramEventBroker broker = null; + final TransactionalEditingDomain theEditingDomain = getEditingDomain(); + if (theEditingDomain != null) { + broker = DiagramEventBroker.getInstance(theEditingDomain); + } + + if (broker != null) { + final Iterator<EObject> iterSemanticElements = resolveAllSemanticElements().iterator(); + while (iterSemanticElements.hasNext()) { + final EObject semantic = iterSemanticElements.next(); + broker.removeNotificationListener(semantic, usedInteractionLabelUpdater); + } + } + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getISequenceEvent() { + return ISequenceElementAccessor.getInteractionUse(getNotationView()).get(); + } + + /** + * {@inheritDoc} + */ + @Override + protected NodeFigure createMainFigure() { + NodeFigure figure = super.createMainFigure(); + + if (figure instanceof DefaultSizeNodeFigure) { + final EObject eObj = this.resolveSemanticElement(); + if (eObj instanceof DDiagramElementContainer) { + final DDiagramElementContainer container = (DDiagramElementContainer) eObj; + if (container.getOwnedStyle() instanceof FlatContainerStyle) { + ((DefaultSizeNodeFigure) figure).setDefaultSize(LayoutConstants.DEFAULT_INTERACTION_USE_WIDTH, LayoutConstants.DEFAULT_INTERACTION_USE_HEIGHT); + } + } + } + + // Add a centered label on the figure + fExpressionLabelFigure = new SiriusWrapLabel(); + refreshUsedInteractionLabel(); + fExpressionLabelFigure.setTextWrap(true); + fExpressionLabelFigure.setLabelAlignment(SWT.CENTER); + figure.add(fExpressionLabelFigure); + + return figure; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/LifelineEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/LifelineEditPart.java new file mode 100644 index 0000000000..65b7ef6099 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/LifelineEditPart.java @@ -0,0 +1,265 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import java.util.List; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.LineBorder; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.DragTracker; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.EditPartViewer; +import org.eclipse.gef.Request; +import org.eclipse.gef.requests.SelectionRequest; +import org.eclipse.gmf.runtime.diagram.ui.tools.DragEditPartsTrackerEx; +import org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator; +import org.eclipse.gmf.runtime.gef.ui.figures.DefaultSizeNodeFigure; +import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; + +import org.eclipse.sirius.BorderedStyle; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DStylizable; +import org.eclipse.sirius.Style; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.description.EndOfLifeMapping; +import org.eclipse.sirius.diagram.sequence.description.ExecutionMapping; +import org.eclipse.sirius.diagram.sequence.description.StateMapping; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.ExecutionItemLocator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.LifelineNodeFigure; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.SouthCenteredBorderItemLocator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.LayoutEditPartConstants; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IStyleConfigurationRegistry; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.StyleConfiguration; +import org.eclipse.sirius.diagram.ui.tools.api.figure.anchor.AnchorProvider; +import org.eclipse.sirius.ui.tools.api.color.VisualBindingManager; + +/** + * Special edit part for Executions. They are treated as bordered nodes. + * + * @author pcdavid, smonnier + */ +public class LifelineEditPart extends AbstractSequenceBorderedEditPart { + private static final boolean ENABLE_LIFELINE_SELECTION = false; + + /** + * Constructor. + * + * @param view + * the view. + */ + public LifelineEditPart(final View view) { + super(view); + } + + /** + * This method is overridden to have the Lifeline (bordered node) starting + * from the border of the Instance Role. + * <p> + * {@inheritDoc} + */ + @Override + public void refreshVisuals() { + updateLifelineWidthAndColor(); + + super.refreshVisuals(); + SequenceEditPartsOperations.setBorderItemLocation(this, PositionConstants.SOUTH, LayoutEditPartConstants.ROOT_EXECUTION_BORDER_ITEM_OFFSET); + } + + private void updateLifelineWidthAndColor() { + DDiagramElement dde = this.resolveDiagramElement(); + Style style = dde.getStyle(); + if (style instanceof BorderedStyle) { + BorderedStyle borderedStyle = (BorderedStyle) style; + int borderSize = borderedStyle.getBorderSize().intValue(); + Color fg = VisualBindingManager.getDefault().getColorFromRGBValues(borderedStyle.getBorderColor()); + + // Update LifelineNodeFigure + nodePlate.setLineWidth(borderSize); + if (fg != null) { + nodePlate.setForegroundColor(fg); + } + + // Update its border. + if (nodePlate.getBorder() instanceof LineBorder) { + LineBorder lineBorder = (LineBorder) nodePlate.getBorder(); + lineBorder.setWidth(borderSize); + if (fg != null) { + lineBorder.setColor(fg); + } + } + } + } + + /** + * This method has been overridden to be able to select the parent + * InstanceRoleEditPart when selecting this LifelineEditPart. + * <p> + * {@inheritDoc} + */ + @Override + public void setSelected(int value) { + if (ENABLE_LIFELINE_SELECTION) { + super.setSelected(value); + } else { + getParent().setSelected(value); + } + } + + /** + * This method has been overridden to use a specific Tracker to be able to + * select the InstanceRole and group requests. + * <p> + * {@inheritDoc} + */ + @Override + public DragTracker getDragTracker(Request request) { + if (!ENABLE_LIFELINE_SELECTION) { + if (request instanceof SelectionRequest) { + return new LifeLineSelectionDragEditPartsTrackerEx(this); + } + } + return super.getDragTracker(request); + } + + /** + * This method has been overridden to use a specific BorderItemLocator to + * place the Destroy end item properly. + * <p> + * {@inheritDoc} + */ + @Override + public IBorderItemLocator createBorderItemLocator(IFigure figure, DDiagramElement vpElementBorderItem) { + IBorderItemLocator result; + if (vpElementBorderItem.getMapping() instanceof EndOfLifeMapping) { + result = new SouthCenteredBorderItemLocator(figure, LayoutEditPartConstants.EOL_BORDER_ITEM_OFFSET); + } else if (vpElementBorderItem.getMapping() instanceof ExecutionMapping || vpElementBorderItem.getMapping() instanceof StateMapping) { + result = new ExecutionItemLocator(this, figure); + } else { + result = super.createBorderItemLocator(figure, vpElementBorderItem); + } + return result; + } + + /** + * This method is overridden to use a specific figure for this border node. + * + * {@inheritDoc} + */ + @Override + protected NodeFigure createNodePlate() { + DefaultSizeNodeFigure result = null; + final EObject eObj = resolveSemanticElement(); + if (eObj instanceof DStylizable && eObj instanceof DDiagramElement) { + final DStylizable viewNode = (DStylizable) eObj; + final StyleConfiguration styleConfiguration = IStyleConfigurationRegistry.INSTANCE.getStyleConfiguration(((DDiagramElement) eObj).getDiagramElementMapping(), viewNode.getStyle()); + final AnchorProvider anchorProvider = styleConfiguration.getAnchorProvider(); + result = new LifelineNodeFigure(getMapMode().DPtoLP(5), getMapMode().DPtoLP(5), anchorProvider); + nodePlate = result; + } + return result; + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getISequenceEvent() { + return ISequenceElementAccessor.getLifeline(getNotationView()).get(); + } + + /** + * Specific tracker used to select the parent InstanceRole instead of the + * RootExecution and append request on RootExecution to InstanceRole. + * + * @author smonnier + */ + private final class LifeLineSelectionDragEditPartsTrackerEx extends DragEditPartsTrackerEx { + + /** + * Constructor. + * + * @param sourceEditPart + * the execution edit part + */ + public LifeLineSelectionDragEditPartsTrackerEx(EditPart sourceEditPart) { + super(sourceEditPart); + } + + /** + * This method has been overridden to be able to manipulate the parent + * InstanceRoleEditPart as well as this LifelineEditPart. For instance, + * moving the LifelineEditPart will also move the parent + * InstanceRoleEditPart. + * <p> + * {@inheritDoc} + */ + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected List createOperationSet() { + List createOperationSet = super.createOperationSet(); + if (createOperationSet.contains(LifelineEditPart.this)) { + if (getParent() instanceof InstanceRoleEditPart) { + createOperationSet.add(getParent()); + } + } + return createOperationSet; + } + + /** + * This method has been overridden to be able to select the parent + * InstanceRoleEditPart when selecting this LifelineEditPart. + * <p> + * {@inheritDoc} + */ + @Override + protected void performSelection() { + if (hasSelectionOccurred()) { + return; + } + setFlag(FLAG_SELECTION_PERFORMED, true); + EditPartViewer viewer = getCurrentViewer(); + List<?> selectedObjects = viewer.getSelectedEditParts(); + + if (getCurrentInput().isModKeyDown(SWT.MOD1)) { + if (selectedObjects.contains(getSourceEditPart().getParent())) { + viewer.deselect(getSourceEditPart()); + } else { + viewer.appendSelection(getSourceEditPart().getParent()); + } + } else if (getCurrentInput().isShiftKeyDown()) { + viewer.appendSelection(getSourceEditPart().getParent()); + } else { + viewer.select(getSourceEditPart().getParent()); + } + } + + /** + * Always disable the clone with Ctrl key in Sirius because it only + * clone the graphical element and not the semantic element. + * + * @param cloneActive + * true if cloning should be active (never considered here) + * + * @see org.eclipse.gef.tools.DragEditPartsTracker#setCloneActive(boolean) + */ + @Override + protected void setCloneActive(boolean cloneActive) { + super.setCloneActive(false); + } + + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/LostMessageEndEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/LostMessageEndEditPart.java new file mode 100644 index 0000000000..25d9770c64 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/LostMessageEndEditPart.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.draw2d.ConnectionAnchor; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gef.Request; +import org.eclipse.gef.editpolicies.ResizableEditPolicy; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.diagram.edit.internal.part.DiagramNodeEditPartOperation; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeEditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.LostMessageEnd; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ConnectionAnchorOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.LostMessageEndSelectionPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceLaunchToolEditPolicy; + +/** + * The edit part for lost message end. + * + * @author mporhel + */ +public class LostMessageEndEditPart extends DNodeEditPart { + /** + * Constructor. + * + * @param view + * the view. + */ + public LostMessageEndEditPart(View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + public void addNotify() { + SequenceEditPartsOperations.registerDiagramElement(this, resolveDiagramElement()); + super.addNotify(); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeNotify() { + super.removeNotify(); + SequenceEditPartsOperations.unregisterDiagramElement(this, resolveDiagramElement()); + } + + /** + * Use the same y location as the corresponding source connection anchor, + * stored in ViewLocationHint, to improve user feedback. + * <p> + * {@inheritDoc} + */ + @Override + public ConnectionAnchor getSourceConnectionAnchor(Request request) { + return ConnectionAnchorOperation.safeCenterAnchor(super.getSourceConnectionAnchor(request)); + } + + /** + * Use the same y location as the corresponding source connection anchor, + * stored in ViewLocationHint, to improve user feedback. + * <p> + * {@inheritDoc} + */ + @Override + public ConnectionAnchor getTargetConnectionAnchor(Request request) { + return ConnectionAnchorOperation.safeCenterAnchor(super.getTargetConnectionAnchor(request)); + } + + /** + * {@inheritDoc} + */ + @Override + public EditPolicy getPrimaryDragEditPolicy() { + ResizableEditPolicy result = new LostMessageEndSelectionPolicy(); + DDiagramElement dde = resolveDiagramElement(); + if (dde instanceof DNode) { + DNode node = (DNode) dde; + DiagramNodeEditPartOperation.updateResizeKind(result, node); + } + return result; + } + + /** + * Overridden to install a specific edit policy managing the moving and + * resizing requests on lost message ends. + * <p> + * {@inheritDoc} + */ + @Override + public void installEditPolicy(Object key, EditPolicy editPolicy) { + if (EditPolicy.PRIMARY_DRAG_ROLE.equals(key)) { + super.installEditPolicy(key, getPrimaryDragEditPolicy()); + } else { + super.installEditPolicy(key, editPolicy); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void createDefaultEditPolicies() { + super.createDefaultEditPolicies(); + + // Handle $endBefore for launch tools. + installEditPolicy(org.eclipse.sirius.diagram.tools.api.requests.RequestConstants.REQ_LAUNCH_TOOL, new SequenceLaunchToolEditPolicy()); + } + + public LostMessageEnd getLostMessageEnd() { + return ISequenceElementAccessor.getLostMessageEnd(getNotationView()).get(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/ObservationPointEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/ObservationPointEditPart.java new file mode 100644 index 0000000000..638ff29889 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/ObservationPointEditPart.java @@ -0,0 +1,282 @@ +/******************************************************************************* + * Copyright (c) 2012, 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.draw2d.ConnectionAnchor; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gef.GraphicalEditPart; +import org.eclipse.gef.Request; +import org.eclipse.gef.editpolicies.ResizableEditPolicy; +import org.eclipse.gmf.runtime.gef.ui.figures.DefaultSizeNodeFigure; +import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.CollapseFilter; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DStylizable; +import org.eclipse.sirius.SiriusPackage; +import org.eclipse.sirius.business.api.query.DDiagramElementQuery; +import org.eclipse.sirius.diagram.edit.internal.part.DiagramBorderNodeEditPartOperation; +import org.eclipse.sirius.diagram.edit.internal.part.DiagramNodeEditPartOperation; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeEditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ObservationPoint; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ConnectionAnchorOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.ObservationPointSelectionPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceLaunchToolEditPolicy; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IStyleConfigurationRegistry; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.StyleConfiguration; +import org.eclipse.sirius.diagram.ui.tools.api.figure.AirDefaultSizeNodeFigure; +import org.eclipse.sirius.diagram.ui.tools.api.figure.anchor.AnchorProvider; + +/** + * The edit part for {@link ObservationPoint}s. + * + * @author mporhel + */ +public class ObservationPointEditPart extends DNodeEditPart { + + private DefaultSizeNodeFigure nodePlate; + + /** + * Constructor. + * + * @param view + * the view. + */ + public ObservationPointEditPart(View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + public void addNotify() { + SequenceEditPartsOperations.registerDiagramElement(this, resolveDiagramElement()); + super.addNotify(); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeNotify() { + super.removeNotify(); + SequenceEditPartsOperations.unregisterDiagramElement(this, resolveDiagramElement()); + } + + /** + * Use the same y location as the corresponding source connection anchor, + * stored in ViewLocationHint, to improve user feedback. + * <p> + * {@inheritDoc} + */ + @Override + public ConnectionAnchor getSourceConnectionAnchor(Request request) { + return ConnectionAnchorOperation.safeCenterAnchor(super.getSourceConnectionAnchor(request)); + } + + /** + * Use the same y location as the corresponding source connection anchor, + * stored in ViewLocationHint, to improve user feedback. + * <p> + * {@inheritDoc} + */ + @Override + public ConnectionAnchor getTargetConnectionAnchor(Request request) { + return ConnectionAnchorOperation.safeCenterAnchor(super.getTargetConnectionAnchor(request)); + } + + /** + * {@inheritDoc} + */ + @Override + public EditPolicy getPrimaryDragEditPolicy() { + ResizableEditPolicy result = new ObservationPointSelectionPolicy(); + DDiagramElement dde = resolveDiagramElement(); + if (dde instanceof DNode) { + DNode node = (DNode) dde; + DiagramNodeEditPartOperation.updateResizeKind(result, node); + } + return result; + } + + /** + * Overridden to install a specific edit policy managing the moving and + * resizing requests on lost message ends. + * <p> + * {@inheritDoc} + */ + @Override + public void installEditPolicy(Object key, EditPolicy editPolicy) { + if (EditPolicy.PRIMARY_DRAG_ROLE.equals(key)) { + super.installEditPolicy(key, getPrimaryDragEditPolicy()); + } else { + super.installEditPolicy(key, editPolicy); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void createDefaultEditPolicies() { + super.createDefaultEditPolicies(); + + // Handle $endBefore for launch tools. + installEditPolicy(org.eclipse.sirius.diagram.tools.api.requests.RequestConstants.REQ_LAUNCH_TOOL, new SequenceLaunchToolEditPolicy()); + } + + public ObservationPoint getObservationPoint() { + return ISequenceElementAccessor.getObservationPoint(getNotationView()).get(); + } + + @Override + protected void handleNotificationEvent(Notification notification) { + super.handleNotificationEvent(notification); + + if (SiriusPackage.eINSTANCE.getDDiagramElement_GraphicalFilters().equals(notification.getFeature())) { + boolean collapse = notification.getNewValue() instanceof CollapseFilter && notification.getOldValue() == null; + boolean uncollapse = notification.getOldValue() instanceof CollapseFilter && notification.getNewValue() == null; + if (collapse || uncollapse) { + DDiagramElement dde = resolveDiagramElement(); + if (dde instanceof DNode) { + DNode node = (DNode) dde; + + EditPolicy ep = getEditPolicy(EditPolicy.PRIMARY_DRAG_ROLE); + if (ep instanceof ResizableEditPolicy) { + DiagramBorderNodeEditPartOperation.updateResizeKind((ResizableEditPolicy) ep, node); + } + } + } + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void refreshVisuals() { + super.refreshVisuals(); + + DDiagramElement node = resolveDiagramElement(); + if (new DDiagramElementQuery(node).isCollapsed()) { + final int x = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getLocation_X())).intValue(); + final int y = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getLocation_Y())).intValue(); + final int width = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getSize_Width())).intValue(); + final int height = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getSize_Height())).intValue(); + + Rectangle collpasedSize = new Rectangle(x, y, width, height); + ((GraphicalEditPart) getParent()).setLayoutConstraint(this, getFigure(), collpasedSize); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramBorderNodeEditPart#activate() + */ + @Override + public void activate() { + if (nodePlate instanceof AirDefaultSizeNodeFigure) + ((AirDefaultSizeNodeFigure) nodePlate).setZoomManager(getZoomManager()); + super.activate(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramBorderNodeEditPart#deactivate() + */ + @Override + public void deactivate() { + super.deactivate(); + if (nodePlate instanceof AirDefaultSizeNodeFigure) + ((AirDefaultSizeNodeFigure) nodePlate).setZoomManager(null); + } + + /** + * {@inheritDoc} + */ + @Override + public IFigure getNodePlate() { + return nodePlate; + } + + /** + * {@inheritDoc} + */ + @Override + protected NodeFigure createNodePlate() { + DefaultSizeNodeFigure result = new ObservationPointFigure(10, 10, null); + final EObject eObj = resolveSemanticElement(); + if (eObj instanceof DStylizable && eObj instanceof DDiagramElement) { + final DStylizable viewNode = (DStylizable) eObj; + final StyleConfiguration styleConfiguration = IStyleConfigurationRegistry.INSTANCE.getStyleConfiguration(((DDiagramElement) eObj).getDiagramElementMapping(), viewNode.getStyle()); + final AnchorProvider anchorProvider = styleConfiguration.getAnchorProvider(); + result = new ObservationPointFigure(getMapMode().DPtoLP(5), getMapMode().DPtoLP(5), anchorProvider); + nodePlate = result; + } + return result; + } + + /** + * ObservationPoint is a top level node but collapsing should be possible. + * + * @author mporhel + */ + private class ObservationPointFigure extends AirDefaultSizeNodeFigure { + /** + * Create a new {@link AirDefaultSizeNodeFigure}. + * + * @param defSize + * the size. + * @param anchorProvider + * the anchor provider. + */ + public ObservationPointFigure(final Dimension defSize, final AnchorProvider anchorProvider) { + super(defSize, anchorProvider); + } + + /** + * Create a new {@link AirDefaultSizeNodeFigure}. + * + * @param width + * the width. + * @param height + * the height. + * @param anchorProvider + * the anchor provider. + */ + public ObservationPointFigure(final int width, final int height, final AnchorProvider anchorProvider) { + super(width, height, anchorProvider); + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension getMinimumSize(int hint, int hint2) { + return new Dimension(0, 0); + } + + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/OperandCompartmentEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/OperandCompartmentEditPart.java new file mode 100644 index 0000000000..d745c58b5e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/OperandCompartmentEditPart.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.gef.EditPolicy; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.diagram.graphical.edit.policies.NodeCreationEditPolicy; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeContainerViewNodeContainerCompartment2EditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ExecutionOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceNodeCreationPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceLaunchToolEditPolicy; + +/** + * Custom edit part to customize what happens inside an operand. + * + * @author pcdavid + */ +public class OperandCompartmentEditPart extends DNodeContainerViewNodeContainerCompartment2EditPart { + /** + * The visual ID. Same as a normal container compartment. + * + * @see DNodeContainerViewNodeContainerCompartment2EditPart.VISUAL_ID + */ + public static final int VISUAL_ID = 7002; + + /** + * Constructor. + * + * @param view + * the view <code>controlled</code> by this editpart. + */ + public OperandCompartmentEditPart(View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + protected void createDefaultEditPolicies() { + super.createDefaultEditPolicies(); + ExecutionOperations.replaceEditPolicy(this, EditPolicy.CONTAINER_ROLE, new SequenceNodeCreationPolicy(), NodeCreationEditPolicy.class); + + // Handle $endBefore for launch tools. + installEditPolicy(org.eclipse.sirius.diagram.tools.api.requests.RequestConstants.REQ_LAUNCH_TOOL, new SequenceLaunchToolEditPolicy()); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/OperandEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/OperandEditPart.java new file mode 100644 index 0000000000..8131cd4f89 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/OperandEditPart.java @@ -0,0 +1,183 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gmf.runtime.gef.ui.figures.DefaultSizeNodeFigure; +import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DStylizable; +import org.eclipse.sirius.FlatContainerStyle; +import org.eclipse.sirius.diagram.edit.internal.part.DiagramContainerEditPartOperation; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeContainer2EditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.OperandResizableEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceLaunchToolEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.OperandFigure; + +/** + * Special edit part for operands inside combined fragments. + * + * @author pcdavid + */ +public class OperandEditPart extends DNodeContainer2EditPart implements ISequenceEventEditPart { + /** + * Constructor. + * + * @param view + * the view. + */ + public OperandEditPart(View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + public void addNotify() { + SequenceEditPartsOperations.registerDiagramElement(this, resolveDiagramElement()); + super.addNotify(); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeNotify() { + super.removeNotify(); + SequenceEditPartsOperations.unregisterDiagramElement(this, resolveDiagramElement()); + } + + /** + * Overridden to install a specific edit policy managing the moving and + * resizing requests on operands. + * <p> + * {@inheritDoc} + */ + @Override + public void installEditPolicy(Object key, EditPolicy editPolicy) { + if (EditPolicy.PRIMARY_DRAG_ROLE.equals(key)) { + super.installEditPolicy(key, new OperandResizableEditPolicy()); + } else { + super.installEditPolicy(key, editPolicy); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void createDefaultEditPolicies() { + super.createDefaultEditPolicies(); + + // Handle $endBefore for launch tools. + installEditPolicy(org.eclipse.sirius.diagram.tools.api.requests.RequestConstants.REQ_LAUNCH_TOOL, new SequenceLaunchToolEditPolicy()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addDropShadow(NodeFigure figure, IFigure shape) { + // Removes the shadow border to have operands border overlapping + // combined fragment border + figure.setBorder(null); + } + + /** + * {@inheritDoc} + */ + @Override + protected NodeFigure createNodePlate() { + NodeFigure nodePlate = super.createNodePlate(); + + final EObject eObj = this.resolveSemanticElement(); + if (nodePlate instanceof DefaultSizeNodeFigure && eObj instanceof DDiagramElementContainer) { + final DDiagramElementContainer container = (DDiagramElementContainer) eObj; + if (container.getOwnedStyle() instanceof FlatContainerStyle) { + ((DefaultSizeNodeFigure) nodePlate).setDefaultSize(LayoutConstants.DEFAULT_OPERAND_WIDTH, LayoutConstants.DEFAULT_OPERAND_HEIGHT); + } + } + + return nodePlate; + } + + /** + * {@inheritDoc} + */ + @Override + protected NodeFigure createMainFigure() { + NodeFigure figure = super.createMainFigure(); + + return figure; + } + + /** + * {@inheritDoc} + * + * Overridden to create our own figure without border but only a bottom dash + * line. + */ + @Override + protected IFigure createNodeShape() { + // We recover the combined fragment to have the operand separator with + // the same style as the combined fragment border + Operand operand = ISequenceElementAccessor.getOperand(getNotationView()).get(); + final EObject eObj = operand.getCombinedFragment().getNotationNode().getElement(); + if (eObj instanceof DStylizable && eObj instanceof DDiagramElement) { + return new OperandFigure(DiagramContainerEditPartOperation.getCornerDimension(this), DiagramContainerEditPartOperation.getBackgroundStyle(this), operand); + } else { + return super.createNodeShape(); + } + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getISequenceEvent() { + return ISequenceElementAccessor.getOperand(getNotationView()).get(); + } + + /** + * Overridden. + * + * {@inheritDoc} + */ + @Override + protected void refreshVisuals() { + if (getParent() != null) { + super.refreshVisuals(); + } + } + + /** + * Finds the parent {@link CombinedFragmentEditPart}. + * + * @return the parent {@link CombinedFragmentEditPart} + */ + public CombinedFragmentEditPart getParentCombinedFragmentEditPart() { + Preconditions.checkArgument(getParent() instanceof CombinedFragmentCompartmentEditPart); + Preconditions.checkArgument(getParent().getParent() instanceof CombinedFragmentEditPart); + return (CombinedFragmentEditPart) getParent().getParent(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/SequenceBracketEdgeEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/SequenceBracketEdgeEditPart.java new file mode 100644 index 0000000000..3182f568e2 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/SequenceBracketEdgeEditPart.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import java.util.List; + +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.business.internal.bracket.BracketRelativeBendpoint; +import org.eclipse.sirius.diagram.business.internal.bracket.Direction; +import org.eclipse.sirius.diagram.graphical.edit.part.specific.BracketEdgeEditPart; + +/** + * A custom bracket edit part to force default bracket direction. + * + * @author mporhel + */ +public class SequenceBracketEdgeEditPart extends BracketEdgeEditPart { + + /** + * Default constructor. + * + * @param view + * the underlying {@link View}. + */ + public SequenceBracketEdgeEditPart(View view) { + super(view); + } + + /** + * Get default draw2d bendpoints. + * + * @return default draw2d bendpoints + */ + @Override + protected List<BracketRelativeBendpoint> getDefaultFigureConstraint() { + final List<BracketRelativeBendpoint> defaultFigureConstraint = Lists.newArrayList(); + defaultFigureConstraint.add(new BracketRelativeBendpoint(getConnectionFigure(), Direction.LEFT.ordinal(), Direction.LEFT.ordinal(), 50)); + return defaultFigureConstraint; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/SequenceDiagramEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/SequenceDiagramEditPart.java new file mode 100644 index 0000000000..74a97a4b6f --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/SequenceDiagramEditPart.java @@ -0,0 +1,252 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.transaction.ResourceSetListener; +import org.eclipse.emf.transaction.ResourceSetListenerImpl; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.business.api.dialect.DialectManager; +import org.eclipse.sirius.business.api.session.ModelChangeTrigger; +import org.eclipse.sirius.business.api.session.SessionEventBroker; +import org.eclipse.sirius.diagram.graphical.edit.policies.ContainerCreationEditPolicy; +import org.eclipse.sirius.diagram.internal.edit.parts.DDiagramEditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.refresh.RefreshLayoutCommand; +import org.eclipse.sirius.diagram.sequence.business.internal.refresh.RefreshLayoutScope; +import org.eclipse.sirius.diagram.sequence.business.internal.refresh.RefreshLayoutTrigger; +import org.eclipse.sirius.diagram.sequence.business.internal.refresh.SequenceCanonicalSynchronizerAdapter; +import org.eclipse.sirius.diagram.sequence.business.internal.refresh.SequenceCanonicalSynchronizerAdapterScope; +import org.eclipse.sirius.diagram.sequence.ui.business.internal.refresh.VisibilityEventHandler; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.command.SequenceEMFCommandFactory; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ExecutionOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceContainerCreationPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceLaunchToolEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.SequenceZOrderingRefresher; +import org.eclipse.sirius.diagram.tools.api.editor.DDiagramEditor; +import org.eclipse.sirius.diagram.tools.api.properties.PropertiesService; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactory; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactoryProvider; +import org.eclipse.sirius.tools.api.ui.property.IPropertiesProvider; + +/** + * The edit part for sequence diagrams themselves. + * + * @author pcdavid + */ +public class SequenceDiagramEditPart extends DDiagramEditPart { + /** + * The listener in charge with refreshing the graphical ordering and layout + * when the model and/or graphics positions change. + */ + private final VisibilityEventHandler semanticOrderingSynchronizer; + + private IDiagramCommandFactoryProvider previousProvider; + + private ModelChangeTrigger refreshLayout; + + private ModelChangeTrigger sequenceCanonicalSynchronizer; + + private ResourceSetListener refreshZorder = new ResourceSetListenerImpl() { + @Override + public boolean isPostcommitOnly() { + return true; + } + + @Override + public void resourceSetChanged(org.eclipse.emf.transaction.ResourceSetChangeEvent event) { + new SequenceZOrderingRefresher(SequenceDiagramEditPart.this).run(); + refreshConnectionsBendpoints(); + } + }; + + /** + * Constructor. + * + * @param diagramView + * the view. + */ + public SequenceDiagramEditPart(final View diagramView) { + super(diagramView); + this.semanticOrderingSynchronizer = new VisibilityEventHandler(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void createDefaultEditPolicies() { + super.createDefaultEditPolicies(); + ExecutionOperations.replaceEditPolicy(this, EditPolicy.CONTAINER_ROLE, new SequenceContainerCreationPolicy(), ContainerCreationEditPolicy.class); + + // Handle $endBefore for launch tools. + installEditPolicy(org.eclipse.sirius.diagram.tools.api.requests.RequestConstants.REQ_LAUNCH_TOOL, new SequenceLaunchToolEditPolicy()); + } + + /** + * {@inheritDoc} + */ + @Override + public void addNotify() { + SequenceEditPartsOperations.registerDiagramElement(this, resolveSemanticElement()); + super.addNotify(); + Object property = getViewer().getProperty(DDiagramEditor.EDITOR_ID); + if (property instanceof DDiagramEditor) { + DDiagramEditor diagramEditor = (DDiagramEditor) property; + setCustomCommandFactoryProvider(diagramEditor); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void removeNotify() { + super.removeNotify(); + SequenceEditPartsOperations.unregisterDiagramElement(this, resolveSemanticElement()); + Object property = getViewer().getProperty(DDiagramEditor.EDITOR_ID); + if (property instanceof DDiagramEditor && this.previousProvider != null) { + DDiagramEditor diagramEditor = (DDiagramEditor) property; + diagramEditor.setEmfCommandFactoryProvider(this.previousProvider); + this.previousProvider = null; + } + } + + /** + * Overridden to do some initialization and add a post-commit listener. + * <p> + * {@inheritDoc} + */ + @Override + public void activate() { + super.activate(); + /* + * Once the diagram (and all its children) is active, refresh the + * various ordering. This is especially needed when creating/opening a + * diagram as some commands need a properly initialized graphically + * ordering to work. + */ + boolean autoRefresh = PropertiesService.getInstance().getPropertiesProvider().getProperty(IPropertiesProvider.KEY_AUTO_REFRESH); + boolean refreshOnOpen = DialectManager.INSTANCE.isRefreshActivatedOnRepresentationOpening(); + getEditingDomain().getCommandStack().execute(new RefreshLayoutCommand(getEditingDomain(), getDiagramView(), autoRefresh || refreshOnOpen)); + getEditingDomain().addResourceSetListener(semanticOrderingSynchronizer); + getEditingDomain().addResourceSetListener(refreshZorder); + + Option<SessionEventBroker> broker = getSessionBroker(); + if (broker.some()) { + SessionEventBroker sessionEventBroker = broker.get(); + + Predicate<Notification> refreshLayoutScope = new RefreshLayoutScope(); + refreshLayout = new RefreshLayoutTrigger(getDiagramView()); + sessionEventBroker.addLocalTrigger(refreshLayoutScope, refreshLayout); + + Predicate<Notification> sequenceCanonicalSynchronizerLayoutScope = new SequenceCanonicalSynchronizerAdapterScope(); + sequenceCanonicalSynchronizer = new SequenceCanonicalSynchronizerAdapter(); + sessionEventBroker.addLocalTrigger(sequenceCanonicalSynchronizerLayoutScope, sequenceCanonicalSynchronizer); + } + } + + private Option<SessionEventBroker> getSessionBroker() { + DDiagramEditor diagramEditor = (DDiagramEditor) this.getViewer().getProperty(DDiagramEditor.EDITOR_ID); + if (diagramEditor != null) { + return Options.newSome(diagramEditor.getSession().getEventBroker()); + } + return Options.newNone(); + } + + private void setCustomCommandFactoryProvider(DDiagramEditor diagramEditor) { + previousProvider = diagramEditor.getEmfCommandFactoryProvider(); + diagramEditor.setEmfCommandFactoryProvider(new IDiagramCommandFactoryProvider() { + + /** the EMF command factory */ + private IDiagramCommandFactory commandFactory; + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.tools.api.command.IDiagramCommandFactoryProvider#getCommandFactory(org.eclipse.emf.transaction.TransactionalEditingDomain) + */ + public IDiagramCommandFactory getCommandFactory(final TransactionalEditingDomain editingDomain) { + if (commandFactory == null) { + commandFactory = new SequenceEMFCommandFactory(SequenceDiagramEditPart.this); + } + return commandFactory; + } + }); + } + + /** + * Overridden to remove the post-commit listener and other initializations. + * <p> + * {@inheritDoc} + */ + @Override + public void deactivate() { + getEditingDomain().removeResourceSetListener(refreshZorder); + getEditingDomain().removeResourceSetListener(semanticOrderingSynchronizer); + Option<SessionEventBroker> broker = getSessionBroker(); + if (broker.some()) { + SessionEventBroker sessionEventBroker = broker.get(); + sessionEventBroker.removeLocalTrigger(refreshLayout); + sessionEventBroker.removeLocalTrigger(sequenceCanonicalSynchronizer); + } + super.deactivate(); + } + + /** + * {@inheritDoc} + * + * Overridden to be accessible from {@link SequenceZOrderingRefresher}. + */ + @Override + public void reorderChild(EditPart child, int index) { + super.reorderChild(child, index); + } + + /** + * {@inheritDoc} + */ + @Override + protected void refreshChildren() { + super.refreshChildren(); + refreshConnectionsBendpoints(); + new SequenceZOrderingRefresher(this).run(); + } + + /** + * Refresh the bendpoints of source & target connections. + */ + protected void refreshConnectionsBendpoints() { + for (SequenceMessageEditPart connectionEditPart : Iterables.filter(getConnections(), SequenceMessageEditPart.class)) { + connectionEditPart.refreshBendpoints(); + } + } + + /** + * Returns the underlying sequence diagram element. + * + * @return the underlying sequence diagram element. + */ + public SequenceDiagram getSequenceDiagram() { + return ISequenceElementAccessor.getSequenceDiagram(getDiagramView()).get(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/SequenceMessageEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/SequenceMessageEditPart.java new file mode 100644 index 0000000000..27c483c323 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/SequenceMessageEditPart.java @@ -0,0 +1,220 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.draw2d.Connection; +import org.eclipse.draw2d.ConnectionRouter; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.gef.DragTracker; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gef.Request; +import org.eclipse.gef.requests.ReconnectRequest; +import org.eclipse.gef.requests.SelectionRequest; +import org.eclipse.gmf.runtime.diagram.ui.figures.LabelLocator; +import org.eclipse.gmf.runtime.diagram.ui.util.EditPartUtil; +import org.eclipse.gmf.runtime.notation.RelativeBendpoints; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.gmf.runtime.notation.datatype.RelativeBendpoint; + +import org.eclipse.sirius.diagram.internal.edit.parts.DEdgeEditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceLaunchToolEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceMessageEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.tools.SequenceMessageSelectConnectionEditPartTracker; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.SequenceMessageLabelLocator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.SequenceMessagesRouter; + +/** + * The edit part for sequence diagrams messages. + * + * @author pcdavid, smonnier + */ +public class SequenceMessageEditPart extends DEdgeEditPart implements ISequenceEventEditPart { + /** + * id for extended meta data. + */ + public static final String MSG_TO_SELF_TOP_MOVE = "messageToSelfTopMove"; + + /** + * The global router for all the messages. + */ + private static final ConnectionRouter ROUTER = new SequenceMessagesRouter(); + + /** + * Constructor. + * + * @param view + * the view. + */ + public SequenceMessageEditPart(final View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + public void addNotify() { + SequenceEditPartsOperations.registerDiagramElement(this, resolveDiagramElement()); + super.addNotify(); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeNotify() { + super.removeNotify(); + SequenceEditPartsOperations.unregisterDiagramElement(this, resolveSemanticElement()); + } + + /** + * Force our own router for sequence messages. + */ + @SuppressWarnings("restriction") + @Override + protected void installRouter() { + getPrimaryShape().setConnectionRouter(ROUTER); + setCursor(org.eclipse.gmf.runtime.gef.ui.internal.l10n.Cursors.CURSOR_SEG_MOVE); + } + + /** + * {@inheritDoc} + */ + @Override + protected void createDefaultEditPolicies() { + super.createDefaultEditPolicies(); + + // Handle $endBefore for launch tools. + installEditPolicy(org.eclipse.sirius.diagram.tools.api.requests.RequestConstants.REQ_LAUNCH_TOOL, new SequenceLaunchToolEditPolicy()); + } + + /** + * Override to install the MessageEditPolicy. + * <p> + * {@inheritDoc} + */ + @Override + @SuppressWarnings("restriction") + public void installEditPolicy(Object key, EditPolicy editPolicy) { + if (EditPolicy.CONNECTION_BENDPOINTS_ROLE.equals(key)) { + super.installEditPolicy(key, new SequenceMessageEditPolicy()); + setCursor(org.eclipse.gmf.runtime.gef.ui.internal.l10n.Cursors.CURSOR_SEG_MOVE); + } else { + super.installEditPolicy(key, editPolicy); + } + } + + /** + * Overridden to hide feedback on reconnect attempts, which are not allowed. + * <p> + * {@inheritDoc} + */ + @Override + public void showSourceFeedback(Request request) { + if (!(request instanceof ReconnectRequest)) { + super.showSourceFeedback(request); + } + } + + /** + * Overridden to hide feedback on reconnect attempts, which are not allowed. + * <p> + * {@inheritDoc} + */ + @Override + public void showTargetFeedback(Request request) { + if (!(request instanceof ReconnectRequest)) { + super.showTargetFeedback(request); + } + } + + /** + * Sets the mouse cursor when the mouse is over this edit part. + * + * @param cursor + * the cursor shape. + */ + public void setCursor(final org.eclipse.swt.graphics.Cursor cursor) { + EditPartUtil.synchronizeRunnableToMainThread(this, new Runnable() { + public void run() { + Connection connectionFigure = SequenceMessageEditPart.this.getConnectionFigure(); + connectionFigure.setCursor(cursor); + }; + }); + } + + /** + * {@inheritDoc} + */ + @Override + public EditPart getTargetEditPart(Request request) { + if (request instanceof SelectionRequest) { + this.setCursor(org.eclipse.gmf.runtime.gef.ui.internal.l10n.Cursors.CURSOR_SEG_MOVE); + } + return super.getTargetEditPart(request); + } + + /** + * {@inheritDoc} + */ + @Override + public void refreshBendpoints() { + RelativeBendpoints bendpoints = (RelativeBendpoints) getEdge().getBendpoints(); + List<?> modelConstraint = bendpoints.getPoints(); + List<org.eclipse.draw2d.RelativeBendpoint> figureConstraint = new ArrayList<org.eclipse.draw2d.RelativeBendpoint>(); + for (int i = 0; i < modelConstraint.size(); i++) { + RelativeBendpoint wbp = (RelativeBendpoint) modelConstraint.get(i); + org.eclipse.draw2d.RelativeBendpoint rbp = new org.eclipse.draw2d.RelativeBendpoint(getConnectionFigure()); + rbp.setRelativeDimensions(new Dimension(wbp.getSourceX(), wbp.getSourceY()), new Dimension(wbp.getTargetX(), wbp.getTargetY())); + rbp.setWeight(0); + figureConstraint.add(rbp); + } + getConnectionFigure().setRoutingConstraint(figureConstraint); + } + + /** + * {@inheritDoc} + */ + @Override + public void setLayoutConstraint(EditPart child, IFigure childFigure, Object constraint) { + if (constraint instanceof LabelLocator) { + // custom locator to avoid vertical label position change during + // horizontal move of lifelines. + SequenceMessageLabelLocator smll = new SequenceMessageLabelLocator(childFigure.getParent(), (LabelLocator) constraint); + super.setLayoutConstraint(child, childFigure, smll); + } else { + super.setLayoutConstraint(child, childFigure, constraint); + } + } + + /** + * {@inheritDoc} + */ + @Override + public DragTracker getDragTracker(Request req) { + return new SequenceMessageSelectConnectionEditPartTracker(this); + } + + /** + * {@inheritDoc} + */ + public ISequenceEvent getISequenceEvent() { + return ISequenceElementAccessor.getMessage(getNotationView()).get(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/SequenceMessageNameEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/SequenceMessageNameEditPart.java new file mode 100644 index 0000000000..d35a4155d0 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/SequenceMessageNameEditPart.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.gef.Request; +import org.eclipse.gef.commands.Command; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.diagram.ui.commands.CommandProxy; +import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; +import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramEdgeEditPart.ViewEdgeFigure; +import org.eclipse.sirius.diagram.internal.edit.parts.DEdgeNameEditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.InverseRelativeNodePositionOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.SequenceMessageLabelLocator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; + +/** + * The edit part for sequence message labels. + * + * @author mporhel, smonnier + */ +public class SequenceMessageNameEditPart extends DEdgeNameEditPart { + /** + * The visual ID. + */ + public static final int VISUAL_ID = DEdgeNameEditPart.VISUAL_ID; + + /** + * Constructor. + * + * @param view + * the view. + */ + public SequenceMessageNameEditPart(View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + public void refresh() { + if (resolveSemanticElement() instanceof DEdge) { + super.refresh(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Command getCommand(Request request) { + Command result = super.getCommand(request); + if (result != null && result.canExecute() && new RequestQuery(request).isMove() && messageIsRightToLeft()) { + CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(getEditingDomain(), result.getLabel()); + ctc.compose(new CommandProxy(result)); + ICommand fixPositionCommand = CommandFactory.createICommand(getEditingDomain(), new InverseRelativeNodePositionOperation((Node) getNotationView())); + ctc.compose(fixPositionCommand); + result = new ICommandProxy(ctc); + } + return result; + } + + private boolean messageIsRightToLeft() { + IFigure figure = getFigure(); + ViewEdgeFigure parentFigure = null; + if (figure != null && figure.getParent() instanceof ViewEdgeFigure) { + parentFigure = (ViewEdgeFigure) figure.getParent(); + } + return SequenceMessageLabelLocator.isRightToLeft(parentFigure); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/StateEditPart.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/StateEditPart.java new file mode 100644 index 0000000000..c7874c29f3 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/part/StateEditPart.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gef.editpolicies.ResizableEditPolicy; +import org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.diagram.edit.internal.part.AbstractDiagramNodeEditPartOperation; +import org.eclipse.sirius.diagram.edit.internal.part.DiagramBorderNodeEditPartOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.StateSelectionEditPolicy; + +/** + * Special edit part for States. Implemented as bordered nodes, either directly + * on a lifeline or on an execution. + * + * @author smonnier + */ +public class StateEditPart extends ExecutionEditPart { + + /** + * Constructor. + * + * @param view + * the view. + */ + public StateEditPart(View view) { + super(view); + } + + /** + * {@inheritDoc} + */ + @Override + public ISequenceEvent getISequenceEvent() { + return ISequenceElementAccessor.getState(getNotationView()).get(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.edit.api.part.IAbstractDiagramNodeEditPart#createBorderItemLocator(org.eclipse.draw2d.IFigure, + * org.eclipse.sirius.DDiagramElement) + */ + @Override + public IBorderItemLocator createBorderItemLocator(final IFigure figure, final DDiagramElement vpElementBorderItem) { + return AbstractDiagramNodeEditPartOperation.createBorderItemLocator(this, figure, vpElementBorderItem); + } + + /** + * {@inheritDoc} + */ + @Override + public EditPolicy getPrimaryDragEditPolicy() { + final ResizableEditPolicy result = new StateSelectionEditPolicy(); + DDiagramElement dde = this.resolveDiagramElement(); + if (dde instanceof DNode) { + DNode node = (DNode) dde; + DiagramBorderNodeEditPartOperation.updateResizeKind(result, node); + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/AbstractFrameResizableEditPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/AbstractFrameResizableEditPolicy.java new file mode 100644 index 0000000000..17ba0c814d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/AbstractFrameResizableEditPolicy.java @@ -0,0 +1,273 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.Collection; + +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.FreeformViewport; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.Request; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.requests.AlignmentRequest; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.swt.graphics.Color; + +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.graphical.edit.policies.AirResizableEditPolicy; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractFrame; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ISequenceEventEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.AbstractInteractionFrameValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.ISEComplexMoveValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.HorizontalGuide; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.RangeGuide; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.tools.api.draw2d.ui.figures.FigureUtilities; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; + +/** + * A specific AirResizableEditPolicy to manage interaction use roles move & + * resize requests. + * + * @author mporhel + */ +public abstract class AbstractFrameResizableEditPolicy extends AirResizableEditPolicy { + + /** + * The color to use for the horizontal feedback rules shown when + * moving/resizing an execution. + */ + private static final Color FRAME_FEEDBACK_COLOR = ColorConstants.lightGray; + + private Collection<Figure> guides = Lists.newArrayList(); + + /** + * Constructor. + */ + public AbstractFrameResizableEditPolicy() { + super(); + setResizeDirections(PositionConstants.NORTH_SOUTH); + } + + /** + * {@inheritDoc} + */ + @Override + public int getResizeDirections() { + return super.getResizeDirections(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setResizeDirections(int newDirections) { + super.setResizeDirections(PositionConstants.NORTH_SOUTH); + } + + /** + * {@inheritDoc} + */ + @Override + protected void showChangeBoundsFeedback(ChangeBoundsRequest request) { + eraseChangeBoundsFeedback(request); + cancelHorizontalDelta(request); + + super.showChangeBoundsFeedback(request); + + ISequenceEventEditPart hostPart = (ISequenceEventEditPart) getHost(); + RequestQuery requestQuery = new RequestQuery(request); + + ISequenceEvent frame = hostPart.getISequenceEvent(); + if (hostPart.getSelected() == EditPart.SELECTED_PRIMARY && requestQuery.isMove()) { + ISEComplexMoveValidator validator = ISEComplexMoveValidator.getOrCreateValidator(request, requestQuery, frame); + if (validator != null) { + SequenceInteractionFeedBackBuilder feedBackBuilder = new SequenceInteractionFeedBackBuilder(validator, getFeedbackLayer(), hostPart); + for (Figure fig : feedBackBuilder.buildFeedBack()) { + addFeedback(fig); + guides.add(fig); + } + } + } else if (requestQuery.isResize()) { + Rectangle newBounds = getNewBounds(getHostAbsoluteBounds().getCopy(), request); + Figure frameGuide = new RangeGuide(FRAME_FEEDBACK_COLOR, new Range(newBounds.y, newBounds.y + Math.max(0, newBounds.height)), false); + Rectangle bounds = getFeedbackLayer().getBounds().getCopy(); + bounds.y = newBounds.y; + bounds.height = newBounds.height; + frameGuide.setBounds(bounds); + addFeedback(frameGuide); + guides.add(frameGuide); + + // display a guide for each Operand start + for (ISequenceEventEditPart child : getChildrenToFeedBack(request)) { + newBounds = getNewBounds(getAbsoluteBounds(child.getFigure()), request); + HorizontalGuide childGuide = createAndAddFeedbackHGuide(newBounds.getTop().y); + guides.add(childGuide); + } + + if (frame instanceof AbstractFrame) { + AbstractInteractionFrameValidator validator = AbstractInteractionFrameValidator.getOrCreateResizeValidator(request, (AbstractFrame) frame); + if (!validator.isValid()) { + feedBackConflicts(validator); + } + + feedBackExpansion(validator); + } + } + } + + private void feedBackExpansion(AbstractInteractionFrameValidator validator) { + Rectangle bounds = getFeedbackLayer().getBounds().getCopy(); + Range expansionZone = validator.getExpansionZone(); + if (!expansionZone.isEmpty()) { + Rectangle screenRange = new Rectangle(0, expansionZone.getLowerBound(), 0, expansionZone.width()); + screenRange.performScale(GraphicalHelper.getZoom((IGraphicalEditPart) getHost())); + Range expand = Range.verticalRange(screenRange); + + RangeGuide expansion = new RangeGuide(validator.isValid() ? ColorConstants.blue : ColorConstants.red, expand, true); + bounds.height = expand.width(); + bounds.y = expand.getLowerBound(); + expansion.setBounds(bounds); + addFeedback(expansion); + guides.add(expansion); + } + } + + private void feedBackConflicts(AbstractInteractionFrameValidator validator) { + for (Integer conflict : validator.getInvalidPositions()) { + Point conflictingPosition = new Point(0, conflict); + conflictingPosition.performScale(GraphicalHelper.getZoom((IGraphicalEditPart) getHost())); + + Rectangle bounds = getFeedbackLayer().getBounds().getCopy(); + bounds.y = conflictingPosition.y; + bounds.height = 1; + + HorizontalGuide conflictGuide = new HorizontalGuide(ColorConstants.red, conflictingPosition.y); + conflictGuide.setBounds(bounds); + addFeedback(conflictGuide); + guides.add(conflictGuide); + } + } + + private Rectangle getNewBounds(Rectangle oldBounds, ChangeBoundsRequest cbr) { + Rectangle newBounds = cbr.getTransformedRectangle(oldBounds).getCopy(); + + FreeformViewport viewport = FigureUtilities.getFreeformViewport(getHostFigure()); + if (viewport != null) { + newBounds.translate(viewport.getViewLocation()); + } + + return newBounds; + } + + private HorizontalGuide createAndAddFeedbackHGuide(int y) { + Rectangle bounds = getFeedbackLayer().getBounds().getCopy(); + bounds.height = 1; + bounds.y = y; + + HorizontalGuide guide = new HorizontalGuide(FRAME_FEEDBACK_COLOR, y); + guide.setBounds(bounds); + addFeedback(guide); + return guide; + } + + /** + * {@inheritDoc} + */ + @Override + protected void eraseChangeBoundsFeedback(ChangeBoundsRequest request) { + // cancelHorizontalDelta((ChangeBoundsRequest) request); + removeFeedBackOnGuides(); + super.eraseChangeBoundsFeedback(request); + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getAutoSizeCommand(Request request) { + return UnexecutableCommand.INSTANCE; + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getAlignCommand(AlignmentRequest request) { + return UnexecutableCommand.INSTANCE; + } + + /** + * Cancel horizontal changes of the given request. + * + * @param request + * a request. + */ + protected void cancelHorizontalDelta(ChangeBoundsRequest request) { + if (request == null) { + return; + } + + Point moveDelta = request.getMoveDelta(); + if (moveDelta != null) { + request.setMoveDelta(new Point(0, moveDelta.y)); + } + + Dimension sizeDelta = request.getSizeDelta(); + if (sizeDelta != null) { + request.setSizeDelta(new Dimension(0, sizeDelta.height)); + } + } + + private void removeFeedBackOnGuides() { + if (guides != null && !guides.isEmpty()) { + for (Figure hGuide : guides) { + removeFeedback(hGuide); + } + guides.clear(); + } + } + + /** + * Return absolute bounds of the current host. + * + * @return absolute bounds of the current host. + */ + private Rectangle getHostAbsoluteBounds() { + return getAbsoluteBounds(getHostFigure()); + } + + private Rectangle getAbsoluteBounds(IFigure figure) { + Rectangle bounds = figure.getBounds().getCopy(); + figure.getParent().translateToAbsolute(bounds); + return bounds; + } + + /** + * Additional feedback. + * + * @param request + * the request to feedback. + * @return a collection of {@link ISequenceEventEditPart} to feedback. + */ + protected abstract Collection<ISequenceEventEditPart> getChildrenToFeedBack(ChangeBoundsRequest request); +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/CombinedFragmentResizableEditPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/CombinedFragmentResizableEditPolicy.java new file mode 100644 index 0000000000..c82a0691e5 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/CombinedFragmentResizableEditPolicy.java @@ -0,0 +1,415 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.Collection; +import java.util.Collections; + +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +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.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.editparts.ZoomManager; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.common.core.command.IdentityCommand; +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.emf.commands.core.command.AbstractTransactionalCommand; +import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand; +import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.Size; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DNodeContainer; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +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.operation.VerticalSpaceExpansion; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.CombinedFragmentCompartmentEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.CombinedFragmentEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ISequenceEventEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.OperandEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.AbstractInteractionFrameValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.ISEComplexMoveValidator; +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.tools.internal.edit.command.CommandFactory; + +/** + * A specific AirResizableEditPolicy to combined fragment roles move & resize + * requests. + * + * @author smonnier + */ +public class CombinedFragmentResizableEditPolicy extends AbstractFrameResizableEditPolicy { + + private static final String RESIZE = "Resize"; + + /** + * {@inheritDoc} + */ + @Override + protected Command getMoveCommand(ChangeBoundsRequest request) { + cancelHorizontalDelta(request); + CombinedFragmentEditPart hostPart = (CombinedFragmentEditPart) getHost(); + + ICommand solution = IdentityCommand.INSTANCE; + RequestQuery requestQuery = new RequestQuery(request); + if (hostPart.getSelected() == EditPart.SELECTED_PRIMARY && requestQuery.isMove()) { + ISEComplexMoveValidator validator = ISEComplexMoveValidator.getOrCreateValidator(request, requestQuery, hostPart.getISequenceEvent()); + if (validator != null && validator.isValid()) { + CompositeTransactionalCommand ctc = buildNewMoveCommand(hostPart, request, validator); + postProcessDefaultCommand(hostPart, request, ctc, null); + solution = ctc; + } else { + solution = org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE; + } + } + return new ICommandProxy(solution); + } + + private CompositeTransactionalCommand buildNewMoveCommand(CombinedFragmentEditPart hostPart, ChangeBoundsRequest request, ISEComplexMoveValidator validator) { + TransactionalEditingDomain editingDomain = hostPart.getEditingDomain(); + ISEComplexMoveCommandBuilder builder = new ISEComplexMoveCommandBuilder(editingDomain, "Execution Move Composite Command", new RequestQuery(request), validator); + return builder.buildCommand(); + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getResizeCommand(ChangeBoundsRequest request) { + cancelHorizontalDelta(request); + + CombinedFragmentEditPart cfep = (CombinedFragmentEditPart) getHost(); + ISequenceEvent fragment = cfep.getISequenceEvent(); + AbstractInteractionFrameValidator validator = AbstractInteractionFrameValidator.getOrCreateResizeValidator(request, (CombinedFragment) fragment); + + Command result; + if (validator != null && validator.isValid()) { + CompositeTransactionalCommand ctc = getRezizeCustomCommand(cfep, request); + + Range expansionZone = validator == null ? Range.emptyRange() : validator.getExpansionZone(); + ICommand autoExpand = null; + if (expansionZone != null && !expansionZone.isEmpty()) { + SequenceDiagram diagram = fragment.getDiagram(); + Collection<ISequenceEvent> eventToIgnore = Collections.singletonList(fragment); + autoExpand = CommandFactory.createICommand(cfep.getEditingDomain(), new VerticalSpaceExpansion(diagram, expansionZone, 0, eventToIgnore)); + } + + postProcessDefaultCommand(cfep, request, ctc, autoExpand); + result = new ICommandProxy(ctc); + } else { + result = UnexecutableCommand.INSTANCE; + } + + return result; + } + + private CompositeTransactionalCommand getRezizeCustomCommand(CombinedFragmentEditPart self, ChangeBoundsRequest request) { + CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(self.getEditingDomain(), "Combined Fragment Resize Composite Command"); + ctc.add(CombinedFragmentResizableEditPolicy.getResizeBorderItemTCommand(self, request)); + Option<CombinedFragment> combinedFragmentOption = ISequenceElementAccessor.getCombinedFragment(self.getNotationView()); + if (combinedFragmentOption.some() && !combinedFragmentOption.get().getOperands().isEmpty()) { + // if (!validateResize(request, combinedFragmentOption.get())) { + // // FIXME this validation should be removed because done in the + // // validator now. + // return UnexecutableCommand.INSTANCE; + // } + if (request.getResizeDirection() == PositionConstants.NORTH) { + // we need to process the same resize to the first operand + // and keep the others unchanged + addResizeOperandsFromNorthCommand(ctc, request); + } else if (request.getResizeDirection() == PositionConstants.SOUTH) { + // we need to process the same resize to the last operand + // and keep the others unchanged + addResizeOperandsFromSouthCommand(ctc, request); + } + } + return ctc; + } + + private void postProcessDefaultCommand(CombinedFragmentEditPart self, ChangeBoundsRequest request, CompositeTransactionalCommand ctc, ICommand autoExpand) { + if (ctc != null && ctc.canExecute()) { + if (autoExpand != null) { + ctc.compose(autoExpand); + } + + SequenceEditPartsOperations.addRefreshGraphicalOrderingCommand(ctc, self); + SequenceEditPartsOperations.addRefreshSemanticOrderingCommand(ctc, self); + SequenceEditPartsOperations.addSynchronizeSemanticOrderingCommand(ctc, self.getISequenceEvent()); + } + } + + /** + * Returns the command to resize the combined fragment from the north face, + * and therefore resize up the first operand. + * + * @param ctc + * the current transactional command. + * @param request + * the request for a resize. + */ + private void addResizeOperandsFromNorthCommand(CompositeTransactionalCommand ctc, ChangeBoundsRequest request) { + // we need to process the same resize to the first operand + OperandEditPart firstOperandEditPart = getFirstOperandEditPart(); + Option<Operand> firstOperand = ISequenceElementAccessor.getOperand(firstOperandEditPart.getNotationView()); + int position = 0; + + OperandEditPart followingOperandEditPart = getFollowingOperandEditPart(position); + Option<Operand> followingOperand = followingOperandEditPart != null ? ISequenceElementAccessor.getOperand(followingOperandEditPart.getNotationView()) : Options.<Operand> newNone(); + + position++; + + Point newLocation = null; + Dimension newDimension = null; + if (firstOperand.some()) { + Range verticalRange = firstOperand.get().getVerticalRange(); + newLocation = new Point(0, LayoutConstants.COMBINED_FRAGMENT_TITLE_HEIGHT); + newDimension = new Dimension(firstOperand.get().getBounds().width, verticalRange.width() + request.getSizeDelta().height); + ctc.add(createOperandSetBoundsCommand(firstOperandEditPart, newLocation, newDimension)); + } + + if (followingOperandEditPart != null && followingOperand.some()) { + + // recurse + while (followingOperandEditPart != null && followingOperand.some()) { + Range verticalRange = followingOperand.get().getVerticalRange(); + newLocation = new Point(0, newLocation.y + newDimension.height); + newDimension = new Dimension(followingOperand.get().getBounds().width, verticalRange.width()); + ctc.add(createOperandSetBoundsCommand(followingOperandEditPart, newLocation, newDimension)); + followingOperandEditPart = getFollowingOperandEditPart(position); + followingOperand = followingOperandEditPart != null ? ISequenceElementAccessor.getOperand(followingOperandEditPart.getNotationView()) : Options.<Operand> newNone(); + position++; + } + + } + } + + /** + * Returns the command to resize the combined fragment from the south face, + * and therefore resize down the last operand. + * + * @param ctc + * the current transactional command. + * @param request + * the request for a resize. + */ + public void addResizeOperandsFromSouthCommand(CompositeTransactionalCommand ctc, ChangeBoundsRequest request) { + // we need to process the same resize to the first operand if it is + // single + OperandEditPart firstOperandEditPart = getFirstOperandEditPart(); + Option<Operand> firstOperand = ISequenceElementAccessor.getOperand(firstOperandEditPart.getNotationView()); + int position = 0; + + OperandEditPart followingOperandEditPart = getFollowingOperandEditPart(position); + Option<Operand> followingOperand = followingOperandEditPart != null ? ISequenceElementAccessor.getOperand(followingOperandEditPart.getNotationView()) : Options.<Operand> newNone(); + + position++; + + OperandEditPart lastOperandEditPart = getLastOperandEditPart(); + + Point newLocation = null; + Dimension newDimension = null; + if (firstOperand.some()) { + Range verticalRange = firstOperand.get().getVerticalRange(); + newLocation = new Point(0, LayoutConstants.COMBINED_FRAGMENT_TITLE_HEIGHT); + if (firstOperandEditPart == lastOperandEditPart) { + newDimension = new Dimension(firstOperand.get().getBounds().width, verticalRange.width() + request.getSizeDelta().height); + } else { + newDimension = new Dimension(firstOperand.get().getBounds().width, verticalRange.width()); + } + ctc.add(createOperandSetBoundsCommand(firstOperandEditPart, newLocation, newDimension)); + } + + if (followingOperandEditPart != null && followingOperand.some()) { + Range verticalRange = followingOperand.get().getVerticalRange(); + + // recurse + while (followingOperandEditPart != null && followingOperand.some() && followingOperandEditPart != lastOperandEditPart) { + newLocation = new Point(0, newLocation.y + newDimension.height); + newDimension = new Dimension(followingOperand.get().getBounds().width, verticalRange.width()); + ctc.add(createOperandSetBoundsCommand(followingOperandEditPart, newLocation, newDimension)); + followingOperandEditPart = getFollowingOperandEditPart(position); + followingOperand = followingOperandEditPart != null ? ISequenceElementAccessor.getOperand(followingOperandEditPart.getNotationView()) : Options.<Operand> newNone(); + verticalRange = followingOperand.get().getVerticalRange(); + position++; + } + newLocation = new Point(0, newLocation.y + newDimension.height); + newDimension = new Dimension(followingOperand.get().getBounds().width, verticalRange.width() + request.getSizeDelta().height); + ctc.add(createOperandSetBoundsCommand(lastOperandEditPart, newLocation, newDimension)); + + } + } + + /** + * Finds the following {@link OperandEditPart} of the current + * {@link OperandEditPart} identified by the index currentOperandIndex. + * + * @param currentOperandIndex + * the index of the current {@link OperandEditPart} + * @return the following {@link OperandEditPart} + */ + private OperandEditPart getFollowingOperandEditPart(int currentOperandIndex) { + CombinedFragmentCompartmentEditPart combinedFragmentCompartmentEditPart = Iterables.getOnlyElement(Iterables.filter(getHost().getChildren(), CombinedFragmentCompartmentEditPart.class)); + Iterable<OperandEditPart> operandEditPartList = Iterables.filter(combinedFragmentCompartmentEditPart.getChildren(), OperandEditPart.class); + for (OperandEditPart operandEditPart : operandEditPartList) { + Option<Operand> operandOption = ISequenceElementAccessor.getOperand(operandEditPart.getNotationView()); + if (operandOption.some()) { + Operand operand = operandOption.get(); + int operandIndex = operand.getIndex(); + if (operandIndex == currentOperandIndex + 1) { + return operandEditPart; + } + } + } + return null; + } + + /** + * Finds the first {@link OperandEditPart}. + * + * @return the first {@link OperandEditPart} + */ + private OperandEditPart getFirstOperandEditPart() { + CombinedFragmentCompartmentEditPart combinedFragmentCompartmentEditPart = Iterables.getOnlyElement(Iterables.filter(getHost().getChildren(), CombinedFragmentCompartmentEditPart.class)); + for (OperandEditPart operandEditPart : Iterables.filter(combinedFragmentCompartmentEditPart.getChildren(), OperandEditPart.class)) { + Option<Operand> operandOption = ISequenceElementAccessor.getOperand(operandEditPart.getNotationView()); + if (operandOption.some()) { + Operand operand = operandOption.get(); + int operandIndex = operand.getIndex(); + if (operandIndex == 0) { + return operandEditPart; + } + } + } + return null; + } + + /** + * Finds the last {@link OperandEditPart}. + * + * @return the last {@link OperandEditPart} + */ + private OperandEditPart getLastOperandEditPart() { + CombinedFragmentCompartmentEditPart combinedFragmentCompartmentEditPart = Iterables.getOnlyElement(Iterables.filter(getHost().getChildren(), CombinedFragmentCompartmentEditPart.class)); + Iterable<OperandEditPart> operandEditPartList = Iterables.filter(combinedFragmentCompartmentEditPart.getChildren(), OperandEditPart.class); + int childrenOperandEditPartNumber = Iterables.size(operandEditPartList); + for (OperandEditPart operandEditPart : operandEditPartList) { + Option<Operand> operandOption = ISequenceElementAccessor.getOperand(operandEditPart.getNotationView()); + if (operandOption.some()) { + Operand operand = operandOption.get(); + int operandIndex = operand.getIndex(); + if (operandIndex == childrenOperandEditPartNumber - 1) { + return operandEditPart; + } + } + } + return null; + } + + /** + * Returns the command to resize a bordered node. + * + * @param part + * the edit part corresponding to the bordered node. + * @param request + * the request for a resize. + * @return the command to resize a bordered node. + */ + public static AbstractTransactionalCommand getResizeBorderItemTCommand(IGraphicalEditPart part, ChangeBoundsRequest request) { + final EObject semantic = part.resolveSemanticElement(); + if (semantic instanceof DNodeContainer) { + final double zoom = ((ZoomManager) part.getViewer().getProperty(ZoomManager.class.toString())).getZoom(); + final Dimension dimension = CombinedFragmentResizableEditPolicy.getDimensionFromView(part); + final Point position = CombinedFragmentResizableEditPolicy.getPositionFromView(part); + dimension.height += request.getSizeDelta().height / zoom; + switch (request.getResizeDirection()) { + case PositionConstants.NORTH: + case PositionConstants.NORTH_WEST: + case PositionConstants.NORTH_EAST: + position.y -= request.getSizeDelta().height / zoom; + break; + default: + break; + } + return new SetBoundsCommand(part.getEditingDomain(), RESIZE, new EObjectAdapter(part.getNotationView()), new Rectangle(position, dimension)); + } + return null; + } + + /** + * Creates the {@link SetBoundsCommand} to resize an operand. + * + * @param part + * the {@link OperandEditPart} + * @param location + * the new location of the operand + * @param dimension + * the new dimension of the operand + * @return a command to resize an operand + */ + private AbstractTransactionalCommand createOperandSetBoundsCommand(IGraphicalEditPart part, Point location, Dimension dimension) { + return new SetBoundsCommand(part.getEditingDomain(), RESIZE, new EObjectAdapter(part.getNotationView()), new Rectangle(location, dimension)); + } + + private static Point getPositionFromView(IGraphicalEditPart part) { + final Point position = new Point(); + if (part.getNotationView() instanceof Node && ((Node) part.getNotationView()).getLayoutConstraint() instanceof Location) { + final Location location = (Location) ((Node) part.getNotationView()).getLayoutConstraint(); + position.x = location.getX(); + position.y = location.getY(); + } + return position; + } + + private static Dimension getDimensionFromView(IGraphicalEditPart part) { + final Dimension dimension = new Dimension(); + if (part.getNotationView() instanceof Node && ((Node) part.getNotationView()).getLayoutConstraint() instanceof Size) { + final Size size = (Size) ((Node) part.getNotationView()).getLayoutConstraint(); + dimension.width = size.getWidth(); + dimension.height = size.getHeight(); + } + return dimension; + } + + /** + * {@inheritDoc} + */ + @Override + protected Collection<ISequenceEventEditPart> getChildrenToFeedBack(ChangeBoundsRequest request) { + Collection<ISequenceEventEditPart> feedback = Lists.newArrayList(); + if (getHost() instanceof CombinedFragmentEditPart && new RequestQuery(request).isMove()) { + CombinedFragmentEditPart cfep = (CombinedFragmentEditPart) getHost(); + for (CombinedFragmentCompartmentEditPart cpt : Iterables.filter(cfep.getChildren(), CombinedFragmentCompartmentEditPart.class)) { + Iterables.addAll(feedback, Iterables.filter(cpt.getChildren(), OperandEditPart.class)); + } + } + return feedback; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/EndOfLifeSelectionPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/EndOfLifeSelectionPolicy.java new file mode 100644 index 0000000000..a5852aae72 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/EndOfLifeSelectionPolicy.java @@ -0,0 +1,228 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.Request; +import org.eclipse.gef.RequestConstants; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.requests.AlignmentRequest; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.diagram.ui.commands.CommandProxy; +import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; +import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.diagram.business.internal.query.DNodeQuery; +import org.eclipse.sirius.diagram.graphical.edit.policies.SpecificBorderItemSelectionEditPolicy; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.EndOfLife; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +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.EndOfLifeOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ShiftDescendantMessagesOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.EndOfLifeEditPart; +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.edit.part.SequenceDiagramEditPart; +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.SequenceDiagramEPQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; + +/** + * This policy controls the moves of {@link EndOfLifeEditPart}s, and in + * particular how such moves actually resize the parent lifeline. + * + * @author pcdavid + */ +public class EndOfLifeSelectionPolicy extends SpecificBorderItemSelectionEditPolicy { + /** + * Overridden to validate the host type. + * <p> + * {@inheritDoc} + */ + @Override + public void setHost(EditPart host) { + Preconditions.checkArgument(host instanceof EndOfLifeEditPart); + super.setHost(host); + } + + /** + * Convenience method to return the host part with the correct type. + * + * @return returns the host of this policy, with the appropriate type. + */ + protected EndOfLifeEditPart getEOL() { + return (EndOfLifeEditPart) getHost(); + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getAlignCommand(AlignmentRequest request) { + return UnexecutableCommand.INSTANCE; + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getMoveCommand(ChangeBoundsRequest request) { + Command result = UnexecutableCommand.INSTANCE; + constrainMoveVertically(request); + if (canResizeLifeline(getEOL(), request)) { + ChangeBoundsRequest cbr = EndOfLifeOperations.getLifelineResizeRequest(request, getEOL(), getEOL()); + if (cbr != null) { + result = getLifelineMovesCommand(getEOL(), cbr); + } + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public void showSourceFeedback(Request request) { + constrainMoveVertically(request); + super.showSourceFeedback(request); + if (request instanceof ChangeBoundsRequest && canResizeLifeline(getEOL(), (ChangeBoundsRequest) request)) { + EndOfLifeOperations.showEndOfLifeFeedback(request, getEOL(), getEOL()); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void eraseSourceFeedback(Request request) { + constrainMoveVertically(request); + super.eraseSourceFeedback(request); + EndOfLifeOperations.eraseEndOfLifeFeedback((LifelineEditPart) getEOL().getParent(), request); + } + + /** + * Modifies the requested moves so that the part only moves vertically. + * Horizontal motion is disabled/ignored. + */ + private void constrainMoveVertically(Request request) { + if (request instanceof ChangeBoundsRequest) { + ChangeBoundsRequest cbr = (ChangeBoundsRequest) request; + cbr.setMoveDelta(new Point(0, cbr.getMoveDelta().y)); + } + } + + private Command getLifelineMovesCommand(EndOfLifeEditPart self, ChangeBoundsRequest cbr) { + CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(self.getEditingDomain(), "Lifelines moves command from end of life"); + Option<EndOfLife> endOfLife = ISequenceElementAccessor.getEndOfLife(self.getNotationView()); + LifelineEditPart lep = self.getLifelineEditPart(); + boolean destroyed = endOfLife.some() && endOfLife.get().isExplicitelyDestroyed(); + + if (lep != null) { + if (destroyed) { + /* + * Moving a real EOL (i.e. one which represents the explicit + * destruction of the message) only resizes the corresponding + * lifeline. + */ + addLifelineResizeCommand(self, cbr, ctc, lep); + } else { + /* + * Otherwise the EOL is just a handle to control the vertical + * range of the whole scenario, so we resize all the lifelines + * which are free, i.e. not explicitly destroyed at some + * specific position. + */ + SequenceDiagramEditPart diagram = EditPartsHelper.getSequenceDiagramPart(self); + for (LifelineEditPart lifeline : new SequenceDiagramEPQuery(diagram).getAllLifelines()) { + if (!((Lifeline) lifeline.getISequenceEvent()).isExplicitlyDestroyed()) { + addLifelineResizeCommand(self, cbr, ctc, lifeline); + } + } + } + } + ICommandProxy iCommandProxy = new ICommandProxy(ctc); + iCommandProxy.setLabel(ctc.getLabel()); + return iCommandProxy; + } + + private void addLifelineResizeCommand(EndOfLifeEditPart self, ChangeBoundsRequest cbr, CompositeTransactionalCommand ctc, LifelineEditPart lep) { + cbr.setEditParts(lep); + ctc.compose(new CommandProxy(lep.getCommand(cbr))); + + Dimension sizeDelta = cbr.getSizeDelta().getCopy(); + GraphicalHelper.screen2logical(sizeDelta, getEOL()); + TransactionalEditingDomain domain = getEOL().getEditingDomain(); + ctc.compose(CommandFactory.createICommand(domain, new ShiftDescendantMessagesOperation(lep.getISequenceEvent(), sizeDelta.height, true, false, false))); + } + + private boolean canResizeLifeline(EndOfLifeEditPart self, ChangeBoundsRequest request) { + Option<EndOfLife> endOfLife = ISequenceElementAccessor.getEndOfLife(self.getNotationView()); + boolean destroyed = endOfLife.some() && endOfLife.get().isExplicitelyDestroyed(); + boolean result = !destroyed || request.isConstrainedMove(); + LifelineEditPart lep = self.getLifelineEditPart(); + if (result && lep != null && (org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants.REQ_DROP.equals(request.getType()) || RequestConstants.REQ_MOVE.equals(request.getType()))) { + Range range = lep.getISequenceEvent().getVerticalRange(); + int moveDeltaY = request.getMoveDelta().y; + int rangeLimit = getRangeLimit(self, lep, range, moveDeltaY, destroyed); + result = range.getUpperBound() + moveDeltaY >= rangeLimit; + } else { + result = false; + } + return result; + } + + private int getRangeLimit(EndOfLifeEditPart self, LifelineEditPart lep, Range range, int moveDeltaY, boolean isDestroyed) { + SequenceDiagramEditPart sdep = EditPartsHelper.getSequenceDiagramPart(self); + int rangeLimit = range.getLowerBound() + LayoutConstants.EXECUTION_CHILDREN_MARGIN; + + if (!isDestroyed) { + View lifelineView = (View) lep.getModel(); + Lifeline lifeline = ISequenceElementAccessor.getLifeline(lifelineView).get(); + DNode dNode = (DNode) lifelineView.getElement(); + + rangeLimit = Math.max(rangeLimit, new DNodeQuery(dNode).getDefaultDimension().height + lifeline.getVerticalRange().getLowerBound()); + EventEnd lastEnd = SequenceGraphicalHelper.getEndBefore((SequenceDDiagram) sdep.resolveSemanticElement(), range.getUpperBound()); + if (moveDeltaY < 0 && lastEnd != null) { + SingleEventEnd see; + if (lastEnd instanceof SingleEventEnd) { + see = (SingleEventEnd) lastEnd; + } else { + see = ((CompoundEventEnd) lastEnd).getEventEnds().iterator().next(); + } + + ISequenceEventEditPart ise = EditPartsHelper.findISequenceEvent(see, sdep); + rangeLimit = Math.max(rangeLimit, SequenceEditPartsOperations.getVerticalRange(ise).getUpperBound() + LayoutConstants.EXECUTION_CHILDREN_MARGIN); + } + } else { + rangeLimit = Math.max(lep.getISequenceEvent().getOccupiedRange().getUpperBound() + LayoutConstants.EXECUTION_CHILDREN_MARGIN, rangeLimit); + } + return rangeLimit; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/ExecutionSelectionEditPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/ExecutionSelectionEditPolicy.java new file mode 100644 index 0000000000..3ad4f7037b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/ExecutionSelectionEditPolicy.java @@ -0,0 +1,696 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.FreeformViewport; +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.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.EditPartViewer; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.common.core.command.IdentityCommand; +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.emf.commands.core.command.CompositeTransactionalCommand; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.swt.graphics.Color; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.edit.internal.part.DiagramBorderNodeEditPartOperation; +import org.eclipse.sirius.diagram.graphical.edit.policies.SpecificBorderItemSelectionEditPolicy; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +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.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SetMessageRangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SetVerticalRangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.ShiftDirectSubExecutionsOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.VerticalSpaceExpansion; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.EventFinder; +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +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.operation.ShiftDescendantMessagesOperation; +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.SequenceDiagramEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.AbstractNodeEventResizeSelectionValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.ISEComplexMoveValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.HorizontalGuide; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.RangeGuide; +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.tools.api.draw2d.ui.figures.FigureUtilities; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; + +/** + * Specialization of the default policy for executions, in order to validate and + * execute the specific resize and move behaviors needed for sequence diagrams. + * + * @author pcdavid + */ +public class ExecutionSelectionEditPolicy extends SpecificBorderItemSelectionEditPolicy { + + /** + * The color to use for the horizontal feedback rules shown when + * moving/resizing an execution. + */ + protected static final Color EXECUTION_FEEDBACK_COLOR = ColorConstants.lightGray; + + /** + * Additional figures for feedback. + */ + protected Collection<Figure> guides = Lists.newArrayList(); + + /** + * {@inheritDoc} + */ + @Override + protected Command getMoveCommand(ChangeBoundsRequest request) { + cancelHorizontalDelta(request); + ICommand solution = IdentityCommand.INSTANCE; + ExecutionEditPart hostPart = (ExecutionEditPart) getHost(); + + RequestQuery requestQuery = new RequestQuery(request); + if (hostPart.getSelected() == EditPart.SELECTED_PRIMARY && requestQuery.isMove()) { + ISEComplexMoveValidator validator = ISEComplexMoveValidator.getOrCreateValidator(request, requestQuery, hostPart.getISequenceEvent()); + if (validator != null && validator.isValid()) { + solution = buildMoveCommand(hostPart, request, validator); + } else { + solution = org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE; + } + } + return new ICommandProxy(solution); + } + + private ICommand buildMoveCommand(ExecutionEditPart hostPart, ChangeBoundsRequest request, ISEComplexMoveValidator validator) { + TransactionalEditingDomain editingDomain = hostPart.getEditingDomain(); + RequestQuery requestQuery = new RequestQuery(request); + ISEComplexMoveCommandBuilder builder = new ISEComplexMoveCommandBuilder(editingDomain, "Execution Move Composite Command", requestQuery, validator); + return postProcessCommand(builder.buildCommand(), hostPart, requestQuery); + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getResizeCommand(ChangeBoundsRequest request) { + Command result = UnexecutableCommand.INSTANCE; + ExecutionEditPart hostPart = (ExecutionEditPart) getHost(); + AbstractNodeEvent host = (AbstractNodeEvent) hostPart.getISequenceEvent(); + + AbstractNodeEventResizeSelectionValidator validator = AbstractNodeEventResizeSelectionValidator.getOrCreateValidator(request, host); + + if (validator.isValid()) { + ICommand solution = buildResizeCommand(request, hostPart, validator); + if (solution == null) { + solution = IdentityCommand.INSTANCE; + } + result = new ICommandProxy(solution); + } + return result; + } + + private ICommand buildResizeCommand(ChangeBoundsRequest request, ExecutionEditPart hostPart, AbstractNodeEventResizeSelectionValidator validator) { + TransactionalEditingDomain editingDomain = hostPart.getEditingDomain(); + ICommand solution = null; + + AbstractNodeEvent self = (AbstractNodeEvent) hostPart.getISequenceEvent(); + RequestQuery requestQuery = new RequestQuery(request); + if (requestQuery.isMultiSelectionOperation()) { + validator.setExpansionZone(null); + } + + CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(hostPart.getEditingDomain(), "Execution Resize Composite Command"); + if (needVerticalSpaceExpansion(validator, request)) { + SequenceDiagramEditPart diagram = EditPartsHelper.getSequenceDiagramPart(hostPart); + Collection<ISequenceEvent> eventToIgnore = Collections.singletonList((ISequenceEvent) self); + ctc.compose(CommandFactory.createICommand(editingDomain, new VerticalSpaceExpansion(diagram.getSequenceDiagram(), validator.getExpansionZone(), 0, eventToIgnore))); // FinalParentHelper.computeLinkedSiblings(requestQuery)))); + } + if (validator.getFinalHierarchicalParent().equals(self.getHierarchicalParentEvent())) { + Command cmd = DiagramBorderNodeEditPartOperation.getResizeBorderItemCommand((ExecutionEditPart) getHost(), request, false); + ctc.add(new CommandProxy(cmd)); + ctc.setLabel(cmd.getLabel()); + + if (self instanceof Execution) { + addChildrenAdjustmentCommands((Execution) self, ctc, editingDomain, request, validator); + } + solution = postProcessCommand(ctc, hostPart, requestQuery); + } + return solution; + } + + /** + * Add refresh ordering commands and reorder commands. + * + * @param ctc + * the current composite command to complete. + * @param selfEditPart + * the current edit part + * @param requestQuery + * query on the request + * @return the completed command + */ + protected ICommand postProcessCommand(CompositeTransactionalCommand ctc, ExecutionEditPart selfEditPart, RequestQuery requestQuery) { + AbstractNodeEvent self = (AbstractNodeEvent) selfEditPart.getISequenceEvent(); + SequenceEditPartsOperations.addRefreshGraphicalOrderingCommand(ctc, selfEditPart); + SequenceEditPartsOperations.addRefreshSemanticOrderingCommand(ctc, selfEditPart); + + List<Message> linkedMessages = self.getLinkedMessages(); + if (!linkedMessages.isEmpty() && !linkedMessages.get(0).isLogicallyInstantaneous()) { + SequenceEditPartsOperations.addSynchronizeSemanticOrderingCommand(ctc, linkedMessages.get(0)); + } + + if (requestQuery.isMultiSelectionOperation()) { + SequenceEditPartsOperations.addSynchronizeSemanticOrderingCommand(ctc, self, requestQuery.getISequenceEvents()); + } else { + SequenceEditPartsOperations.addSynchronizeSemanticOrderingCommand(ctc, self); + } + if (linkedMessages.size() >= 2 && !linkedMessages.get(1).isLogicallyInstantaneous()) { + SequenceEditPartsOperations.addSynchronizeSemanticOrderingCommand(ctc, linkedMessages.get(1)); + } + return ctc; + } + + private boolean needVerticalSpaceExpansion(AbstractNodeEventResizeSelectionValidator validator, ChangeBoundsRequest request) { + return validator.getExpansionZone() != null && !new RequestQuery(request).isExecutionMovedIndirectly(); + } + + private Collection<ISequenceEvent> getSequenceEventsUpperToInsertionTime(SequenceDiagram sequenceDiagram, int lowerBound) { + Collection<ISequenceEvent> result = new ArrayList<ISequenceEvent>(); + Set<AbstractNodeEvent> allDelimitedSequenceEvents = sequenceDiagram.getAllAbstractNodeEvents(); + for (ISequenceEvent sequenceEvent : allDelimitedSequenceEvents) { + if (sequenceEvent.getVerticalRange().getLowerBound() > lowerBound) { + result.add(sequenceEvent); + } + } + return result; + } + + /** + * Compute the current vertical move. + * + * @param cbr + * the current request. + * @return the vertical move. + */ + protected Integer getVMove(ChangeBoundsRequest cbr) { + Rectangle logicalDelta = new RequestQuery(cbr).getLogicalDelta(); + return logicalDelta.y; + } + + private void addChildrenAdjustmentCommands(Execution exec, CompositeTransactionalCommand cc, TransactionalEditingDomain editingDomain, final ChangeBoundsRequest cbr, + AbstractNodeEventResizeSelectionValidator validator) { + RequestQuery rq = new RequestQuery(cbr); + Rectangle logicalDelta = rq.getLogicalDelta(); + int moveDelta = logicalDelta.y; + int sizeDelta = logicalDelta.height; + if (rq.isResizeFromTop()) { + cc.compose(CommandFactory.createICommand(editingDomain, new ShiftDirectSubExecutionsOperation(exec, sizeDelta))); + cc.compose(CommandFactory.createICommand(editingDomain, new ShiftDescendantMessagesOperation(exec, moveDelta, true, false, true))); + + addCompoundEventsMoveCommands(exec, cc, editingDomain, true, moveDelta, cbr, validator); + addCompoundEventsMoveCommands(exec, cc, editingDomain, false, 0, cbr, validator); + } else if (rq.isResizeFromBottom()) { + cc.compose(CommandFactory.createICommand(editingDomain, new ShiftDescendantMessagesOperation(exec, sizeDelta, true, false, false))); + + addCompoundEventsMoveCommands(exec, cc, editingDomain, true, 0, cbr, validator); + addCompoundEventsMoveCommands(exec, cc, editingDomain, false, sizeDelta, cbr, validator); + } + } + + private void addCompoundEventsMoveCommands(Execution self, CompositeTransactionalCommand cc, TransactionalEditingDomain editingDomain, final boolean top, int height, ChangeBoundsRequest request, + AbstractNodeEventResizeSelectionValidator validator) { + List<EventEnd> findEnds = EventEndHelper.findEndsFromSemanticOrdering(self); + RequestQuery rq = new RequestQuery(request); + + final Range oldRange = self.getVerticalRange(); + final int movedBound = top ? oldRange.getLowerBound() : oldRange.getUpperBound(); + final EObject sem = self.getSemanticTargetElement().get(); + final Predicate<SingleEventEnd> toMove = new Predicate<SingleEventEnd>() { + public boolean apply(SingleEventEnd input) { + return !input.getSemanticEvent().equals(sem); + } + }; + final Predicate<EventEnd> moved = new Predicate<EventEnd>() { + public boolean apply(EventEnd input) { + return EventEndHelper.getSingleEventEnd(input, sem).isStart() == top; + } + }; + + SequenceDiagram sequenceDiagram = self.getDiagram(); + for (CompoundEventEnd cee : Iterables.filter(Iterables.filter(findEnds, moved), CompoundEventEnd.class)) { + for (SingleEventEnd see : Iterables.filter(Lists.newArrayList(cee.getEventEnds()), toMove)) { + final ISequenceEvent ise = EventEndHelper.findISequenceEvent(see, sequenceDiagram); + if (ise == null) { + continue; + } + + final Range seeRange = ise.getVerticalRange(); + int lDelta = 0; + int uDelta = 0; + if (seeRange.getLowerBound() == movedBound && doNotMoveSourceOfReturnMessageOfReflexiveSyncCall(self, ise, rq)) { + lDelta = height; + if (new ISequenceEventQuery(ise).isReflectiveMessage() && getSelection(ise) == EditPart.SELECTED_NONE) { + // A reflexive message does not have the same lower and + // upper bound but both bound have to be moved like + // "normal" messages + uDelta = height; + } + } + if (seeRange.getUpperBound() == movedBound && doNotMoveTargetOfStartMessageOfReflexiveSyncCall(self, ise, rq)) { + uDelta = height; + if (new ISequenceEventQuery(ise).isReflectiveMessage() && getSelection(ise) == EditPart.SELECTED_NONE) { + // A reflexive message does not have the same lower and + // upper bound but both bound have to be moved like + // "normal" messages + lDelta = height; + } + } + + if ((seeRange.getLowerBound() + lDelta) <= (seeRange.getUpperBound() + uDelta)) { + final Range newRange = new Range(seeRange.getLowerBound() + lDelta, seeRange.getUpperBound() + uDelta); + if (ise instanceof Message && !hasBothEndMoving((Message) ise)) { + Message msg = (Message) ise; + addMessageReconnectionCommand(self, cc, editingDomain, msg, newRange, request, validator); + } else { + cc.compose(CommandFactory.createICommand(editingDomain, new SetVerticalRangeOperation(ise, newRange))); + } + } else { + cc.compose(org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE); + } + } + } + } + + private int getSelection(ISequenceElement ise) { + Map<?, ?> editPartRegistry = getHost().getViewer().getEditPartRegistry(); + Object object = editPartRegistry.get(ise.getNotationView()); + if (object instanceof EditPart) { + return ((EditPart) object).getSelected(); + } + return Integer.MIN_VALUE; + } + + private void addMessageReconnectionCommand(Execution self, CompositeTransactionalCommand cc, TransactionalEditingDomain editingDomain, Message message, Range newRange, + ChangeBoundsRequest request, AbstractNodeEventResizeSelectionValidator validator) { + + Set<Execution> executionsInMove = new RequestQuery(request).getExecutions(); + boolean invalidCommand = false; + + Predicate<EventEnd> filterCompoundEventEnd = new Predicate<EventEnd>() { + public boolean apply(EventEnd input) { + return input instanceof CompoundEventEnd; + } + }; + + SetMessageRangeOperation smrc = new SetMessageRangeOperation((Edge) message.getNotationView(), newRange); + + Lifeline selfLifeline = self.getLifeline().get(); + Rectangle logicalDelta = new RequestQuery(request).getLogicalDelta(); + Rectangle bounds = self.getProperLogicalBounds().getCopy(); + bounds.translate(logicalDelta.getLocation()); + bounds.resize(logicalDelta.getSize()); + Range thisFinalRange = Range.verticalRange(bounds); + + List<ISequenceEvent> toIgnore = Lists.newArrayList(); + boolean isReplyMessage = message.getKind() == Message.Kind.REPLY; + boolean isReflective = message.isReflective(); + ISequenceNode sourceElement = message.getSourceElement(); + ISequenceNode targetElement = message.getTargetElement(); + if (!isReplyMessage && isReflective && Iterables.any(EventEndHelper.findEndsFromSemanticOrdering(message), filterCompoundEventEnd) && targetElement == self) { + // Avoid target of the return message of a reflexive sync call to + // reconnect on its execution + toIgnore.add(self); + } + // if a verticalSpaceExpansion will occurs, ignore ISequenceEvent under + // the insertionPoint + if (needVerticalSpaceExpansion(validator, request)) { + Collection<ISequenceEvent> sequenceEventsUpperToInsertionTime = getSequenceEventsUpperToInsertionTime(self.getDiagram(), validator.getExpansionZone().getLowerBound()); + sequenceEventsUpperToInsertionTime.removeAll(executionsInMove); + toIgnore.addAll(sequenceEventsUpperToInsertionTime); + } + + Option<Lifeline> srcLifeline = message.getSourceLifeline(); + if (srcLifeline.some()) { + EventFinder srcFinder = new EventFinder(srcLifeline.get()); + srcFinder.setReconnection(true); + srcFinder.setEventsToIgnore(Predicates.in(toIgnore)); + srcFinder.setExpansionZone(validator.getExpansionZone()); + ISequenceEvent finalSrc = (srcLifeline.get() == selfLifeline && sourceElement == self) ? self : srcFinder.findMostSpecificEvent(newRange); + Range finalSrcRange = (srcLifeline.get() == selfLifeline && sourceElement == self) ? thisFinalRange : finalSrc.getVerticalRange(); + smrc.setSource(finalSrc.getNotationView(), new Rectangle(0, finalSrcRange.getLowerBound(), 0, finalSrcRange.width())); + } else { + Range finalSrcRange = Range.verticalRange(sourceElement.getProperLogicalBounds()); + smrc.setSource(sourceElement.getNotationView(), new Rectangle(0, finalSrcRange.getLowerBound(), 0, finalSrcRange.width())); + } + + toIgnore.clear(); + if (isReplyMessage && isReflective && Iterables.any(EventEndHelper.findEndsFromSemanticOrdering(message), filterCompoundEventEnd) && sourceElement == self) { + // Avoid target of the return message of a reflexive sync call to + // reconnect on its execution + toIgnore.add(self); + } + // if a verticalSpaceExpansion will occurs, ignore ISequenceEvent under + // the insertionPoint + if (needVerticalSpaceExpansion(validator, request)) { + Collection<ISequenceEvent> sequenceEventsUpperToInsertionTime = getSequenceEventsUpperToInsertionTime(self.getDiagram(), validator.getExpansionZone().getLowerBound()); + sequenceEventsUpperToInsertionTime.removeAll(executionsInMove); + toIgnore.addAll(sequenceEventsUpperToInsertionTime); + } + + Option<Lifeline> tgtLifeline = message.getTargetLifeline(); + if (tgtLifeline.some()) { + EventFinder tgtFinder = new EventFinder(tgtLifeline.get()); + tgtFinder.setReconnection(true); + tgtFinder.setEventsToIgnore(Predicates.in(toIgnore)); + tgtFinder.setExpansionZone(validator.getExpansionZone()); + ISequenceEvent finalTgt = (tgtLifeline.get() == selfLifeline && targetElement == self) ? self : tgtFinder.findMostSpecificEvent(newRange); + if (finalTgt == null) { + invalidCommand = true; + } else { + Range finalTgtRange = (tgtLifeline.get() == selfLifeline && targetElement == self) ? thisFinalRange : finalTgt.getVerticalRange(); + smrc.setTarget(finalTgt.getNotationView(), new Rectangle(0, finalTgtRange.getLowerBound(), 0, finalTgtRange.width())); + } + } else { + Range finalTgtRange = Range.verticalRange(targetElement.getProperLogicalBounds()); + smrc.setTarget(targetElement.getNotationView(), new Rectangle(0, finalTgtRange.getLowerBound(), 0, finalTgtRange.width())); + } + + if (invalidCommand) { + cc.compose(org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE); + } else { + cc.compose(CommandFactory.createICommand(editingDomain, smrc)); + } + + } + + private boolean hasBothEndMoving(Message smep) { + Set<Execution> movingExecutionEditPart = getMovingExecutions(); + return movingExecutionEditPart.contains(smep.getSourceElement()) && movingExecutionEditPart.contains(smep.getTargetElement()); + } + + private Set<Execution> getMovingExecutions() { + EditPartViewer viewer = getHost().getViewer(); + Set<Execution> movingExecutions = Sets.newHashSet(); + for (ExecutionEditPart eep : Iterables.filter(viewer.getSelectedEditParts(), ExecutionEditPart.class)) { + Execution exec = (Execution) eep.getISequenceEvent(); + movingExecutions.add(exec); + movingExecutions.addAll(exec.findLinkedExecutions(true)); + } + + ArrayList<Execution> subExecutions = Lists.newArrayList(); + for (Execution eep : movingExecutions) { + subExecutions.addAll(new ISequenceEventQuery(eep).getAllExecutions()); + } + movingExecutions.addAll(subExecutions); + return movingExecutions; + } + + /** + * Avoid moving the source of the return message of a reflexive synchronous + * message when resizing a parent execution. + * + * @param ise + * the sequence event to validate if it is a reflexive message we + * do not want to move + * @return the validation result of the message move. + */ + private boolean doNotMoveSourceOfReturnMessageOfReflexiveSyncCall(Execution self, ISequenceEvent ise, RequestQuery rq) { + return !(isMovedReflexiveMessage(ise, rq) && self.equals(((Message) ise).getSourceElement()) && getSelection(((Message) ise).getSourceElement()) == EditPart.SELECTED_NONE && getSelection(ise) == EditPart.SELECTED_NONE); + } + + /** + * Avoid moving the target of the invocation message of a reflexive + * synchronous message when resizing a parent execution. + * + * @param ise + * the sequence event to validate if it is a reflexive message we + * do not want to move + * @return the validation result of the message move. + */ + private boolean doNotMoveTargetOfStartMessageOfReflexiveSyncCall(Execution self, ISequenceEvent ise, RequestQuery rq) { + return !(isMovedReflexiveMessage(ise, rq) && self.equals(((Message) ise).getTargetElement()) && getSelection(((Message) ise).getTargetElement()) == EditPart.SELECTED_NONE && getSelection(ise) == EditPart.SELECTED_NONE); + } + + private boolean isMovedReflexiveMessage(ISequenceEvent ise, RequestQuery rq) { + return rq.isResize() && new ISequenceEventQuery(ise).isReflectiveMessage(); + } + + /* + * Feedback + */ + /** + * Show/update the horizontal feedback lines aligned on the top and bottom + * of the execution. + * <p> + * {@inheritDoc} + */ + @Override + protected void showChangeBoundsFeedback(ChangeBoundsRequest request) { + eraseChangeBoundsFeedback(request); + super.showChangeBoundsFeedback(request); + + ExecutionEditPart hostPart = (ExecutionEditPart) getHost(); + AbstractNodeEvent host = (AbstractNodeEvent) hostPart.getISequenceEvent(); + RequestQuery requestQuery = new RequestQuery(request); + + if (hostPart.getSelected() == EditPart.SELECTED_PRIMARY && requestQuery.isMove()) { + ISEComplexMoveValidator validator = ISEComplexMoveValidator.getOrCreateValidator(request, requestQuery, host); + if (validator != null) { + SequenceInteractionFeedBackBuilder feedBackBuilder = new SequenceInteractionFeedBackBuilder(validator, getFeedbackLayer(), hostPart); + for (Figure fig : feedBackBuilder.buildFeedBack()) { + addFeedback(fig); + guides.add(fig); + } + } + } else if (requestQuery.isResize()) { + AbstractNodeEventResizeSelectionValidator validator = AbstractNodeEventResizeSelectionValidator.getOrCreateValidator(request, host); + validator.validate(); + showResizeFeedBack(request); + feedBack(validator); + } + } + + /** + * Show feedback for computed conflicts during validation. + * + * @param validator + * the current resize validator. + */ + protected void feedBack(AbstractNodeEventResizeSelectionValidator validator) { + IFigure feedbackLayer = getFeedbackLayer(); + for (Integer conflict : validator.getInvalidPositions()) { + Point conflictingPosition = new Point(0, conflict); + conflictingPosition.performScale(GraphicalHelper.getZoom((IGraphicalEditPart) getHost())); + + Rectangle bounds = feedbackLayer.getBounds().getCopy(); + bounds.y = conflictingPosition.y; + bounds.height = 1; + + HorizontalGuide conflictGuide = new HorizontalGuide(ColorConstants.red, conflictingPosition.y); + conflictGuide.setBounds(bounds); + addFeedback(conflictGuide); + guides.add(conflictGuide); + } + + Range expansionZone = validator.getExpansionZone(); + if (expansionZone != null && !expansionZone.isEmpty() && expansionZone.width() != 0) { + Rectangle screenRange = new Rectangle(0, expansionZone.getLowerBound(), 0, expansionZone.width()); + screenRange.performScale(GraphicalHelper.getZoom((IGraphicalEditPart) getHost())); + Range expand = Range.verticalRange(screenRange); + + Rectangle bounds = feedbackLayer.getBounds().getCopy(); + bounds.height = expand.width(); + bounds.y = expand.getLowerBound(); + + RangeGuide expansion = new RangeGuide(validator.isValid() ? ColorConstants.blue : ColorConstants.red, expand, true); + expansion.setBounds(bounds); + addFeedback(expansion); + guides.add(expansion); + } + } + + /** + * Show feedback for resize. + * + * @param request + * the current resize request. + */ + protected void showResizeFeedBack(ChangeBoundsRequest request) { + // TODO Refactor this and share the code with + // SequenceMessageEditPolicy + // and put in a graphical edit policy to inherit instead of copy the + // feedback utility methods. + + Rectangle oldBounds = getHostAbsoluteBounds().getCopy(); + Rectangle newBounds = request.getTransformedRectangle(oldBounds).getCopy(); + Rectangle execBounds = newBounds.getCopy(); + + FreeformViewport viewport = FigureUtilities.getFreeformViewport(getHostFigure()); + if (viewport != null) { + oldBounds.translate(viewport.getViewLocation()); + newBounds.translate(viewport.getViewLocation()); + } + + if (getHost() instanceof ExecutionEditPart && newBounds.height > 0) { + ISequenceEvent iSequenceEvent = ((ISequenceEventEditPart) getHost()).getISequenceEvent(); + GraphicalHelper.screen2logical(newBounds, (IGraphicalEditPart) getHost()); + GraphicalHelper.screen2logical(oldBounds, (IGraphicalEditPart) getHost()); + Range oldRange = Range.verticalRange(oldBounds); + Range execRange = Range.verticalRange(newBounds); + Range fullFinalRange = Range.verticalRange(newBounds); + List<ISequenceEvent> delimitingMessages = EventEndHelper.getCompoundEvents(iSequenceEvent); + if (delimitingMessages.size() > 0) { + ISequenceEvent callMessage = delimitingMessages.get(0); + Range callMsgRange = callMessage.getVerticalRange(); + if (request.isConstrainedMove()) { + fullFinalRange = new Range(oldRange.getLowerBound() - callMsgRange.width(), fullFinalRange.getUpperBound()); + } else { + fullFinalRange = new Range(fullFinalRange.getLowerBound() - callMsgRange.width(), fullFinalRange.getUpperBound()); + } + } + if (delimitingMessages.size() > 1) { + ISequenceEvent returnMessage = delimitingMessages.get(1); + Range returnMsgRange = returnMessage.getVerticalRange(); + if (request.isConstrainedResize()) { + fullFinalRange = new Range(fullFinalRange.getLowerBound(), oldRange.getUpperBound() + returnMsgRange.width()); + } else { + fullFinalRange = new Range(fullFinalRange.getLowerBound(), fullFinalRange.getUpperBound() + returnMsgRange.width()); + } + } + newBounds = new Rectangle(0, fullFinalRange.getLowerBound(), 0, fullFinalRange.width()); + execBounds = new Rectangle(0, execRange.getLowerBound(), 0, execRange.width()); + + if (iSequenceEvent.isLogicallyInstantaneous() && delimitingMessages.isEmpty()) { + execBounds.y = execBounds.getCenter().y; + execBounds.height = 1; + + newBounds = execBounds.getCopy(); + } + + GraphicalHelper.logical2screen(newBounds, (IGraphicalEditPart) getHost()); + GraphicalHelper.logical2screen(execBounds, (IGraphicalEditPart) getHost()); + } + + Point topLocation = new Point(1, newBounds.getTop().y); + Point bottomLocation = new Point(1, newBounds.getBottom().y); + + Rectangle bounds = getFeedbackLayer().getBounds().getCopy(); + execBounds.height = Math.max(execBounds.height, 0); + + Figure execGuide = new RangeGuide(EXECUTION_FEEDBACK_COLOR, Range.verticalRange(execBounds), false); + bounds.height = execBounds.height + 1; + bounds.y = execBounds.y; + execGuide.setBounds(bounds); + addFeedback(execGuide); + guides.add(execGuide); + + if (execBounds.y != topLocation.y) { + Figure topGuide = new HorizontalGuide(EXECUTION_FEEDBACK_COLOR, topLocation.y); + bounds.height = 1; + bounds.y = topLocation.y; + topGuide.setBounds(bounds); + addFeedback(topGuide); + guides.add(topGuide); + } + + if (execBounds.bottom() != bottomLocation.y) { + bounds = getFeedbackLayer().getBounds().getCopy(); + Figure bottomGuide = new HorizontalGuide(EXECUTION_FEEDBACK_COLOR, bottomLocation.y); + bounds.height = 1; + bounds.y = bottomLocation.y; + bottomGuide.setBounds(bounds); + addFeedback(bottomGuide); + guides.add(bottomGuide); + } + } + + private Rectangle getHostAbsoluteBounds() { + Rectangle bounds = getHostFigure().getBounds().getCopy(); + getHostFigure().getParent().translateToAbsolute(bounds); + return bounds; + } + + private void removeFeedBackOnGuides() { + if (guides != null && !guides.isEmpty()) { + for (Figure hGuide : guides) { + removeFeedback(hGuide); + } + guides.clear(); + } + } + + /** + * Remove the horizontal feedback lines. + * <p> + * {@inheritDoc} + */ + @Override + protected void eraseChangeBoundsFeedback(ChangeBoundsRequest request) { + removeFeedBackOnGuides(); + super.eraseChangeBoundsFeedback(request); + } + + /** + * Cancel horizontal changes of the given request. + * + * @param request + * a request. + */ + protected void cancelHorizontalDelta(ChangeBoundsRequest request) { + if (request == null) { + return; + } + + Point moveDelta = request.getMoveDelta(); + if (moveDelta != null) { + request.setMoveDelta(new Point(0, moveDelta.y)); + } + + Dimension sizeDelta = request.getSizeDelta(); + if (sizeDelta != null) { + request.setSizeDelta(new Dimension(0, sizeDelta.height)); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/ExecutionSemanticEditPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/ExecutionSemanticEditPolicy.java new file mode 100644 index 0000000000..eaccc0e508 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/ExecutionSemanticEditPolicy.java @@ -0,0 +1,336 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * 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.ColorConstants; +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 com.google.common.collect.Iterables; + +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.description.NodeMapping; +import org.eclipse.sirius.description.style.NodeStyleDescription; +import org.eclipse.sirius.description.style.SquareDescription; +import org.eclipse.sirius.description.style.WorkspaceImageDescription; +import org.eclipse.sirius.diagram.internal.edit.policies.DNode2ItemSemanticEditPolicy; +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.tools.api.layout.GraphicalHelper; +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutUtils; +import org.eclipse.sirius.diagram.ui.tools.internal.util.EditPartQuery; + +/** + * 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() : ""; + 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((IGraphicalEditPart) getHost())); + Range forbiddenRange = Range.verticalRange(screenRange); + + forbiddenRangeArea = new RangeGuide(ColorConstants.red, 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; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/FrameAwareContainerCreationPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/FrameAwareContainerCreationPolicy.java new file mode 100644 index 0000000000..0edd40a338 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/FrameAwareContainerCreationPolicy.java @@ -0,0 +1,276 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.GraphicalEditPart; +import org.eclipse.gef.LayerConstants; +import org.eclipse.gef.Request; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.editparts.LayerManager; +import org.eclipse.gef.requests.CreateRequest; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.description.tool.AbstractToolDescription; +import org.eclipse.sirius.description.tool.ContainerCreationDescription; +import org.eclipse.sirius.diagram.graphical.edit.policies.ContainerCreationEditPolicy; +import org.eclipse.sirius.diagram.graphical.edit.policies.CreationUtil; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +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.operation.VerticalSpaceExpansion; +import org.eclipse.sirius.diagram.sequence.description.tool.CombinedFragmentCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InteractionUseCreationTool; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.command.SequenceDelegatingCommandFactory; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceDiagramEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.FrameCreationValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.CreateRequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.tools.api.command.DoNothingCommand; +import org.eclipse.sirius.diagram.tools.api.editor.DDiagramEditor; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactory; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactoryProvider; + +/** + * An extension of the standard Sirius ContainerCreationEditPolicy which + * knows how to handle the specific tools use to create frames (i.e. Interaction + * Uses and Combined Fragments). + * + * @author pcdavid + */ +public class FrameAwareContainerCreationPolicy extends ContainerCreationEditPolicy { + + /** Label use for the Interaction Use creation. */ + public static final String INTERACTION_USE_CREATION_CMD_LABEL = "Interaction Use creation"; + + /** Label use for the Combined Fragment creation. */ + public static final String COMBINED_FRAGMENT_CREATION_CMD_LABEL = "Combined Fragment creation"; + + /** + * Additional figures for feedback. + */ + protected Collection<Figure> guides = Lists.newArrayList(); + + /** + * {@inheritDoc} + */ + @Override + protected Command getCreateContainerOnDiagramCommand(CreateRequest request, ContainerCreationDescription ccdTool, DDiagram diagram) { + Command result; + + boolean frameCreationTool = ccdTool instanceof InteractionUseCreationTool || ccdTool instanceof CombinedFragmentCreationTool; + if (frameCreationTool && getHost() instanceof SequenceDiagramEditPart && diagram instanceof SequenceDDiagram) { + SequenceDiagramEditPart sdep = (SequenceDiagramEditPart) getHost(); + TransactionalEditingDomain domain = sdep.getEditingDomain(); + Diagram gmfDiagram = sdep.getDiagramView(); + SequenceDiagram sequenceDiagram = ISequenceElementAccessor.getSequenceDiagram(gmfDiagram).get(); + FrameCreationValidator creationValidator = FrameCreationValidator.getOrCreateValidator(sequenceDiagram, ccdTool, new CreateRequestQuery(request, sdep)); + + if (creationValidator.isValid()) { + EventEnd startingEndPredecessor = creationValidator.getStartingEndPredecessor(); + EventEnd finishingEndPredecessor = creationValidator.getFinishingEndPredecessor(); + List<EObject> coverage = creationValidator.getCoverage(); + Range expansionZone = creationValidator.getExpansionZone(); + + CreationUtil creationUtil = new CreationUtil(request, getDiagramCommandFactory(startingEndPredecessor, finishingEndPredecessor, coverage, getCreationRange(request)), + getRealLocation(request), getRealSize(ccdTool, request), getHost()); + result = creationUtil.getContainerCreationDescription(diagram, ccdTool); + + // Add a vertical expansion command if we do inclusion + if (expansionZone != null && !expansionZone.isEmpty() && result != null && result.canExecute()) { + // Shift the element to not include int the range of the + // AbstractFrame to create + VerticalSpaceExpansion verticalSpaceExpansion = new VerticalSpaceExpansion(sequenceDiagram, expansionZone, 0, Collections.<ISequenceEvent> emptyList()); + ICommand expandSubEventsCmd = CommandFactory.createICommand(domain, verticalSpaceExpansion); + + result = new ICommandProxy(expandSubEventsCmd).chain(result); + } + if (result != null) { + if (ccdTool instanceof InteractionUseCreationTool) { + result.setLabel(INTERACTION_USE_CREATION_CMD_LABEL); + } else if (ccdTool instanceof CombinedFragmentCreationTool) { + result.setLabel(COMBINED_FRAGMENT_CREATION_CMD_LABEL); + } + } + } else { + result = creationValidator.getCoverage().isEmpty() ? DoNothingCommand.INSTANCE : UnexecutableCommand.INSTANCE; + } + } else { + result = super.getCreateContainerOnDiagramCommand(request, ccdTool, diagram); + } + return result; + } + + /** + * Overridden to show the feedback of the expansion zone for + * InteractionUse/CombinedFragment creation when there is inclusion of + * existing sequence events in its creation range and vertical space + * expansion is needed for some sequence events. + * + * + * {@inheritDoc} + */ + @Override + public void showTargetFeedback(Request request) { + eraseTargetFeedback(request); + if (request instanceof CreateRequest && this.getHost() instanceof SequenceDiagramEditPart) { + SequenceDiagramEditPart sdep = (SequenceDiagramEditPart) getHost(); + CreateRequest createRequest = (CreateRequest) request; + Option<ISequenceElement> seqDiag = ISequenceElementAccessor.getISequenceElement((View) this.getHost().getModel()); + AbstractToolDescription tool = getTool(createRequest); + if (seqDiag.some() && seqDiag.get() instanceof SequenceDiagram && tool instanceof InteractionUseCreationTool || tool instanceof CombinedFragmentCreationTool) { + FrameCreationValidator validator = FrameCreationValidator.getOrCreateValidator((SequenceDiagram) seqDiag.get(), (ContainerCreationDescription) tool, new CreateRequestQuery( + createRequest, sdep)); + if (validator != null) { + SequenceInteractionFeedBackBuilder feedBackBuilder = new SequenceInteractionFeedBackBuilder(validator, getFeedbackLayer(), (IGraphicalEditPart) getHost()); + for (Figure fig : feedBackBuilder.buildFeedBack()) { + addFeedback(fig); + guides.add(fig); + } + } + } + } + } + + /** + * Overridden to erase feedback for AbstractFrame creation request. + * + * {@inheritDoc} + */ + @Override + public void eraseTargetFeedback(Request request) { + removeFeedBackOnGuides(); + } + + private void removeFeedBackOnGuides() { + if (guides != null && !guides.isEmpty()) { + for (Figure hGuide : guides) { + removeFeedback(hGuide); + } + guides.clear(); + } + } + + /** + * Add a IFigure to the feedbackLayer. + * + * @param figure + * the feedback figure to add + */ + protected void addFeedback(IFigure figure) { + getFeedbackLayer().add(figure); + } + + /** + * Returns the layer used for displaying feedback. + * + * @return the feedback layer + */ + protected IFigure getFeedbackLayer() { + return getLayer(LayerConstants.FEEDBACK_LAYER); + } + + /** + * Convenience method to return the host's Figure. + * + * @return The host GraphicalEditPart's Figure + */ + protected IFigure getHostFigure() { + return ((GraphicalEditPart) getHost()).getFigure(); + } + + /** + * Obtains the specified layer. + * + * @param layer + * the key identifying the layer + * @return the requested layer + */ + protected IFigure getLayer(Object layer) { + return LayerManager.Helper.find(getHost()).getLayer(layer); + } + + /** + * Removes the specified <code>Figure</code> from the + * {@link LayerConstants#FEEDBACK_LAYER}. + * + * @param figure + * the feedback to remove + */ + protected void removeFeedback(IFigure figure) { + getFeedbackLayer().remove(figure); + } + + private Range getCreationRange(CreateRequest request) { + Point realLocation = getRealLocation(request); + Range result = Range.emptyRange(); + if (request.getSize() != null) { + result = new Range(realLocation.y, realLocation.y + request.getSize().height); + } + return result; + } + + private Dimension getRealSize(ContainerCreationDescription ccdTool, CreateRequest request) { + Dimension realSize = request.getSize(); + if (realSize == null) { + realSize = new Dimension(0, 0); + if (ccdTool instanceof InteractionUseCreationTool) { + realSize.height = LayoutConstants.DEFAULT_INTERACTION_USE_HEIGHT; + } else if (ccdTool instanceof CombinedFragmentCreationTool) { + realSize.height = LayoutConstants.DEFAULT_COMBINED_FRAGMENT_HEIGHT; + } + } + return realSize; + } + + private IDiagramCommandFactory getDiagramCommandFactory(EventEnd startingEndPredecessor, EventEnd finishingEndPredecessor, List<EObject> coverage, Range creationRange) { + SequenceDiagram seqDiag = null; + EditPart host = getHost(); + if (host instanceof SequenceDiagramEditPart) { + seqDiag = ((SequenceDiagramEditPart) host).getSequenceDiagram(); + } + + final DDiagramEditor diagramEditor = (DDiagramEditor) host.getViewer().getProperty(DDiagramEditor.EDITOR_ID); + if (diagramEditor == null || seqDiag == null) { + return null; + } + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(seqDiag.getNotationDiagram()); + final Object adapter = diagramEditor.getAdapter(IDiagramCommandFactoryProvider.class); + final IDiagramCommandFactoryProvider cmdFactoryProvider = (IDiagramCommandFactoryProvider) adapter; + final IDiagramCommandFactory diagramCommandFactory = cmdFactoryProvider.getCommandFactory(domain); + return new SequenceDelegatingCommandFactory(diagramCommandFactory, domain, seqDiag, startingEndPredecessor, finishingEndPredecessor, coverage); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/ISEComplexMoveCommandBuilder.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/ISEComplexMoveCommandBuilder.java new file mode 100644 index 0000000000..51ecc60087 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/ISEComplexMoveCommandBuilder.java @@ -0,0 +1,332 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.Collection; +import java.util.Map; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.diagram.ui.commands.SetBoundsCommand; +import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages; +import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand; +import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter; +import org.eclipse.gmf.runtime.notation.Edge; + +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.ISequenceNodeMoveOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.ReparentExecutionOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SetMessageRangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.VerticalSpaceExpansion; +import org.eclipse.sirius.diagram.sequence.business.internal.util.EventFinder; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ShiftMessagesOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.ISEComplexMoveValidator; +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.tools.internal.edit.command.CommandFactory; + +/** + * A builder for complex sequence move commands. + * + * @author mporhel + */ +public class ISEComplexMoveCommandBuilder { + + private final TransactionalEditingDomain editingDomain; + + private final String label; + + private final RequestQuery requestQuery; + + private final ISEComplexMoveValidator validator; + + /** + * Constructor. + * + * @param editingDomain + * tue editing domain + * @param label + * the label of the command to build + * @param requestQuery + * a query corresponding to the current request + * @param validator + * the move validator. + */ + public ISEComplexMoveCommandBuilder(TransactionalEditingDomain editingDomain, String label, RequestQuery requestQuery, ISEComplexMoveValidator validator) { + this.editingDomain = editingDomain; + this.label = label; + this.requestQuery = requestQuery; + this.validator = validator; + } + + /** + * Build the command. + * + * @return a composite transactional command. + */ + public CompositeTransactionalCommand buildCommand() { + CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(editingDomain, label); + + Integer vMove = requestQuery.getLogicalDelta().y; + expandDiagram(ctc, vMove); + handleNodes(ctc, vMove); + handleMessages(ctc, vMove); + + return ctc; + } + + private void handleMessages(CompositeTransactionalCommand ctc, Integer vMove) { + + shiftMessages(ctc, vMove); + resizeMessages(ctc, vMove); + reconnectMessages(ctc, vMove); + + } + + private void handleNodes(CompositeTransactionalCommand ctc, Integer vMove) { + Collection<ISequenceNode> seqNodesToMove = Lists.newArrayList(validator.getSequenceNodeToMove()); + Map<AbstractNodeEvent, ISequenceEvent> reparents = Maps.newHashMap(); + + computeReparents(seqNodesToMove, reparents); + + moveNodes(ctc, vMove, seqNodesToMove); + reparentNodes(ctc, vMove, reparents); + } + + private void reconnectMessages(CompositeTransactionalCommand ctc, Integer vMove) { + for (Reconnection reconnection : computeReconnections()) { + Message message = reconnection.getMessage(); + ISequenceNode source = reconnection.getSource(); + ISequenceNode target = reconnection.getTarget(); + + Rectangle srcBounds = source.getProperLogicalBounds(); + Rectangle tgtBounds = target.getProperLogicalBounds(); + Collection<ISequenceEvent> movedElements = validator.getMovedElements(); + if (movedElements.contains(source)) { + srcBounds = requestQuery.getLogicalTransformedRectangle(srcBounds); + } + if (movedElements.contains(target)) { + tgtBounds = requestQuery.getLogicalTransformedRectangle(tgtBounds); + } + + Range newRange = movedElements.contains(message) ? message.getVerticalRange().shifted(vMove) : message.getVerticalRange(); + SetMessageRangeOperation smrc = new SetMessageRangeOperation((Edge) message.getNotationView(), newRange); + smrc.setSource(source.getNotationNode(), srcBounds); + smrc.setTarget(target.getNotationNode(), tgtBounds); + ctc.compose(CommandFactory.createICommand(editingDomain, smrc)); + } + } + + private void reparentNodes(CompositeTransactionalCommand ctc, Integer vMove, Map<AbstractNodeEvent, ISequenceEvent> reparents) { + for (Map.Entry<AbstractNodeEvent, ISequenceEvent> entry : reparents.entrySet()) { + ISequenceEvent newParent = entry.getValue(); + ctc.compose(CommandFactory.createICommand(editingDomain, new ReparentExecutionOperation(entry.getKey(), newParent))); + + // Compute the absolute bounds implied by the requested move. + Rectangle realLocation = entry.getKey().getProperLogicalBounds(); + if (validator.getMovedElements().contains(entry.getKey())) { + realLocation = requestQuery.getLogicalTransformedRectangle(realLocation); + } + + // Adjust x coordinate depending on the nature of the final parent. + Rectangle parentBounds = newParent.getProperLogicalBounds(); + if (validator.getMovedElements().contains(newParent)) { + parentBounds = requestQuery.getLogicalTransformedRectangle(parentBounds); + } + + Range futureRange = validator.getRangeFunction().apply(entry.getKey()); + realLocation.y = futureRange.getLowerBound(); + + // Make the coordinates relative to the final parent's figure. + final Point parentOrigin = parentBounds.getLocation(); + final Dimension d = realLocation.getTopLeft().getDifference(parentOrigin); + Point locationOnFinalParent = new Point(realLocation.x, d.height); + + // Create the command to apply the change. + final ICommand moveCommand = new SetBoundsCommand(ctc.getEditingDomain(), DiagramUIMessages.Commands_MoveElement, new EObjectAdapter(entry.getKey().getNotationNode()), locationOnFinalParent); + ctc.compose(moveCommand); + + } + } + + private void shiftMessages(CompositeTransactionalCommand ctc, Integer vMove) { + ICommand messageMoveCommand = CommandFactory.createICommand(editingDomain, new ShiftMessagesOperation(validator.getMessageToMove(), validator.getMovedElements(), vMove, false, true)); + ctc.add(messageMoveCommand); + } + + private void resizeMessages(CompositeTransactionalCommand ctc, Integer vMove) { + for (Message msg : Iterables.concat(validator.getResizedStartMessages(), validator.getResizedEndMessages())) { + SetMessageRangeOperation smrc = new SetMessageRangeOperation(msg.getNotationEdge(), validator.getRangeFunction().apply(msg)); + + ISequenceEvent src = (ISequenceEvent) msg.getSourceElement(); + Range srcRange = validator.getRangeFunction().apply(src); + smrc.setSource(src.getNotationView(), new Rectangle(0, srcRange.getLowerBound(), 0, srcRange.getUpperBound())); + + ISequenceEvent tgt = (ISequenceEvent) msg.getTargetElement(); + Range tgtRange = validator.getRangeFunction().apply(tgt); + smrc.setTarget(tgt.getNotationView(), new Rectangle(0, tgtRange.getLowerBound(), 0, tgtRange.getUpperBound())); + + ICommand resizeStartMessages = CommandFactory.createICommand(editingDomain, smrc); + ctc.add(resizeStartMessages); + } + } + + private void moveNodes(CompositeTransactionalCommand ctc, Integer vMove, Collection<ISequenceNode> seqNodesToMove) { + ICommand moveExecCmd = CommandFactory.createICommand(editingDomain, new ISequenceNodeMoveOperation(seqNodesToMove, vMove)); + ctc.compose(moveExecCmd); + ctc.setLabel(moveExecCmd.getLabel()); + } + + private void expandDiagram(CompositeTransactionalCommand ctc, Integer vMove) { + if (validator.getExpansionZone() != null && !validator.getExpansionZone().isEmpty()) { + ctc.compose(CommandFactory.createICommand(editingDomain, new VerticalSpaceExpansion(validator.getDiagram(), validator.getExpansionZone(), vMove, validator.getMovedElements()))); + } + } + + private void computeReparents(Collection<ISequenceNode> sequenceNodesToMove, Map<AbstractNodeEvent, ISequenceEvent> reparents) { + // reparent directly moved execution + Collection<AbstractNodeEvent> movedExecutions = Lists.newArrayList(Iterables.filter(sequenceNodesToMove, AbstractNodeEvent.class)); + + // reparent unmoved executions + Collection<AbstractNodeEvent> unmovedExecutions = Lists.newArrayList(Iterables.filter(validator.getDiagram().getAllAbstractNodeEvents(), + Predicates.not(Predicates.in(validator.getMovedElements())))); + + for (AbstractNodeEvent execToReparent : Iterables.concat(movedExecutions, unmovedExecutions)) { + ISequenceEvent potentialParent = getNewParent(execToReparent, reparents); + if (potentialParent instanceof ISequenceNode && !potentialParent.equals(execToReparent.getHierarchicalParentEvent())) { + reparents.put(execToReparent, potentialParent); + sequenceNodesToMove.remove(execToReparent); + } + } + } + + private Collection<Reconnection> computeReconnections() { + Collection<Reconnection> reconnections = Lists.newArrayList(); + // Reconnect moved and unmoved messages + for (Message message : validator.getDiagram().getAllMessages()) { + + // check source change + ISequenceNode sourceElement = message.getSourceElement(); + ISequenceNode newSource = getNewReconnectionEnd(message, sourceElement); + + // check target change + ISequenceNode targetElement = message.getTargetElement(); + ISequenceNode newTarget = targetElement; + if (targetElement instanceof ISequenceEvent) { + newTarget = getNewReconnectionEnd(message, targetElement); + } + + if (!sourceElement.equals(newSource) || !targetElement.equals(newTarget)) { + Reconnection rec = new Reconnection(message, newSource, newTarget); + reconnections.add(rec); + } + } + return reconnections; + } + + private ISequenceEvent getNewParent(AbstractNodeEvent movedExec, Map<AbstractNodeEvent, ISequenceEvent> reparents) { + EventFinder newParentFinder = new EventFinder(movedExec.getLifeline().get()); + newParentFinder.setReparent(true); + newParentFinder.setVerticalRangefunction(validator.getRangeFunction()); + newParentFinder.setEventsToIgnore(Predicates.equalTo((ISequenceEvent) movedExec)); + newParentFinder.setReparented(reparents); + Range futureRange = validator.getRangeFunction().apply(movedExec); + Range lookedRange = new Range(futureRange.getLowerBound(), futureRange.getLowerBound()); + + if (movedExec instanceof State && movedExec.isLogicallyInstantaneous()) { + int mid = futureRange.middleValue(); + lookedRange = new Range(mid, mid); + } + + ISequenceEvent potentialParent = newParentFinder.findMostSpecificEvent(lookedRange); + return potentialParent; + } + + private ISequenceNode getNewReconnectionEnd(Message message, ISequenceNode actualEnd) { + boolean compoundEnds = false; + if (message.isReflective() && actualEnd instanceof Execution) { + Execution exec = (Execution) actualEnd; + compoundEnds = exec.getLinkedMessages().contains(message); + } + + boolean bothMoved = false; + bothMoved = validator.getMovedElements().contains(message) && validator.getMovedElements().contains(actualEnd); + + if (!compoundEnds && !bothMoved && actualEnd instanceof ISequenceEvent) { + EventFinder newEndFinder = new EventFinder(actualEnd.getLifeline().get()); + newEndFinder.setReconnection(true); + + Range lookedRange = validator.getRangeFunction().apply(message); + newEndFinder.setVerticalRangefunction(validator.getRangeFunction()); + + ISequenceEvent potentialEnd = newEndFinder.findMostSpecificEvent(lookedRange); + if (potentialEnd instanceof ISequenceNode) { + return (ISequenceNode) potentialEnd; + } + } + return actualEnd; + } + + /** + * Wrapper to handle reconnection. + * + * @author mporhel + * + */ + public static class Reconnection { + final Message message; + + final ISequenceNode source; + + final ISequenceNode target; + + /** + * Constructor. + * + * @param message + * message to reconnect. + * @param source + * source after reconnection. + * @param target + * target after reconnection. + */ + public Reconnection(Message message, ISequenceNode source, ISequenceNode target) { + this.message = message; + this.source = source; + this.target = target; + } + + public Message getMessage() { + return message; + } + + public ISequenceNode getSource() { + return source; + } + + public ISequenceNode getTarget() { + return target; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/InstanceRoleResizableEditPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/InstanceRoleResizableEditPolicy.java new file mode 100644 index 0000000000..0b8c1fe4bd --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/InstanceRoleResizableEditPolicy.java @@ -0,0 +1,316 @@ +/******************************************************************************* + * Copyright (c) 2010, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.commands.operations.IUndoableOperation; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.Request; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.requests.AlignmentRequest; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.common.core.command.IdentityCommand; +import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; +import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages; +import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand; +import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.graphical.edit.policies.AirResizableEditPolicy; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.LostMessageEnd; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.MoveViewOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ResizeViewOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InstanceRoleEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.InstanceRoleMoveValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.InstanceRoleResizeValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.tools.api.command.DoNothingCommand; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; + +/** + * A specific AirResizableEditPolicy to manage instance roles move & resize + * requests. Constraints : + * <ul> + * <li>LayoutConstants#LIFELINES_START_X left margin must be always respected</li> + * <li>LayoutConstants#LIFELINES_START_Y top margin must be always respected</li> + * <li>LayoutConstants#LIFELINES_MIN_X_GAP minimum gap between neighbors + * InstanceRoles must be always respected</li> + * <li>Resize must not be possible to the top if + * LayoutConstants#LIFELINES_START_Y is not respected</li> + * <li>Resize must not be possible to the left if + * LayoutConstants#LIFELINES_START_X is not respected</li> + * <li>Drop a InstanceRole in another index of its initial index in the left to + * right ordered list of InstanceRole must not shift InstanceRole at it left but + * only shift InstanceRole at it right</li> + * </ul> + * + * @author smonnier, edugueperoux + */ +public class InstanceRoleResizableEditPolicy extends AirResizableEditPolicy { + + private static final String INSTANCE_ROLE_MOVE_COMMAND_NAME = "InstanceRole move"; + + private static final String INSTANCE_ROLE_RESIZE_COMMAND_NAME = "InstanceRole resize"; + + /** + * Manage move requests. + * + * @param request + * to move InstanceRoleEditPart's figure + * + * @return {@link Command} to move InstanceRoleEditPart's figure + */ + @Override + protected Command getMoveCommand(ChangeBoundsRequest request) { + cancelVerticalMoveDelta(request); + Command result = DoNothingCommand.INSTANCE; + RequestQuery requestQuery = new RequestQuery(request); + if (getHost().getSelected() == EditPart.SELECTED_PRIMARY && requestQuery.isMove()) { + InstanceRoleMoveValidator validator = new InstanceRoleMoveValidator(); + List<InstanceRole> instanceRoles = requestQuery.getInstanceRoles(); + validator.setSequenceElements(instanceRoles); + + result = UnexecutableCommand.INSTANCE; + if (validator.isValid(request)) { + result = getMoveCommand(validator, requestQuery); + } + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + protected void showChangeBoundsFeedback(ChangeBoundsRequest request) { + cancelVerticalMoveDelta(request); + super.showChangeBoundsFeedback(request); + } + + private Command getMoveCommand(InstanceRoleMoveValidator validator, RequestQuery requestQuery) { + GraphicalEditPart host = (GraphicalEditPart) getHost(); + TransactionalEditingDomain transactionalEditingDomain = host.getEditingDomain(); + CompositeTransactionalCommand compositeCommand = new CompositeTransactionalCommand(transactionalEditingDomain, INSTANCE_ROLE_MOVE_COMMAND_NAME); + ICommand moveCmds = getMoveViewCommands(transactionalEditingDomain, validator.getMoveDeltas()); + if (moveCmds != null && moveCmds.canExecute()) { + compositeCommand.compose(moveCmds); + } + postProcessDefaultCommand((InstanceRoleEditPart) getHost(), compositeCommand, requestQuery); + + ICommand solution = compositeCommand; + if (!solution.canExecute()) { + solution = IdentityCommand.INSTANCE; + } + return new ICommandProxy(solution); + } + + /** + * Manage resize requests. + * + * @param request + * to resize InstanceRoleEditPart's figure + * + * @return {@link Command} to resize InstanceRoleEditPart's figure + */ + @Override + protected Command getResizeCommand(ChangeBoundsRequest request) { + + Command result = DoNothingCommand.INSTANCE; + RequestQuery requestQuery = new RequestQuery(request); + if (getHost().getSelected() == EditPart.SELECTED_PRIMARY && requestQuery.isResize()) { + InstanceRoleResizeValidator validator = new InstanceRoleResizeValidator(); + List<InstanceRole> instanceRoles = requestQuery.getInstanceRoles(); + validator.setSequenceElements(instanceRoles); + + result = UnexecutableCommand.INSTANCE; + if (validator.isValid(request)) { + result = getResizeCommand(validator, new RequestQuery(request)); + } + } + + return result; + } + + private Command getResizeCommand(InstanceRoleResizeValidator validator, RequestQuery requestQuery) { + GraphicalEditPart host = (GraphicalEditPart) getHost(); + TransactionalEditingDomain transactionalEditingDomain = host.getEditingDomain(); + + CompositeTransactionalCommand compositeCommand = new CompositeTransactionalCommand(transactionalEditingDomain, INSTANCE_ROLE_RESIZE_COMMAND_NAME); + ICommand moveViewCmd = getMoveViewCommands(transactionalEditingDomain, validator.getMoveDeltas()); + if (moveViewCmd != null && moveViewCmd.canExecute()) { + compositeCommand.compose(moveViewCmd); + } + ICommand resizeViewCmd = getResizeViewCommands(transactionalEditingDomain, validator.getSizeDeltas()); + if (resizeViewCmd != null && resizeViewCmd.canExecute()) { + compositeCommand.compose(resizeViewCmd); + } + + postProcessDefaultCommand((InstanceRoleEditPart) getHost(), compositeCommand, requestQuery); + + ICommand solution = compositeCommand; + if (!solution.canExecute()) { + solution = IdentityCommand.INSTANCE; + } + return new ICommandProxy(solution); + } + + /** + * Get {@link MoveViewOperation} composition from moveDeltas map. + * + * @param transactionalEditingDomain + * TransactionalEditingDomain used to construct the result + * ICommand + * @param moveDeltas + * + * @return composition of {@link MoveViewOperation} or null if move/resize + * is not needed + */ + private ICommand getMoveViewCommands(TransactionalEditingDomain transactionalEditingDomain, Map<InstanceRole, Point> moveDeltas) { + CompositeTransactionalCommand compositeCommand = new CompositeTransactionalCommand(transactionalEditingDomain, ""); + + // handle move lost nodes + Multimap<InstanceRole, LostMessageEnd> lostNodes = HashMultimap.create(); + + InstanceRoleEditPart irep = (InstanceRoleEditPart) getHost(); + SequenceDiagram diagram = irep.getInstanceRole().getDiagram(); + for (Message msg : diagram.getAllMessages()) { + ISequenceNode sourceElement = msg.getSourceElement(); + Option<Lifeline> sourceLifeline = sourceElement.getLifeline(); + ISequenceNode targetElement = msg.getTargetElement(); + Option<Lifeline> targetLifeline = targetElement.getLifeline(); + if (sourceLifeline.some() && targetElement instanceof LostMessageEnd) { + lostNodes.put(sourceLifeline.get().getInstanceRole(), (LostMessageEnd) targetElement); + } else if (sourceElement instanceof LostMessageEnd && targetLifeline.some()) { + lostNodes.put(targetLifeline.get().getInstanceRole(), (LostMessageEnd) sourceElement); + } + } + + // Move commands to shift siblings InstanceRoles + for (Entry<InstanceRole, Point> entry : moveDeltas.entrySet()) { + Point delta = entry.getValue(); + if (delta.x != 0 || delta.y != 0) { + InstanceRole instanceRole = entry.getKey(); + addMoveViewCommand(transactionalEditingDomain, compositeCommand, delta, instanceRole); + + // move lost message ends + for (LostMessageEnd lme : lostNodes.get(instanceRole)) { + addMoveViewCommand(transactionalEditingDomain, compositeCommand, delta, lme); + } + + } + } + return compositeCommand; + } + + private void addMoveViewCommand(TransactionalEditingDomain transactionalEditingDomain, CompositeTransactionalCommand compositeCommand, Point delta, ISequenceNode sequenceNode) { + IAdaptable adapter = new EObjectAdapter(sequenceNode.getNotationNode()); + AbstractModelChangeOperation<Void> moveViewOperation = new MoveViewOperation(DiagramUIMessages.SetLocationCommand_Label_Resize, adapter, delta); + IUndoableOperation moveCmd = CommandFactory.createICommand(transactionalEditingDomain, moveViewOperation); + compositeCommand.add(moveCmd); + } + + /** + * Get {@link ResizeViewOperation} composition from sizeDeltas map. + * + * @param transactionalEditingDomain + * TransactionalEditingDomain used to construct the result + * ICommand + * + * @return composition of {@link ResizeViewOperation} + */ + private ICommand getResizeViewCommands(TransactionalEditingDomain transactionalEditingDomain, Map<InstanceRole, Dimension> sizeDeltas) { + CompositeTransactionalCommand compositeCommand = new CompositeTransactionalCommand(transactionalEditingDomain, ""); + // Resize Commands + for (Entry<InstanceRole, Dimension> entry : sizeDeltas.entrySet()) { + Dimension sizeDelta = entry.getValue(); + + if (sizeDelta.width != 0 || sizeDelta.height != 0) { + InstanceRole instanceRole = entry.getKey(); + IAdaptable adapter = new EObjectAdapter(instanceRole.getNotationView()); + AbstractModelChangeOperation<Void> resizeViewOperation = new ResizeViewOperation(DiagramUIMessages.SetLocationCommand_Label_Resize, adapter, sizeDelta); + IUndoableOperation resizeCmd = CommandFactory.createICommand(transactionalEditingDomain, resizeViewOperation); + compositeCommand.add(resizeCmd); + } + } + if (!compositeCommand.canExecute()) { + return null; + } + return compositeCommand; + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getAutoSizeCommand(Request request) { + return UnexecutableCommand.INSTANCE; + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getAlignCommand(AlignmentRequest request) { + return UnexecutableCommand.INSTANCE; + } + + /** + * Cancel vertical changes of the given request. + * + * @param request + * a request. + */ + protected void cancelVerticalMoveDelta(ChangeBoundsRequest request) { + if (request == null) { + return; + } + + RequestQuery query = new RequestQuery(request); + if (query.isMove()) { + Point moveDelta = request.getMoveDelta().getCopy(); + if (moveDelta != null) { + request.setMoveDelta(new Point(moveDelta.x, 0)); + } + } + } + + private void postProcessDefaultCommand(InstanceRoleEditPart self, CompositeTransactionalCommand command, RequestQuery requestQuery) { + if (command != null && command.canExecute()) { + SequenceEditPartsOperations.addRefreshGraphicalOrderingCommand(command, self); + SequenceEditPartsOperations.addRefreshSemanticOrderingCommand(command, self); + if (requestQuery.isMultiSelectionOperation()) { + SequenceEditPartsOperations.addSynchronizeSemanticOrderingCommand(command, self.getInstanceRole(), requestQuery.getInstanceRoles()); + } else { + SequenceEditPartsOperations.addSynchronizeSemanticOrderingCommand(command, self.getInstanceRole()); + } + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/InstanceRoleSiriusGraphicalNodeEditPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/InstanceRoleSiriusGraphicalNodeEditPolicy.java new file mode 100644 index 0000000000..3ea2b48334 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/InstanceRoleSiriusGraphicalNodeEditPolicy.java @@ -0,0 +1,306 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.editparts.AbstractGraphicalEditPart; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gef.requests.CreateConnectionRequest; +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.emf.commands.core.command.CompositeTransactionalCommand; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.description.tool.EdgeCreationDescription; +import org.eclipse.sirius.diagram.business.internal.view.EdgeLayoutData; +import org.eclipse.sirius.diagram.graphical.edit.policies.SiriusGraphicalNodeEditPolicy; +import org.eclipse.sirius.diagram.internal.edit.parts.DNode2EditPart; +import org.eclipse.sirius.diagram.internal.view.factories.ViewLocationHint; +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.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.EndOfLifeMoveOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.ShiftDirectSubExecutionsOperation; +import org.eclipse.sirius.diagram.sequence.description.tool.MessageCreationTool; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ShiftDescendantMessagesOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ISequenceEventEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InstanceRoleEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.LifelineEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.CreateMessageCreationValidator; +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.ui.tools.api.layout.GraphicalHelper; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactoryProvider; + +/** + * {@link SiriusGraphicalNodeEditPolicy} specific to sequence to manage + * creation of message targeting InstanceRole (create & destroy messages). + * + * @author edugueperoux + */ +public class InstanceRoleSiriusGraphicalNodeEditPolicy extends SiriusGraphicalNodeEditPolicy { + + /** + * overriden to handle Ymove of InstanceRoles when connecting a + * "create message". + * + * @param request + * the request to create a new connection targeting an + * {@link org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InstanceRoleEditPart} + * @return a command that wrap the Y move + */ + @Override + protected Command getConnectionCompleteCommand(CreateConnectionRequest request) { + Command result = UnexecutableCommand.INSTANCE; + + TransactionalEditingDomain domain = ((IGraphicalEditPart) getHost()).getEditingDomain(); + + InstanceRoleEditPart host = (InstanceRoleEditPart) getHost(); + ISequenceElement sequenceElement = host.getInstanceRole(); + SequenceDiagram sequenceDiagram = sequenceElement.getDiagram(); + + RequestQuery requestQuery = new RequestQuery(request); + + if (requestQuery.isCreateMessageCreation()) { + CreateMessageCreationValidator validator = new CreateMessageCreationValidator(); + + EditPart sourceEditPart = request.getSourceEditPart(); + EditPart targetEditPart = request.getTargetEditPart(); + + Option<Lifeline> lifelineSource = ISequenceElementAccessor.getISequenceElement((View) sourceEditPart.getModel()).get().getLifeline(); + Option<Lifeline> lifelineTarget = ISequenceElementAccessor.getISequenceElement((View) targetEditPart.getModel()).get().getLifeline(); + + InstanceRole instanceRoleSource = lifelineSource.get().getInstanceRole(); + InstanceRole instanceRoleTarget = lifelineTarget.get().getInstanceRole(); + + validator.setSource(instanceRoleSource); + validator.setTarget(instanceRoleTarget); + + Point firstClickLocation = SequenceEditPartsOperations.getConnectionSourceLocation(request, host); + Point secondClickLocation = SequenceEditPartsOperations.getConnectionTargetLocation(request, host); + + // Creation message will be horizontal. + if (!ExecutionSemanticEditPolicy.isCombinedFragmentTitleRangeEdgeCreation(sequenceElement, sequenceDiagram, firstClickLocation, firstClickLocation)) { + + validator.setFirstClickLocation(firstClickLocation); + validator.setSecondClickLocation(secondClickLocation); + + if (validator.isValid(request)) { + result = super.getConnectionCompleteCommand(request); + } + } + } else { + + // OLD code + // TODO EDU : refactor this following with validators + result = super.getConnectionCompleteCommand(request); + if (result != null && result.canExecute() && validateIsConnectingCreateMessage(request)) { + CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(domain, "Move down lifeline/ add create participant message"); + Point sourceLocation = ((Point) ViewLocationHint.getInstance().getData(org.eclipse.gef.RequestConstants.REQ_CONNECTION_START)).getCopy(); + final LifelineEditPart lep = EditPartsHelper.getAllLifelines((IGraphicalEditPart) getHost()).get(0); + + if (((AbstractGraphicalEditPart) getHost()).getTargetConnections().isEmpty() && validateIsNotCreateMessageToSelf(request) && validateNoEventBeforeCreate(sourceLocation, lep) + && validateNotCreatingMessageInDifferentOperands(request, sourceLocation)) { + + // Create a ChangeBoundsRequest to move down the targeted + // InstanceRoleEditPart to the Y position of connection + // source + ChangeBoundsRequest changeBoundsRequest = new ChangeBoundsRequest(org.eclipse.gef.RequestConstants.REQ_MOVE); + Rectangle bounds = ((AbstractGraphicalEditPart) getHost()).getFigure().getBounds(); + Point scrollSize = GraphicalHelper.getScrollSize((IGraphicalEditPart) getHost()); + int yMoveNeeded = sourceLocation.y - bounds.y - bounds.height / 2 + scrollSize.y; + + changeBoundsRequest.setMoveDelta(new Point(0, yMoveNeeded)); + // add a specific extended data to enable vertical move of + // an + // InstanceRoleEditPart + changeBoundsRequest.setConstrainedMove(true); + changeBoundsRequest.setEditParts(getHost()); + ctc.compose(new CommandProxy(getHost().getCommand(changeBoundsRequest))); + ctc.compose(new CommandProxy(result)); + + /* + * These additional commands adjust the positions of the + * executions and messages on the lifeline so that visually + * they do not move. They are dual to the commands we add + * when moving a normal execution, as in that case we want + * all the executions and messages it contains to move + * along. + */ + final int deltaY = changeBoundsRequest.getMoveDelta().y; + + // Avoid EndOfLife Move + Lifeline lifeline = (Lifeline) lep.getISequenceEvent(); + ctc.compose(CommandFactory.createICommand(domain, new EndOfLifeMoveOperation(lifeline, -deltaY))); + ctc.compose(CommandFactory.createICommand(domain, new ShiftDirectSubExecutionsOperation(lep.getISequenceEvent(), -deltaY))); + ctc.compose(CommandFactory.createICommand(domain, new ShiftDescendantMessagesOperation(lep.getISequenceEvent(), deltaY, true, false, true))); + SequenceEditPartsOperations.addRefreshSemanticOrderingCommand(ctc, (IGraphicalEditPart) getHost()); + SequenceEditPartsOperations.addRefreshGraphicalOrderingCommand(ctc, (IGraphicalEditPart) getHost()); + + } else { + result = UnexecutableCommand.INSTANCE; + } + result = new ICommandProxy(ctc); + } + } + return result; + } + + /** + * validate if the request will create a "create message". + * + * @param request + * the request to create a new connection targeting an + * {@link org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InstanceRoleEditPart} + * @return if the request will create a "create message" + */ + private boolean validateIsConnectingCreateMessage(CreateConnectionRequest request) { + // validate request type + boolean result = org.eclipse.gef.RequestConstants.REQ_CONNECTION_END.equals(request.getType()) && request.getNewObject() instanceof MessageCreationTool; + // validate request connection ends + result = result && getHost().equals(request.getTargetEditPart()) && request.getSourceEditPart() instanceof DNode2EditPart; + return result; + } + + /** + * validate the request will not create a "create message" to self. + * + * @param request + * the request to create a "create message". + * @return the validation that this request will not create a + * "create message" to self. + */ + private boolean validateIsNotCreateMessageToSelf(CreateConnectionRequest request) { + // validate request does not add a create message to self + return EditPartsHelper.findParentLifeline((IGraphicalEditPart) request.getSourceEditPart()).getParent() != request.getTargetEditPart(); + } + + /** + * Validates that a message is not created between two elements that are not + * in the same operand. + * + * @param request + * current create connection request + * @param location + * location of target + * @return the validation that a message is not created between two elements + * that are not in the same operand. + */ + private boolean validateNotCreatingMessageInDifferentOperands(CreateConnectionRequest request, Point location) { + boolean result = false; + if (getHost() instanceof InstanceRoleEditPart && request.getSourceEditPart() instanceof ISequenceEventEditPart) { + GraphicalHelper.screen2logical(location, (InstanceRoleEditPart) getHost()); + + InstanceRoleEditPart targetEditPart = (InstanceRoleEditPart) getHost(); + Option<Operand> targetParentOperand; + if (targetEditPart.getInstanceRole().getLifeline().some()) { + targetParentOperand = targetEditPart.getInstanceRole().getLifeline().get().getParentOperand(location.y); + } else { + targetParentOperand = Options.newNone(); + } + + ISequenceEventEditPart sourceEditPart = (ISequenceEventEditPart) request.getSourceEditPart(); + Option<Operand> sourceParentOperand; + if (sourceEditPart.getISequenceEvent() instanceof Lifeline) { + sourceParentOperand = ((Lifeline) sourceEditPart.getISequenceEvent()).getParentOperand(location.y); + } else { + sourceParentOperand = sourceEditPart.getISequenceEvent().getParentOperand(); + } + if (targetParentOperand.some()) { + // if the target is in an operand, the source has to be in the + // same operand. + result = sourceParentOperand.some() && targetParentOperand.get().equals(sourceParentOperand.get()); + } else { + // if the target is not in an operand, the source has to be in + // no operand as well. + result = !sourceParentOperand.some(); + } + } + return result; + } + + /** + * Validates that there is no message on the targeted + * {@link org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InstanceRoleEditPart} + * before sourceLocation Y position. There can not be any message before + * creation. + * + * @param sourceLocation + * location where the create message has its source + * @param lep + * @return if there is no message on the targeted + * {@link org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InstanceRoleEditPart} + * before sourceLocation + */ + private boolean validateNoEventBeforeCreate(final Point sourceLocation, LifelineEditPart lep) { + ISequenceEvent lifeline = lep.getISequenceEvent(); + int firstEventInTargetInstanceRole = lifeline.getVerticalRange().getUpperBound(); + + // Filter the combined fragment that contain source location to be able + // to add a create message + Predicate<ISequenceEvent> notParentCombinedFragment = new Predicate<ISequenceEvent>() { + + public boolean apply(ISequenceEvent input) { + if (input instanceof CombinedFragment) { + CombinedFragment combinedFragment = (CombinedFragment) input; + return !combinedFragment.getVerticalRange().includes(sourceLocation.y); + } + return true; + } + }; + + for (ISequenceEvent ise : Iterables.filter(lifeline.getSubEvents(), notParentCombinedFragment)) { + firstEventInTargetInstanceRole = Math.min(firstEventInTargetInstanceRole, ise.getVerticalRange().getLowerBound()); + } + + return sourceLocation.y < firstEventInTargetInstanceRole; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.graphical.edit.policies.SiriusGraphicalNodeEditPolicy#buildCreateEdgeCommand(org.eclipse.gef.requests.CreateConnectionRequest, + * org.eclipse.sirius.EdgeTarget, org.eclipse.sirius.EdgeTarget, + * org.eclipse.sirius.description.tool.EdgeCreationDescription, + * org.eclipse.sirius.tools.api.command.IDiagramCommandFactoryProvider, + * org.eclipse.sirius.diagram.business.internal.view.EdgeLayoutData) + */ + @Override + protected Command buildCreateEdgeCommand(CreateConnectionRequest request, EdgeTarget source, EdgeTarget target, EdgeCreationDescription edgeCreationDescription, + IDiagramCommandFactoryProvider cmdFactoryProvider, EdgeLayoutData edgeLayoutData) { + CompoundCommand result = new CompoundCommand(); + SequenceEditPartsOperations.appendFullRefresh((IGraphicalEditPart) getHost(), result); + addStoreLayoutDataCommand(result, edgeLayoutData); + SequenceEditPartsOperations.buildCreateEdgeCommand((IGraphicalEditPart) getHost(), result, request, source, target, edgeCreationDescription, cmdFactoryProvider); + SequenceEditPartsOperations.appendFullRefresh((IGraphicalEditPart) getHost(), result); + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/InteractionUseResizableEditPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/InteractionUseResizableEditPolicy.java new file mode 100644 index 0000000000..60d028da4f --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/InteractionUseResizableEditPolicy.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.Collection; +import java.util.Collections; + +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.diagram.ui.commands.CommandProxy; +import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; +import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand; + +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.VerticalSpaceExpansion; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ISequenceEventEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InteractionUseEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.AbstractInteractionFrameValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.InteractionUseMoveValidator; +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.tools.internal.edit.command.CommandFactory; + +/** + * A specific AirResizableEditPolicy to manage interaction use roles move & + * resize requests. + * + * @author mporhel + */ +public class InteractionUseResizableEditPolicy extends AbstractFrameResizableEditPolicy { + + /** + * {@inheritDoc} + */ + @Override + protected Command getMoveCommand(ChangeBoundsRequest request) { + cancelHorizontalDelta(request); + Command result = super.getMoveCommand(request); + + InteractionUseEditPart iuep = (InteractionUseEditPart) getHost(); + InteractionUseMoveValidator validator = new InteractionUseMoveValidator((InteractionUse) iuep.getISequenceEvent(), new RequestQuery(request)); + + if (validator.isValid()) { + // TODO Implement MoveInteractionUseCommand + } else { + result = UnexecutableCommand.INSTANCE; + } + + result = postProcessDefaultCommand(iuep, request, result, validator); + return result; + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getResizeCommand(ChangeBoundsRequest request) { + Command result = super.getResizeCommand(request); + + InteractionUseEditPart iuep = (InteractionUseEditPart) getHost(); + AbstractInteractionFrameValidator validator = AbstractInteractionFrameValidator.getOrCreateResizeValidator(request, (InteractionUse) iuep.getISequenceEvent()); + + if (validator.isValid()) { + // TODO Implement ResizeInteractionUseCommand + } else { + result = UnexecutableCommand.INSTANCE; + } + + result = postProcessDefaultCommand(iuep, request, result, validator); + return result; + } + + private Command postProcessDefaultCommand(InteractionUseEditPart self, ChangeBoundsRequest request, Command defaultCommand, AbstractInteractionFrameValidator validator) { + Command result = defaultCommand; + + if (result != null && result.canExecute()) { + CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(self.getEditingDomain(), "Interaction Use Move Composite Command"); + ctc.setLabel(String.valueOf(defaultCommand.getLabel())); + ctc.compose(new CommandProxy(defaultCommand)); + + Range expansionZone = validator.getExpansionZone(); + if (expansionZone != null && !expansionZone.isEmpty()) { + ISequenceEvent iSequenceEvent = self.getISequenceEvent(); + SequenceDiagram diagram = iSequenceEvent.getDiagram(); + Collection<ISequenceEvent> eventToIgnore = Collections.singletonList(iSequenceEvent); + ICommand autoExpand = CommandFactory.createICommand(self.getEditingDomain(), new VerticalSpaceExpansion(diagram, expansionZone, 0, eventToIgnore)); + ctc.compose(autoExpand); + } + + SequenceEditPartsOperations.addRefreshGraphicalOrderingCommand(ctc, self); + SequenceEditPartsOperations.addRefreshSemanticOrderingCommand(ctc, self); + SequenceEditPartsOperations.addSynchronizeSemanticOrderingCommand(ctc, self.getISequenceEvent()); + result = new ICommandProxy(ctc); + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + protected Collection<ISequenceEventEditPart> getChildrenToFeedBack(ChangeBoundsRequest request) { + return Lists.newArrayList(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/LostMessageEndSelectionPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/LostMessageEndSelectionPolicy.java new file mode 100644 index 0000000000..b552cf103a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/LostMessageEndSelectionPolicy.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.List; + +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.requests.AlignmentRequest; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.diagram.ui.internal.requests.ChangeBoundsDeferredRequest; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.graphical.edit.policies.AirResizableEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.LostMessageEndEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; + +/** + * This policy controls the moves of {@link LostMessageEndEditPart}s. + * + * @author mporhel + */ +public class LostMessageEndSelectionPolicy extends AirResizableEditPolicy { + + private static List<Integer> handledAlignments = Lists.newArrayList(); + { + handledAlignments.add(PositionConstants.LEFT); + handledAlignments.add(PositionConstants.CENTER); + handledAlignments.add(PositionConstants.RIGHT); + } + + /** + * Overridden to validate the host type. + * <p> + * {@inheritDoc} + */ + @Override + public void setHost(EditPart host) { + Preconditions.checkArgument(host instanceof LostMessageEndEditPart); + super.setHost(host); + } + + /** + * Convenience method to return the host part with the correct type. + * + * @return returns the host of this policy, with the appropriate type. + */ + protected LostMessageEndEditPart getLostMessageEnd() { + return (LostMessageEndEditPart) getHost(); + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getAlignCommand(AlignmentRequest request) { + Command result = UnexecutableCommand.INSTANCE; + + if (handledAlignments.contains(request.getAlignment())) { + result = super.getAlignCommand(request); + } + + return result; + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getMoveCommand(ChangeBoundsRequest request) { + cancelVerticalMoveDelta(request); + return super.getMoveCommand(request); + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getMoveDeferredCommand(ChangeBoundsDeferredRequest request) { + return UnexecutableCommand.INSTANCE; + } + + /** + * {@inheritDoc} + */ + @Override + protected void showChangeBoundsFeedback(ChangeBoundsRequest request) { + cancelVerticalMoveDelta(request); + super.showChangeBoundsFeedback(request); + } + + /** + * Cancel vertical changes of the given request. + * + * @param request + * a request. + */ + protected void cancelVerticalMoveDelta(ChangeBoundsRequest request) { + if (request == null) { + return; + } + + RequestQuery query = new RequestQuery(request); + if (query.isMove()) { + Point moveDelta = request.getMoveDelta().getCopy(); + if (moveDelta != null) { + request.setMoveDelta(new Point(moveDelta.x, 0)); + } + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/ObservationPointSelectionPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/ObservationPointSelectionPolicy.java new file mode 100644 index 0000000000..883f82306e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/ObservationPointSelectionPolicy.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import org.eclipse.gef.EditPart; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.requests.AlignmentRequest; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.diagram.ui.internal.requests.ChangeBoundsDeferredRequest; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.diagram.graphical.edit.policies.AirResizableEditPolicy; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ObservationPointEditPart; + +/** + * This policy controls the moves of {@link ObservationPointEditPart}s. + * + * @author mporhel + */ +public class ObservationPointSelectionPolicy extends AirResizableEditPolicy { + + /** + * Overridden to validate the host type. + * <p> + * {@inheritDoc} + */ + @Override + public void setHost(EditPart host) { + Preconditions.checkArgument(host instanceof ObservationPointEditPart); + super.setHost(host); + } + + /** + * Convenience method to return the host part with the correct type. + * + * @return returns the host of this policy, with the appropriate type. + */ + protected ObservationPointEditPart getObservationPoint() { + return (ObservationPointEditPart) getHost(); + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getAlignCommand(AlignmentRequest request) { + return UnexecutableCommand.INSTANCE; + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getMoveCommand(ChangeBoundsRequest request) { + return UnexecutableCommand.INSTANCE; + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getMoveDeferredCommand(ChangeBoundsDeferredRequest request) { + return UnexecutableCommand.INSTANCE; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/OperandResizableEditPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/OperandResizableEditPolicy.java new file mode 100644 index 0000000000..7a19ca2074 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/OperandResizableEditPolicy.java @@ -0,0 +1,337 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.Request; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.editparts.ZoomManager; +import org.eclipse.gef.requests.AlignmentRequest; +import org.eclipse.gef.requests.ChangeBoundsRequest; +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.commands.SetBoundsCommand; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand; +import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand; +import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.Size; + +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DNodeContainer; +import org.eclipse.sirius.diagram.graphical.edit.policies.AirResizableEditPolicy; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.OperandEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.OperandResizeValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * A specific AirResizableEditPolicy to operand roles move & resize requests. + * + * @author smonnier + */ +public class OperandResizableEditPolicy extends AirResizableEditPolicy { + + private static final String RESIZE = "Resize"; + + /** + * Constructor. + */ + public OperandResizableEditPolicy() { + super(); + setResizeDirections(PositionConstants.NORTH_SOUTH); + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getMoveCommand(ChangeBoundsRequest request) { + return UnexecutableCommand.INSTANCE; + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getResizeCommand(ChangeBoundsRequest request) { + cancelHorizontalDelta(request); + + OperandEditPart oep = (OperandEditPart) getHost(); + OperandResizeValidator operandResizeValidator = new OperandResizeValidator((Operand) oep.getISequenceEvent(), new RequestQuery(request)); + operandResizeValidator.validate(); + + Command result; + if (operandResizeValidator.isValid()) { + result = getResizeCustomCommand(oep, request); + } else { + result = UnexecutableCommand.INSTANCE; + } + return result; + } + + private static Point getPositionFromView(IGraphicalEditPart part) { + final Point position = new Point(); + if (part.getNotationView() instanceof Node && ((Node) part.getNotationView()).getLayoutConstraint() instanceof Location) { + final Location location = (Location) ((Node) part.getNotationView()).getLayoutConstraint(); + position.x = location.getX(); + position.y = location.getY(); + } + return position; + } + + private static Dimension getDimensionFromView(IGraphicalEditPart part) { + final Dimension dimension = new Dimension(); + if (part.getNotationView() instanceof Node && ((Node) part.getNotationView()).getLayoutConstraint() instanceof Size) { + final Size size = (Size) ((Node) part.getNotationView()).getLayoutConstraint(); + dimension.width = size.getWidth(); + dimension.height = size.getHeight(); + } + return dimension; + } + + /** + * Returns the command to resize a bordered node. + * + * @param part + * the edit part corresponding to the bordered node. + * @param request + * the request for a resize. + * @return the command to resize a bordered node. + */ + public static AbstractTransactionalCommand getResizeBorderItemTCommand(IGraphicalEditPart part, ChangeBoundsRequest request) { + final EObject semantic = part.resolveSemanticElement(); + if (semantic instanceof DNodeContainer) { + final double zoom = ((ZoomManager) part.getViewer().getProperty(ZoomManager.class.toString())).getZoom(); + final Dimension dimension = OperandResizableEditPolicy.getDimensionFromView(part); + final Point position = OperandResizableEditPolicy.getPositionFromView(part); + dimension.height += request.getSizeDelta().height / zoom; + switch (request.getResizeDirection()) { + case PositionConstants.NORTH: + case PositionConstants.NORTH_WEST: + case PositionConstants.NORTH_EAST: + position.y -= request.getSizeDelta().height / zoom; + break; + default: + break; + } + return new SetBoundsCommand(part.getEditingDomain(), RESIZE, new EObjectAdapter(part.getNotationView()), new Rectangle(position, dimension)); + } + return null; + } + + private Command getResizeCustomCommand(OperandEditPart self, ChangeBoundsRequest request) { + CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(self.getEditingDomain(), "Operand Resize Composite Command"); + ctc.add(OperandResizableEditPolicy.getResizeBorderItemTCommand(self, request)); + Option<Operand> operandOption = ISequenceElementAccessor.getOperand(self.getNotationView()); + if (operandOption.some()) { + Operand operand = operandOption.get(); + int operandIndex = operand.getIndex(); + if (request.getResizeDirection() == PositionConstants.NORTH) { + // Resizing the operand from north face must resize the + // previous operand + OperandEditPart previousOperandEditPart = getPreviousOperandEditPart(operandIndex); + if (previousOperandEditPart == null && self.getSelected() != EditPart.SELECTED_NONE) { + // There is no previous operand, resize from north face + // is forbidden + ctc.add(new CommandProxy(UnexecutableCommand.INSTANCE)); + } else if (previousOperandEditPart != null) { + // We apply the inverse resize to the previous operand + Option<Operand> previousOperandOption = ISequenceElementAccessor.getOperand(previousOperandEditPart.getNotationView()); + Operand previousOperand = previousOperandOption.get(); + Range previousOperandVerticalRange = previousOperand.getVerticalRange(); + Range combinedFragmentVerticalRange = previousOperand.getCombinedFragment().getVerticalRange(); + int delta = previousOperandVerticalRange.getLowerBound() - combinedFragmentVerticalRange.getLowerBound(); + Point newLocation = new Point(0, delta); + Dimension newDimension = new Dimension(previousOperand.getBounds().width, previousOperandVerticalRange.width() - request.getSizeDelta().height); + ctc.add(createOperandSetBoundsCommand(previousOperandEditPart, newLocation, newDimension)); + postProcessDefaultCommand(ctc, self); + } + } else if (request.getResizeDirection() == PositionConstants.SOUTH) { + OperandEditPart followingOperandEditPart = getFollowingOperandEditPart(operandIndex); + if (followingOperandEditPart == null && self.getSelected() != EditPart.SELECTED_NONE) { + // There is no following operand, resize from south face + // is forbidden + ctc.add(new CommandProxy(UnexecutableCommand.INSTANCE)); + } else if (followingOperandEditPart != null) { + // We apply the inverse resize to the following operand + Option<Operand> followingOperandOption = ISequenceElementAccessor.getOperand(followingOperandEditPart.getNotationView()); + Operand followingOperand = followingOperandOption.get(); + Range followingOperandVerticalRange = followingOperand.getVerticalRange(); + Range combinedFragmentVerticalRange = followingOperand.getCombinedFragment().getVerticalRange(); + int delta = followingOperandVerticalRange.getLowerBound() - combinedFragmentVerticalRange.getLowerBound(); + Point newLocation = new Point(0, delta + request.getSizeDelta().height); + Dimension newDimension = new Dimension(followingOperand.getBounds().width, followingOperandVerticalRange.width() - request.getSizeDelta().height); + ctc.add(createOperandSetBoundsCommand(followingOperandEditPart, newLocation, newDimension)); + postProcessDefaultCommand(ctc, self); + } + } + } + return new ICommandProxy(ctc); + } + + /** + * Refresh ordering. + * + * @param ctc + * the current command create on operand resize + * @param self + * the {@link OperandEditPart} that is resizing + */ + private void postProcessDefaultCommand(CompositeTransactionalCommand ctc, OperandEditPart self) { + SequenceEditPartsOperations.addRefreshGraphicalOrderingCommand(ctc, self); + SequenceEditPartsOperations.addRefreshSemanticOrderingCommand(ctc, self); + SequenceEditPartsOperations.addSynchronizeSemanticOrderingCommand(ctc, self.getISequenceEvent()); + } + + /** + * Creates the {@link SetBoundsCommand} to resize an operand. + * + * @param part + * the {@link OperandEditPart} + * @param location + * the new location of the operand + * @param dimension + * the new dimension of the operand + * @return a command to resize an operand + */ + private AbstractTransactionalCommand createOperandSetBoundsCommand(IGraphicalEditPart part, Point location, Dimension dimension) { + return new SetBoundsCommand(part.getEditingDomain(), RESIZE, new EObjectAdapter(part.getNotationView()), new Rectangle(location, dimension)); + } + + /** + * Finds the previous {@link OperandEditPart} of the current + * {@link OperandEditPart} identified by the index currentOperandIndex. + * + * @param currentOperandIndex + * the index of the current {@link OperandEditPart} + * @return the previous {@link OperandEditPart} + */ + private OperandEditPart getPreviousOperandEditPart(int currentOperandIndex) { + for (OperandEditPart operandEditPart : Iterables.filter(getHost().getParent().getChildren(), OperandEditPart.class)) { + Option<Operand> operandOption = ISequenceElementAccessor.getOperand(operandEditPart.getNotationView()); + if (operandOption.some()) { + Operand operand = operandOption.get(); + int operandIndex = operand.getIndex(); + if (operandIndex == currentOperandIndex - 1) { + return operandEditPart; + } + } + } + return null; + } + + /** + * Finds the following {@link OperandEditPart} of the current + * {@link OperandEditPart} identified by the index currentOperandIndex. + * + * @param currentOperandIndex + * the index of the current {@link OperandEditPart} + * @return the following {@link OperandEditPart} + */ + private OperandEditPart getFollowingOperandEditPart(int currentOperandIndex) { + for (OperandEditPart operandEditPart : Iterables.filter(getHost().getParent().getChildren(), OperandEditPart.class)) { + Option<Operand> operandOption = ISequenceElementAccessor.getOperand(operandEditPart.getNotationView()); + if (operandOption.some()) { + Operand operand = operandOption.get(); + int operandIndex = operand.getIndex(); + if (operandIndex == currentOperandIndex + 1) { + return operandEditPart; + } + } + } + return null; + } + + private void cancelHorizontalDelta(ChangeBoundsRequest request) { + if (request == null) { + return; + } + + Point moveDelta = request.getMoveDelta(); + if (moveDelta != null) { + request.setMoveDelta(new Point(0, moveDelta.y)); + } + + Dimension sizeDelta = request.getSizeDelta(); + if (sizeDelta != null) { + request.setSizeDelta(new Dimension(0, sizeDelta.height)); + } + } + + private void cancelVerticalDelta(ChangeBoundsRequest request) { + if (request == null) { + return; + } + + Point moveDelta = request.getMoveDelta(); + if (moveDelta != null) { + request.setMoveDelta(new Point(moveDelta.x, 0)); + } + + Dimension sizeDelta = request.getSizeDelta(); + if (sizeDelta != null) { + request.setSizeDelta(new Dimension(sizeDelta.width, 0)); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getAutoSizeCommand(Request request) { + return UnexecutableCommand.INSTANCE; + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getAlignCommand(AlignmentRequest request) { + return UnexecutableCommand.INSTANCE; + } + + /** + * {@inheritDoc}. + */ + @Override + protected void showChangeBoundsFeedback(ChangeBoundsRequest request) { + cancelHorizontalDelta(request); + RequestQuery query = new RequestQuery(request); + if (query.isMove()) { + cancelVerticalDelta(request); + } + super.showChangeBoundsFeedback(request); + } + + /** + * {@inheritDoc}. + * + * Overridden to cancel feedback move because operand move is forbidden. + */ + @Override + public void showSourceFeedback(Request request) { + + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceContainerCreationPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceContainerCreationPolicy.java new file mode 100644 index 0000000000..db7c257210 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceContainerCreationPolicy.java @@ -0,0 +1,333 @@ +/******************************************************************************* + * Copyright (c) 2010, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.GraphicalEditPart; +import org.eclipse.gef.LayerConstants; +import org.eclipse.gef.Request; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.editparts.LayerManager; +import org.eclipse.gef.requests.CreateRequest; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.description.tool.AbstractToolDescription; +import org.eclipse.sirius.description.tool.ContainerCreationDescription; +import org.eclipse.sirius.description.tool.NodeCreationDescription; +import org.eclipse.sirius.diagram.graphical.edit.policies.ContainerCreationEditPolicy; +import org.eclipse.sirius.diagram.graphical.edit.policies.CreationUtil; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +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.operation.VerticalSpaceExpansion; +import org.eclipse.sirius.diagram.sequence.description.tool.CombinedFragmentCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InstanceRoleCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InteractionUseCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ObservationPointCreationTool; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.command.SequenceDelegatingCommandFactory; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceDiagramEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.FrameCreationValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.SequenceGraphicalHelper; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.CreateRequestQuery; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.EditPartsHelper; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.tools.api.command.DoNothingCommand; +import org.eclipse.sirius.diagram.tools.api.editor.DDiagramEditor; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactory; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactoryProvider; + +/** + * An extension of the standard Sirius ContainerCreationEditPolicy which + * knows how to handle the specific tools use to create frames (i.e. Interaction + * Uses and Combined Fragments). + * + * @author pcdavid + */ +public class SequenceContainerCreationPolicy extends ContainerCreationEditPolicy { + + /** + * Additional figures for feedback. + */ + protected Collection<Figure> guides = Lists.newArrayList(); + + @Override + protected Command getCreateNodeOnDiagramCommand(CreateRequest request, NodeCreationDescription tool, DDiagram diagram) { + Command result; + if (tool instanceof ObservationPointCreationTool && diagram instanceof SequenceDDiagram) { + SequenceDDiagram diag = (SequenceDDiagram) diagram; + Point location = request.getLocation().getCopy(); + GraphicalHelper.screen2logical(location, (IGraphicalEditPart) getHost()); + + EventEnd startingEndPredecessor = SequenceGraphicalHelper.getEndBefore(diag, location.y); + EventEnd finishingEndEndPredecessor = startingEndPredecessor; + if (request.getSize() != null) { + Dimension size = request.getSize().getCopy(); + GraphicalHelper.screen2logical(size, (IGraphicalEditPart) getHost()); + finishingEndEndPredecessor = SequenceGraphicalHelper.getEndBefore(diag, location.y + size.height); + } + + CreationUtil creationUtil = new CreationUtil(request, getDiagramCommandFactory(startingEndPredecessor, finishingEndEndPredecessor, location), getRealLocation(request), request.getSize(), + getHost()); + result = creationUtil.getNodeCreationCommand(diagram, tool); + } else if (tool instanceof InstanceRoleCreationTool && diagram instanceof SequenceDDiagram) { + SequenceDDiagram diag = (SequenceDDiagram) diagram; + Point location = request.getLocation().getCopy(); + GraphicalHelper.screen2logical(location, (IGraphicalEditPart) getHost()); + + EObject predecessor = SequenceGraphicalHelper.getInstanceRoleBefore(diag, location.x); + CreationUtil creationUtil = new CreationUtil(request, getDiagramCommandFactory(predecessor, location), getRealLocation(request), request.getSize(), getHost()); + result = creationUtil.getNodeCreationCommand(diagram, tool); + } else { + result = super.getCreateNodeOnDiagramCommand(request, tool, diagram); + } + + return result; + } + + /** + * {@inheritDoc} + */ + @Override + protected Command getCreateContainerOnDiagramCommand(CreateRequest request, ContainerCreationDescription ccdTool, DDiagram diagram) { + Command result; + + boolean frameCreationTool = ccdTool instanceof InteractionUseCreationTool || ccdTool instanceof CombinedFragmentCreationTool; + if (frameCreationTool && getHost() instanceof SequenceDiagramEditPart && diagram instanceof SequenceDDiagram) { + SequenceDiagramEditPart sdep = (SequenceDiagramEditPart) getHost(); + TransactionalEditingDomain domain = sdep.getEditingDomain(); + Diagram gmfDiagram = sdep.getDiagramView(); + SequenceDiagram sequenceDiagram = ISequenceElementAccessor.getSequenceDiagram(gmfDiagram).get(); + FrameCreationValidator creationValidator = FrameCreationValidator.getOrCreateValidator(sequenceDiagram, ccdTool, new CreateRequestQuery(request, sdep)); + + if (creationValidator.isValid()) { + EventEnd startingEndPredecessor = creationValidator.getStartingEndPredecessor(); + EventEnd finishingEndPredecessor = creationValidator.getFinishingEndPredecessor(); + List<EObject> coverage = creationValidator.getCoverage(); + Range expansionZone = creationValidator.getExpansionZone(); + + CreationUtil creationUtil = new CreationUtil(request, getDiagramCommandFactory(startingEndPredecessor, finishingEndPredecessor, coverage, getCreationRange(request)), + getRealLocation(request), getRealSize(ccdTool, request), getHost()); + result = creationUtil.getContainerCreationDescription(diagram, ccdTool); + + // Add a vertical expansion command if we do inclusion + if (expansionZone != null && !expansionZone.isEmpty() && result != null && result.canExecute()) { + // Shift the element to not include int the range of the + // AbstractFrame to create + VerticalSpaceExpansion verticalSpaceExpansion = new VerticalSpaceExpansion(sequenceDiagram, expansionZone, 0, Collections.<ISequenceEvent> emptyList()); + ICommand expandSubEventsCmd = CommandFactory.createICommand(domain, verticalSpaceExpansion); + + result = new ICommandProxy(expandSubEventsCmd).chain(result); + } + } else { + result = creationValidator.getCoverage().isEmpty() ? DoNothingCommand.INSTANCE : UnexecutableCommand.INSTANCE; + } + } else { + result = super.getCreateContainerOnDiagramCommand(request, ccdTool, diagram); + } + return result; + } + + /** + * Overridden to show the feedback of the expansion zone for + * InteractionUse/CombinedFragment creation when there is inclusion of + * existing sequence events in its creation range and vertical space + * expansion is needed for some sequence events. + * + * + * {@inheritDoc} + */ + @Override + public void showTargetFeedback(Request request) { + eraseTargetFeedback(request); + if (request instanceof CreateRequest && this.getHost() instanceof SequenceDiagramEditPart) { + SequenceDiagramEditPart sdep = (SequenceDiagramEditPart) getHost(); + CreateRequest createRequest = (CreateRequest) request; + Option<ISequenceElement> seqDiag = ISequenceElementAccessor.getISequenceElement((View) this.getHost().getModel()); + AbstractToolDescription tool = getTool(createRequest); + if (seqDiag.some() && seqDiag.get() instanceof SequenceDiagram && tool instanceof InteractionUseCreationTool || tool instanceof CombinedFragmentCreationTool) { + FrameCreationValidator validator = FrameCreationValidator.getOrCreateValidator((SequenceDiagram) seqDiag.get(), (ContainerCreationDescription) tool, new CreateRequestQuery( + createRequest, sdep)); + if (validator != null) { + SequenceInteractionFeedBackBuilder feedBackBuilder = new SequenceInteractionFeedBackBuilder(validator, getFeedbackLayer(), (IGraphicalEditPart) getHost()); + for (Figure fig : feedBackBuilder.buildFeedBack()) { + addFeedback(fig); + guides.add(fig); + } + } + } + } + } + + /** + * Overridden to erase feedback for AbstractFrame creation request. + * + * {@inheritDoc} + */ + @Override + public void eraseTargetFeedback(Request request) { + removeFeedBackOnGuides(); + } + + private void removeFeedBackOnGuides() { + if (guides != null && !guides.isEmpty()) { + for (Figure hGuide : guides) { + removeFeedback(hGuide); + } + guides.clear(); + } + } + + /** + * Add a IFigure to the feedbackLayer. + * + * @param figure + * the feedback figure to add + */ + protected void addFeedback(IFigure figure) { + getFeedbackLayer().add(figure); + } + + /** + * Returns the layer used for displaying feedback. + * + * @return the feedback layer + */ + protected IFigure getFeedbackLayer() { + return getLayer(LayerConstants.FEEDBACK_LAYER); + } + + /** + * Convenience method to return the host's Figure. + * + * @return The host GraphicalEditPart's Figure + */ + protected IFigure getHostFigure() { + return ((GraphicalEditPart) getHost()).getFigure(); + } + + /** + * Obtains the specified layer. + * + * @param layer + * the key identifying the layer + * @return the requested layer + */ + protected IFigure getLayer(Object layer) { + return LayerManager.Helper.find(getHost()).getLayer(layer); + } + + /** + * Removes the specified <code>Figure</code> from the + * {@link LayerConstants#FEEDBACK_LAYER}. + * + * @param figure + * the feedback to remove + */ + protected void removeFeedback(IFigure figure) { + getFeedbackLayer().remove(figure); + } + + private Range getCreationRange(CreateRequest request) { + Point realLocation = getRealLocation(request); + Range result = Range.emptyRange(); + if (request.getSize() != null) { + result = new Range(realLocation.y, realLocation.y + request.getSize().height); + } + return result; + } + + private Dimension getRealSize(ContainerCreationDescription ccdTool, CreateRequest request) { + Dimension realSize = request.getSize(); + if (realSize == null) { + realSize = new Dimension(0, 0); + if (ccdTool instanceof InteractionUseCreationTool) { + realSize.height = LayoutConstants.DEFAULT_INTERACTION_USE_HEIGHT; + } else if (ccdTool instanceof CombinedFragmentCreationTool) { + realSize.height = LayoutConstants.DEFAULT_COMBINED_FRAGMENT_HEIGHT; + } + } + return realSize; + } + + private IDiagramCommandFactory getDiagramCommandFactory(EventEnd startingEndPredecessor, EventEnd finishingEndPredecessor, List<EObject> coverage, Range creationRange) { + SequenceDiagram seqDiag = null; + EditPart host = getHost(); + if (host instanceof SequenceDiagramEditPart) { + seqDiag = ((SequenceDiagramEditPart) host).getSequenceDiagram(); + } + + final DDiagramEditor diagramEditor = (DDiagramEditor) host.getViewer().getProperty(DDiagramEditor.EDITOR_ID); + if (diagramEditor == null || seqDiag == null) { + return null; + } + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(seqDiag.getNotationDiagram()); + final Object adapter = diagramEditor.getAdapter(IDiagramCommandFactoryProvider.class); + final IDiagramCommandFactoryProvider cmdFactoryProvider = (IDiagramCommandFactoryProvider) adapter; + final IDiagramCommandFactory diagramCommandFactory = cmdFactoryProvider.getCommandFactory(domain); + return new SequenceDelegatingCommandFactory(diagramCommandFactory, domain, seqDiag, startingEndPredecessor, finishingEndPredecessor, coverage); + } + + private IDiagramCommandFactory getDiagramCommandFactory(EventEnd startingEndPredecessor, EventEnd finishingEndPredecessor, Point location) { + EditPart host = getHost(); + SequenceDiagram seqDiag = EditPartsHelper.getSequenceDiagram(host); + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(seqDiag.getNotationDiagram()); + final DDiagramEditor diagramEditor = (DDiagramEditor) this.getHost().getViewer().getProperty(DDiagramEditor.EDITOR_ID); + if (diagramEditor == null) { + return null; + } + final Object adapter = diagramEditor.getAdapter(IDiagramCommandFactoryProvider.class); + + final IDiagramCommandFactoryProvider cmdFactoryProvider = (IDiagramCommandFactoryProvider) adapter; + final IDiagramCommandFactory diagramCommandFactory = cmdFactoryProvider.getCommandFactory(domain); + return new SequenceDelegatingCommandFactory(diagramCommandFactory, domain, seqDiag, startingEndPredecessor, finishingEndPredecessor); + } + + private IDiagramCommandFactory getDiagramCommandFactory(EObject predecessor, Point location) { + EditPart host = getHost(); + SequenceDiagram seqDiag = EditPartsHelper.getSequenceDiagram(host); + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(seqDiag.getNotationDiagram()); + final DDiagramEditor diagramEditor = (DDiagramEditor) this.getHost().getViewer().getProperty(DDiagramEditor.EDITOR_ID); + if (diagramEditor == null) { + return null; + } + final Object adapter = diagramEditor.getAdapter(IDiagramCommandFactoryProvider.class); + + final IDiagramCommandFactoryProvider cmdFactoryProvider = (IDiagramCommandFactoryProvider) adapter; + final IDiagramCommandFactory diagramCommandFactory = cmdFactoryProvider.getCommandFactory(domain); + return new SequenceDelegatingCommandFactory(diagramCommandFactory, domain, seqDiag, predecessor, location); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceInteractionFeedBackBuilder.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceInteractionFeedBackBuilder.java new file mode 100644 index 0000000000..961370a878 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceInteractionFeedBackBuilder.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.Collection; + +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.swt.graphics.Color; + +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.AbstractSequenceInteractionValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.HorizontalGuide; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.RangeGuide; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; + +/** + * A builder for complex sequence move feedback. + * + * @author mporhel + */ +public class SequenceInteractionFeedBackBuilder { + /** + * The color to use for the horizontal feedback rules shown when + * moving/resizing an ISequencEvent. + */ + private static final Color ISE_FEEDBACK_COLOR = ColorConstants.lightGray; + + private final AbstractSequenceInteractionValidator validator; + + private final IFigure feedBackLayer; + + private final IGraphicalEditPart hostPart; + + /** + * Constructor. + * + * @param validator + * the move validator. + * @param feedBackLayer + * the feedback layer. + * @param hostPart + * the main selected part. + */ + public SequenceInteractionFeedBackBuilder(AbstractSequenceInteractionValidator validator, IFigure feedBackLayer, IGraphicalEditPart hostPart) { + this.validator = validator; + this.feedBackLayer = feedBackLayer; + this.hostPart = hostPart; + } + + /** + * Build the command. + * + * @return a composite transactional command. + */ + public Collection<Figure> buildFeedBack() { + Collection<Figure> feedbacks = Lists.newArrayList(); + + // validation on the first call; + validator.validate(); + + feedBackCreatedElements(feedbacks); + + feedBackMovedElements(feedbacks); + + feedBackResizedElements(feedbacks); + + feedBackExpansion(feedbacks); + + feedBackErrors(feedbacks); + + feedBackConflicts(feedbacks); + + return feedbacks; + + } + + private void feedBackConflicts(Collection<Figure> feedbacks) { + for (Integer conflict : validator.getInvalidPostions()) { + Point conflictingPosition = new Point(0, conflict); + conflictingPosition.performScale(GraphicalHelper.getZoom(hostPart)); + + Rectangle bounds = feedBackLayer.getBounds().getCopy(); + bounds.y = conflictingPosition.y; + bounds.height = 1; + + HorizontalGuide conflictGuide = new HorizontalGuide(ColorConstants.red, conflictingPosition.y); + conflictGuide.setBounds(bounds); + feedbacks.add(conflictGuide); + } + + for (Range conflict : validator.getInvalidRanges()) { + Rectangle screenRange = new Rectangle(0, conflict.getLowerBound(), 0, conflict.width()); + screenRange.performScale(GraphicalHelper.getZoom(hostPart)); + Range conflictRange = Range.verticalRange(screenRange); + + Rectangle bounds = feedBackLayer.getBounds().getCopy(); + bounds.y = conflictRange.getLowerBound(); + bounds.height = Math.max(1, conflictRange.width()); + + RangeGuide guide = new RangeGuide(ColorConstants.red, conflictRange, true); + guide.setBounds(bounds); + feedbacks.add(guide); + } + } + + private void feedBackErrors(Collection<Figure> feedbacks) { + for (ISequenceEvent errorEvent : validator.getEventsInError()) { + addFeedBack(errorEvent, ColorConstants.red, true, feedbacks, validator.getRangeFunction().apply(errorEvent)); + } + } + + private void addFeedBack(ISequenceEvent event, Color color, boolean fill, Collection<Figure> feedbacks, Range movedRange) { + Rectangle screenRange = new Rectangle(0, movedRange.getLowerBound(), 0, movedRange.width()); + screenRange.performScale(GraphicalHelper.getZoom(hostPart)); + Range moveRange = Range.verticalRange(screenRange); + + Rectangle bounds = feedBackLayer.getBounds().getCopy(); + if (event != null && event.isLogicallyInstantaneous()) { + moveRange = new Range(screenRange.getCenter().y, screenRange.getCenter().y); + bounds.y = moveRange.getLowerBound(); + bounds.height = 1; + } else { + bounds.y = moveRange.getLowerBound(); + bounds.height = Math.max(1, moveRange.width()); + } + + RangeGuide guide = new RangeGuide(color, moveRange, fill); + guide.setBounds(bounds); + + feedbacks.add(guide); + } + + private void feedBackExpansion(Collection<Figure> feedbacks) { + Rectangle bounds = feedBackLayer.getBounds().getCopy(); + Range expansionZone = validator.getExpansionZone(); + if (expansionZone != null && !expansionZone.isEmpty() && expansionZone.width() != 0) { + Rectangle screenRange = new Rectangle(0, expansionZone.getLowerBound(), 0, expansionZone.width()); + screenRange.performScale(GraphicalHelper.getZoom(hostPart)); + Range expand = Range.verticalRange(screenRange); + + RangeGuide expansion = new RangeGuide(validator.isValid() ? ColorConstants.blue : ColorConstants.red, expand, true); + bounds.height = expand.width(); + bounds.y = expand.getLowerBound(); + expansion.setBounds(bounds); + + feedbacks.add(expansion); + } + } + + private void feedBackMovedElements(Collection<Figure> feedbacks) { + for (ISequenceEvent movedElement : Iterables.filter(validator.getMovedElements(), Predicates.not(Predicates.in(validator.getEventsInError())))) { + addFeedBack(movedElement, ISE_FEEDBACK_COLOR, false, feedbacks, validator.getRangeFunction().apply(movedElement)); + } + } + + private void feedBackCreatedElements(Collection<Figure> feedbacks) { + for (Range creationRange : validator.getCreatedElements()) { + addFeedBack(null, validator.isValid() ? ISE_FEEDBACK_COLOR : ColorConstants.red, false, feedbacks, creationRange); + } + } + + private void feedBackResizedElements(Collection<Figure> feedbacks) { + for (ISequenceEvent movedElement : Iterables.filter(validator.getResizedStartMessages(), Predicates.not(Predicates.in(validator.getEventsInError())))) { + addFeedBack(movedElement, ISE_FEEDBACK_COLOR, false, feedbacks, validator.getRangeFunction().apply(movedElement)); + } + + for (ISequenceEvent movedElement : Iterables.filter(validator.getResizedEndMessages(), Predicates.not(Predicates.in(validator.getEventsInError())))) { + Range feedbackRange = validator.getRangeFunction().apply(movedElement); + Range expansionZone = validator.getExpansionZone(); + if ((expansionZone != null && !expansionZone.isEmpty()) && feedbackRange.includes(expansionZone.getUpperBound())) { + feedbackRange = new Range(feedbackRange.getLowerBound(), feedbackRange.getUpperBound() - expansionZone.width()); + } + addFeedBack(movedElement, ISE_FEEDBACK_COLOR, false, feedbacks, feedbackRange); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceLaunchToolEditPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceLaunchToolEditPolicy.java new file mode 100644 index 0000000000..2e215d45c2 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceLaunchToolEditPolicy.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.requests.CreateRequest; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import org.eclipse.sirius.description.tool.PaneBasedSelectionWizardDescription; +import org.eclipse.sirius.description.tool.SelectionWizardDescription; +import org.eclipse.sirius.description.tool.ToolDescription; +import org.eclipse.sirius.diagram.graphical.edit.policies.CreationUtil; +import org.eclipse.sirius.diagram.graphical.edit.policies.LaunchToolEditPolicy; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.command.SequenceDelegatingCommandFactory; +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.ui.tools.api.layout.GraphicalHelper; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactory; + +/** + * Edit policy for launching tools. Adding the support of $endBefore variable. + * + * @author mporhel + */ +public class SequenceLaunchToolEditPolicy extends LaunchToolEditPolicy { + + /** + * {@inheritDoc} + */ + @Override + protected CreationUtil getCreationUtil(CreateRequest request, Point location, EditPart editPart, IDiagramCommandFactory baseEmfCommandFactory) { + IDiagramCommandFactory launchToolCommandFactory = baseEmfCommandFactory; + + Object tool = request.getNewObject(); + if (tool instanceof ToolDescription || tool instanceof PaneBasedSelectionWizardDescription || tool instanceof SelectionWizardDescription) { + launchToolCommandFactory = getLaunchToolCommandFactory(editPart, request.getLocation().getCopy(), baseEmfCommandFactory); + } + + return super.getCreationUtil(request, location, editPart, launchToolCommandFactory); + } + + private IDiagramCommandFactory getLaunchToolCommandFactory(EditPart editPart, Point requestLocation, IDiagramCommandFactory baseEmfCommandFactory) { + IDiagramCommandFactory result = baseEmfCommandFactory; + + Point location = requestLocation.getCopy(); + if (editPart instanceof IGraphicalEditPart) { + GraphicalHelper.screen2logical(location, (IGraphicalEditPart) editPart); + } + + SequenceDiagram sequenceDiagram = EditPartsHelper.getSequenceDiagram(editPart); + if (sequenceDiagram != null) { + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(sequenceDiagram.getNotationDiagram()); + SequenceDDiagram diagram = sequenceDiagram.getSequenceDDiagram(); + EventEnd endBefore = SequenceGraphicalHelper.getEndBefore(diagram, location.y); + result = new SequenceDelegatingCommandFactory(baseEmfCommandFactory, domain, sequenceDiagram, endBefore, location.getCopy()); + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceMessageEditPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceMessageEditPolicy.java new file mode 100644 index 0000000000..e33f15d674 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceMessageEditPolicy.java @@ -0,0 +1,852 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.Cursors; +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.workspace.AbstractEMFOperation; +import org.eclipse.gef.ConnectionEditPart; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.Request; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.requests.BendpointRequest; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +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.editpolicies.ConnectionBendpointEditPolicy; +import org.eclipse.gmf.runtime.diagram.ui.internal.commands.SetConnectionBendpointsCommand; +import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants; +import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.swt.graphics.Color; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.EndOfLife; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.LostMessageEnd; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.EndOfLifeMoveOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SetMessageRangeOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.ShiftDirectSubExecutionsOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceMessageViewQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.EventFinder; +import org.eclipse.sirius.diagram.sequence.ordering.CompoundEventEnd; +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.EndOfLifeOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.ShiftDescendantMessagesOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.EndOfLifeEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ISequenceEventEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InstanceRoleEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.LifelineEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceDiagramEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceMessageEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.PositionsChecker; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.HorizontalGuide; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.EditPartsHelper; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; + +/** + * Specialized edit policy for sequence diagrams messages: tracks graphical + * reordering and invokes user-specified reordering tool to reflect the changes + * in the semantic model. + * <p> + * This edit policy should only be installed on SequenceMessageEditParts. + * + * @author pcdavid + */ +public class SequenceMessageEditPolicy extends ConnectionBendpointEditPolicy { + + /** + * Key constant use for request from a BendpointRequest on Message. + */ + public static final String REQUEST_FROM_SEQUENCE_MESSAGE_EDIT_POLICY = "Request a Execution resize from a BendpointRequest"; + + /** + * The color top use for the horizontal feedback rules shown when moving a + * message. + */ + private static final Color MESSAGE_FEEDBACK_COLOR = ColorConstants.lightGray; + + private final Collection<Figure> guides = Lists.newArrayList(); + + /** + * Saves the validation status of the command on bendpoint move. + */ + private boolean invalidCommand; + + /** + * Overridden to check that we are only installed on sequence messages. + */ + @Override + @SuppressWarnings("restriction") + public void activate() { + Preconditions.checkState(getHost() instanceof SequenceMessageEditPart); + super.activate(); + } + + protected SequenceMessageEditPart getMessage() { + return (SequenceMessageEditPart) getHost(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void addInvisibleCreationHandle(@SuppressWarnings("rawtypes") List list, ConnectionEditPart connEP, int i) { + /* + * Do nothing: the handles created by default use a raw GEF drag tracker + * which we do not control, and which can lead to disconnections of + * branches on reflective messages. + * + */ + } + + /** + * Update the location of the horizontal feedback line. + * <p> + * {@inheritDoc} + */ + @Override + @SuppressWarnings("restriction") + public void showSourceFeedback(Request request) { + removeFeedBackOnGuides(); + + if (request instanceof BendpointRequest) { + BendpointRequest br = (BendpointRequest) request; + SequenceMessageEditPart thisEvent = (SequenceMessageEditPart) getHost(); + ISequenceEvent iSequenceEvent = thisEvent.getISequenceEvent(); + List<EventEnd> ends = EventEndHelper.findEndsFromSemanticOrdering(iSequenceEvent); + super.showSourceFeedback(request); + + MoveType moveType = getMoveType(thisEvent, br, ends); + if (moveType.needsCompoundMove()) { + showCompoundEndFeedback(br, thisEvent, ends, moveType.isFromTop()); + } else { + Point location = new Point(1, thisEvent.getConnectionFigure().getPoints().getFirstPoint().y); + location.performScale(GraphicalHelper.getZoom((IGraphicalEditPart) getHost())); + + Figure guide = new HorizontalGuide(MESSAGE_FEEDBACK_COLOR, location.y); + Rectangle bounds = getFeedbackLayer().getBounds().getCopy(); + bounds.height = 1; + bounds.y = location.y; + guide.setBounds(bounds); + addFeedback(guide); + guides.add(guide); + + if (new ISequenceEventQuery(getMessage().getISequenceEvent()).isReflectiveMessage()) { + Point endLocation = new Point(1, thisEvent.getConnectionFigure().getPoints().getLastPoint().y); + endLocation.performScale(GraphicalHelper.getZoom((IGraphicalEditPart) getHost())); + Figure messageToSelfBottomGuide = new HorizontalGuide(MESSAGE_FEEDBACK_COLOR, endLocation.y); + bounds = getFeedbackLayer().getBounds().getCopy(); + bounds.height = 1; + bounds.y = endLocation.y; + messageToSelfBottomGuide.setBounds(bounds); + addFeedback(messageToSelfBottomGuide); + guides.add(messageToSelfBottomGuide); + } + + if (thisEvent.getTarget() instanceof InstanceRoleEditPart) { + showInstanceRoleFeedback(br); + } else if (thisEvent.getTarget() instanceof EndOfLifeEditPart) { + showEndOfLifeFeedback(br); + } + + Point reqLoc = br.getLocation().getCopy(); + GraphicalHelper.screen2logical(reqLoc, (IGraphicalEditPart) getHost()); + Option<Range> finalRange = computeFinalRange(br, thisEvent, reqLoc); + if (finalRange.some()) { + Collection<Integer> invalidPositions = checkGlobalPositions(iSequenceEvent, finalRange); + for (Integer conflict : invalidPositions) { + bounds = getFeedbackLayer().getBounds().getCopy(); + + Point conflictingPosition = new Point(0, conflict); + conflictingPosition.performScale(GraphicalHelper.getZoom((IGraphicalEditPart) getHost())); + + HorizontalGuide conflictGuide = new HorizontalGuide(ColorConstants.red, conflictingPosition.y); + bounds.y = conflictingPosition.y; + bounds.height = 1; + conflictGuide.setBounds(bounds); + addFeedback(conflictGuide); + guides.add(conflictGuide); + } + } + } + } + } + + private void removeFeedBackOnGuides() { + for (Figure fig : guides) { + removeFeedback(fig); + } + guides.clear(); + + } + + private void showCompoundEndFeedback(BendpointRequest request, SequenceMessageEditPart thisEvent, List<EventEnd> ends, boolean fromTop) { + final EObject thisSemanticEvent = thisEvent.resolveTargetSemanticElement(); + final SequenceDiagramEditPart sdep = EditPartsHelper.getSequenceDiagramPart(thisEvent); + final Range thisRange = thisEvent.getISequenceEvent().getVerticalRange(); + final Point location = request.getLocation().getCopy(); + + final Predicate<SingleEventEnd> toMove = new Predicate<SingleEventEnd>() { + public boolean apply(SingleEventEnd input) { + return !input.getSemanticEvent().equals(thisSemanticEvent); + } + }; + + Dimension resizeDelta = getResizeDelta(location.getCopy(), thisEvent, thisRange, fromTop); + for (CompoundEventEnd cee : Iterables.filter(ends, CompoundEventEnd.class)) { + for (SingleEventEnd see : Iterables.filter(Lists.newArrayList(cee.getEventEnds()), toMove)) { + ISequenceEventEditPart ise = EditPartsHelper.findISequenceEvent(see, sdep); + ChangeBoundsRequest cbr = buildChangeBoundRequest(location.getCopy(), thisEvent, see, resizeDelta); + ise.showSourceFeedback(cbr); + } + } + } + + private void eraseCompoundEndFeedback(BendpointRequest request, SequenceMessageEditPart thisEvent, List<EventEnd> ends, boolean fromTop) { + final EObject thisSemanticEvent = thisEvent.resolveTargetSemanticElement(); + final SequenceDiagramEditPart sdep = EditPartsHelper.getSequenceDiagramPart(thisEvent); + final Range thisRange = thisEvent.getISequenceEvent().getVerticalRange(); + final Point location = request.getLocation().getCopy(); + + final Predicate<SingleEventEnd> toMove = new Predicate<SingleEventEnd>() { + public boolean apply(SingleEventEnd input) { + return !input.getSemanticEvent().equals(thisSemanticEvent); + } + }; + + Dimension resizeDelta = getResizeDelta(location.getCopy(), thisEvent, thisRange, fromTop); + for (CompoundEventEnd cee : Iterables.filter(ends, CompoundEventEnd.class)) { + for (SingleEventEnd see : Iterables.filter(Lists.newArrayList(cee.getEventEnds()), toMove)) { + ISequenceEventEditPart ise = EditPartsHelper.findISequenceEvent(see, sdep); + ChangeBoundsRequest cbr = buildChangeBoundRequest(location.getCopy(), thisEvent, see, resizeDelta); + ise.eraseSourceFeedback(cbr); + } + } + } + + /** + * Moving a create message up and down will also show the feedback of the + * targeted instance role. + * + * @param request + * the "create message" move request + */ + private void showInstanceRoleFeedback(BendpointRequest request) { + InstanceRoleEditPart target = (InstanceRoleEditPart) ((SequenceMessageEditPart) getHost()).getTarget(); + ChangeBoundsRequest cbr = new ChangeBoundsRequest(org.eclipse.gef.RequestConstants.REQ_MOVE); + Point scrollSize = GraphicalHelper.getScrollSize(target); + IGraphicalEditPart source = (IGraphicalEditPart) ((SequenceMessageEditPart) getHost()).getSource(); + int feedbackRangeLimit = request.getLocation().y; + Rectangle sourceBbounds = source.getFigure().getBounds(); + + // limit the vertical move to the first sequence event of the targeted + // instance role + int firstMessageInTargetInstanceRole = Integer.MAX_VALUE; + + LifelineEditPart lifelineEditPart = Iterables.getOnlyElement(EditPartsHelper.getAllLifelines(target)); + Range occupiedRange = lifelineEditPart.getISequenceEvent().getOccupiedRange(); + if (!occupiedRange.isEmpty()) { + // limite the move to the first sequence event of the target + // lifeline + firstMessageInTargetInstanceRole = occupiedRange.getLowerBound() - LayoutConstants.EXECUTION_CHILDREN_MARGIN; + } + + if (feedbackRangeLimit < sourceBbounds.y + LayoutConstants.EXECUTION_CHILDREN_MARGIN) { + feedbackRangeLimit = sourceBbounds.y + LayoutConstants.EXECUTION_CHILDREN_MARGIN; + } else if (firstMessageInTargetInstanceRole < feedbackRangeLimit + LayoutConstants.EXECUTION_CHILDREN_MARGIN) { + feedbackRangeLimit = firstMessageInTargetInstanceRole - LayoutConstants.EXECUTION_CHILDREN_MARGIN; + } else if (feedbackRangeLimit + GraphicalHelper.getScrollSize(target).y > sourceBbounds.y + sourceBbounds.height - LayoutConstants.EXECUTION_CHILDREN_MARGIN) { + feedbackRangeLimit = sourceBbounds.y + sourceBbounds.height - GraphicalHelper.getScrollSize(target).y - LayoutConstants.EXECUTION_CHILDREN_MARGIN; + } + + cbr.getMoveDelta().y = feedbackRangeLimit - (target.getFigure().getBounds().y + target.getFigure().getBounds().height / 2) + scrollSize.y; // br.getLocation().y + target.showSourceFeedback(cbr); + } + + /** + * Moving a destroy message up and down will also show the feedback of the + * targeted end of life. + * + * @param request + * the "create message" move request + */ + private void showEndOfLifeFeedback(Request request) { + EndOfLifeEditPart endOfLifeEditPart = (EndOfLifeEditPart) ((SequenceMessageEditPart) getHost()).getTarget(); + EndOfLifeOperations.showEndOfLifeFeedback(request, endOfLifeEditPart, (IGraphicalEditPart) ((SequenceMessageEditPart) getHost()).getSource()); + } + + /** + * Remove the the horizontal feedback line. + * <p> + * {@inheritDoc} + */ + @Override + @SuppressWarnings("restriction") + public void eraseSourceFeedback(Request request) { + removeFeedBackOnGuides(); + + super.eraseSourceFeedback(request); + + if (request instanceof BendpointRequest) { + SequenceMessageEditPart thisEvent = (SequenceMessageEditPart) getHost(); + List<EventEnd> ends = EventEndHelper.findEndsFromSemanticOrdering(((ISequenceEventEditPart) getHost()).getISequenceEvent()); + BendpointRequest br = (BendpointRequest) request; + // thisEvent.setCursor(org.eclipse.gmf.runtime.gef.ui.internal.l10n.Cursors.CURSOR_SEG_MOVE); + if (thisEvent.getTarget() instanceof InstanceRoleEditPart) { + InstanceRoleEditPart target = (InstanceRoleEditPart) thisEvent.getTarget(); + ChangeBoundsRequest cbr = new ChangeBoundsRequest(org.eclipse.gef.RequestConstants.REQ_MOVE); + cbr.getMoveDelta().y = br.getLocation().y - (target.getFigure().getBounds().y + target.getFigure().getBounds().height / 2); // br.getLocation().y + target.eraseSourceFeedback(cbr); + } else if (thisEvent.getTarget() instanceof EndOfLifeEditPart) { + EndOfLifeOperations.eraseEndOfLifeFeedback((LifelineEditPart) thisEvent.getTarget().getParent(), br); + } else { + MoveType moveType = getMoveType(thisEvent, br, ends); + if (moveType.needsCompoundMove()) { + eraseCompoundEndFeedback(br, thisEvent, ends, moveType.isFromTop()); + } + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public EditPart getTargetEditPart(Request request) { + if (RequestConstants.REQ_SET_ALL_BENDPOINT.equals(request.getType())) { + return getHost(); + } else { + return super.getTargetEditPart(request); + } + } + + /** + * Change the place of the message in the semantic model if the new + * graphical positions requires it. + * <p> + * {@inheritDoc} + */ + @Override + protected Command getBendpointsChangedCommand(BendpointRequest request) { + invalidCommand = false; + + SequenceMessageEditPart thisEvent = (SequenceMessageEditPart) getHost(); + Command result; + Command baseCommand = super.getBendpointsChangedCommand(request); + ICommand smrc = null; + + Point location = request.getLocation().getCopy(); + GraphicalHelper.screen2logical(location, (IGraphicalEditPart) getHost()); + + Option<Range> finalRange = computeFinalRange(request, thisEvent, location); + + if (finalRange.some()) { + smrc = createReconnectionCommandOnBendpointMove(request, thisEvent, location, finalRange.get()); + } + + List<EventEnd> ends = EventEndHelper.findEndsFromSemanticOrdering(thisEvent.getISequenceEvent()); + invalidCommand = invalidCommand || !baseCommand.canExecute() || !finalRange.some(); + invalidCommand = invalidCommand || org.eclipse.gef.RequestConstants.REQ_MOVE_BENDPOINT.equals(request.getType()); + invalidCommand = invalidCommand || !validateMessageParentOperand(finalRange); + invalidCommand = invalidCommand || !checkGlobalPositions(thisEvent.getISequenceEvent(), finalRange).isEmpty(); + + if (invalidCommand) { + thisEvent.setCursor(org.eclipse.draw2d.Cursors.NO); + result = UnexecutableCommand.INSTANCE; + } else { + String label = baseCommand.getLabel(); + CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(thisEvent.getEditingDomain(), (label != null ? label : "<null>") + " and synchronize ordering"); + SequenceEditPartsOperations.appendFullRefresh(thisEvent, ctc); + + MoveType move = getMoveType(thisEvent, request, ends); + if (move.needsCompoundMove()) { + addCompoundEventCommands(ctc, thisEvent, ends, request, move.isFromTop()); + thisEvent.setCursor(Cursors.SIZENS); + } else { + /* + * Overridden to handle lifeline move/resize when moving the + * selected create/destroy message. + */ + if (thisEvent.getTarget() instanceof InstanceRoleEditPart) { + ctc.compose(getMoveCreateMessageCommand(request, thisEvent, baseCommand)); + } else if (thisEvent.getTarget() instanceof EndOfLifeEditPart) { + ctc.compose(getMoveDestroyMessageCommand(request, thisEvent, baseCommand)); + } + ctc.compose(smrc); + + SequenceEditPartsOperations.addRefreshGraphicalOrderingCommand(ctc, thisEvent); + SequenceEditPartsOperations.addSynchronizeSemanticOrderingCommand(ctc, thisEvent.getISequenceEvent()); + thisEvent.setCursor(org.eclipse.gmf.runtime.gef.ui.internal.l10n.Cursors.CURSOR_SEG_MOVE); + } + + SequenceEditPartsOperations.appendFullRefresh(thisEvent, ctc); + // if (!(thisEvent.getTarget() instanceof EndOfLifeEditPart || + // thisEvent.getTarget() instanceof InstanceRoleEditPart)) { + // addRequestLayout(cc, thisEvent); + // } + + if (!ctc.canExecute()) { + thisEvent.setCursor(org.eclipse.draw2d.Cursors.NO); + result = UnexecutableCommand.INSTANCE; + } else { + result = new ICommandProxy(ctc); + } + } + return result; + } + + private Collection<Integer> checkGlobalPositions(final ISequenceEvent thisEvent, final Option<Range> finalRange) { + Function<ISequenceEvent, Range> futureRangeFunction = new Function<ISequenceEvent, Range>() { + public Range apply(ISequenceEvent from) { + Range verticalRange = from.getVerticalRange(); + if (thisEvent.equals(from) && finalRange.some()) { + verticalRange = finalRange.get(); + } + return verticalRange; + } + }; + return new PositionsChecker(thisEvent.getDiagram(), futureRangeFunction).getInvalidPositions(); + } + + private ICommand createReconnectionCommandOnBendpointMove(BendpointRequest request, SequenceMessageEditPart thisEvent, Point location, Range finalRange) { + SetMessageRangeOperation smrc = new SetMessageRangeOperation((Edge) thisEvent.getNotationView(), finalRange); + Message message = (Message) thisEvent.getISequenceEvent(); + boolean reflectiveMessage = message.isReflective(); + + setOperations(true, message, finalRange, smrc, reflectiveMessage); + setOperations(false, message, finalRange, smrc, reflectiveMessage); + + return CommandFactory.createICommand(thisEvent.getEditingDomain(), smrc); + } + + private void setOperations(boolean source, Message message, Range finalRange, SetMessageRangeOperation smrc, boolean reflectiveMessage) { + Range messageEndRange = source ? new Range(finalRange.getLowerBound(), finalRange.getLowerBound()) : new Range(finalRange.getUpperBound(), finalRange.getUpperBound()); + ISequenceNode currentEnd = source ? message.getSourceElement() : message.getTargetElement(); + Option<Lifeline> endLifeline = currentEnd.getLifeline(); + if (endLifeline.some() && currentEnd instanceof ISequenceEvent) { + ISequenceEvent finalEnd; + Range finalEndRange; + + EventFinder endFinder = new EventFinder(endLifeline.get()); + endFinder.setReconnection(true); + + if (!reflectiveMessage) { + finalEnd = endFinder.findMostSpecificEvent(finalRange); + } else { + finalEnd = (ISequenceEvent) currentEnd; + + boolean compoundSrc = finalEnd instanceof Execution && message.equals(source ? ((Execution) finalEnd).getEndMessage().get() : ((Execution) finalEnd).getStartMessage().get()); + if (!compoundSrc && !finalEnd.equals(endFinder.findMostSpecificEvent(messageEndRange))) { + // It is not allowed to reconnect a reflexive message by + // moving bendpoints + invalidCommand = true; + } + + // look for event source + endFinder.setReconnection(false); + ISequenceEvent potentialSource = endFinder.findMostSpecificEvent(messageEndRange); + if (potentialSource instanceof CombinedFragment) { + invalidCommand = true; + } + } + + // finalSrc can be null while moving a message under its lifeline. + // finalTgt cannot be null : restrain the capability to move out of + // lifeline for destruction messages. + if (source && finalEnd == null) { + finalEnd = (ISequenceEvent) currentEnd; + } + + // look for event end (source/target) + endFinder.setReconnection(false); + ISequenceEvent realEnd = endFinder.findMostSpecificEvent(finalRange); + if (realEnd == null) { + // realEnd can be null while moving a message under its + // lifeline + realEnd = (ISequenceEvent) currentEnd; + } + + boolean noValidation = reflectiveMessage || realEnd instanceof Lifeline; + if (!invalidCommand) { + if (finalEnd != null && (noValidation || realEnd != null && realEnd.canChildOccupy(message, finalRange))) { + finalEndRange = finalEnd.getVerticalRange(); + Rectangle endBounds = new Rectangle(0, finalEndRange.getLowerBound(), 0, finalEndRange.width()); + if (source) { + smrc.setSource(finalEnd.getNotationView(), endBounds); + } else { + smrc.setTarget(finalEnd.getNotationView(), endBounds); + } + } else { + // The message is moved beyond the lifeline range + invalidCommand = true; + } + } + } else if (currentEnd instanceof LostMessageEnd || currentEnd instanceof EndOfLife || currentEnd instanceof InstanceRole) { + Rectangle finalEndBounds = currentEnd.getProperLogicalBounds().getCopy(); + if (source) { + smrc.setSource(currentEnd.getNotationView(), finalEndBounds); + } else { + smrc.setTarget(currentEnd.getNotationView(), finalEndBounds); + } + } + + } + + private Option<Range> computeFinalRange(BendpointRequest request, SequenceMessageEditPart smep, Point location) { + Range finalRange = null; + if (!new ISequenceEventQuery(smep.getISequenceEvent()).isReflectiveMessage()) { + finalRange = new Range(location.y, location.y); + } else { + Edge edge = (Edge) smep.getNotationView(); + SequenceMessageViewQuery query = new SequenceMessageViewQuery(edge); + + int firstPointVerticalPosition = query.getFirstPointVerticalPosition(true); + int lastPointVerticalPosition = query.getLastPointVerticalPosition(true); + + switch (request.getIndex()) { + case 0: + finalRange = safeComputeMessageToSelfFinalRangeFromTop(location, lastPointVerticalPosition); + break; + case 2: + finalRange = safeComputeMessageToSelfFinalRangeFromBottom(firstPointVerticalPosition, location); + break; + case 1: + default: + finalRange = new Range(firstPointVerticalPosition, lastPointVerticalPosition); + break; + } + } + return Options.newSome(finalRange); + } + + private Range safeComputeMessageToSelfFinalRangeFromBottom(int firstPointVerticalPosition, Point newBottomLocation) { + if (newBottomLocation.y >= firstPointVerticalPosition + LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP) { + return new Range(firstPointVerticalPosition, newBottomLocation.y); + } + invalidCommand = true; + return null; + } + + private Range safeComputeMessageToSelfFinalRangeFromTop(Point newTopLocation, int lastPointVerticalPosition) { + if (newTopLocation.y <= lastPointVerticalPosition - LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP) { + return new Range(newTopLocation.y, lastPointVerticalPosition); + } + invalidCommand = true; + return null; + } + + private MoveType getMoveType(SequenceMessageEditPart event, BendpointRequest request, List<EventEnd> ends) { + boolean compoundMove = !Iterables.isEmpty(Iterables.filter(ends, CompoundEventEnd.class)); + boolean msgToSelfMove = new ISequenceEventQuery(event.getISequenceEvent()).isReflectiveMessage(); + boolean needsCompoundEventCommands = compoundMove && !msgToSelfMove; + boolean fromTop = true; + if (compoundMove && msgToSelfMove && ends.size() == 2) { + Point location = request.getLocation().getCopy(); + GraphicalHelper.screen2logical(location, event); + if (request.getExtendedData().containsKey(SequenceMessageEditPart.MSG_TO_SELF_TOP_MOVE)) { + fromTop = (Boolean) request.getExtendedData().get(SequenceMessageEditPart.MSG_TO_SELF_TOP_MOVE); + needsCompoundEventCommands = fromTop ? ends.get(0) instanceof CompoundEventEnd : ends.get(1) instanceof CompoundEventEnd; + } else { + needsCompoundEventCommands = false; + } + } + return new MoveType(needsCompoundEventCommands, fromTop); + } + + private void addCompoundEventCommands(CompositeTransactionalCommand ctc, final SequenceMessageEditPart thisEvent, List<EventEnd> ends, BendpointRequest request, boolean fromTop) { + final EObject thisSemanticEvent = thisEvent.resolveTargetSemanticElement(); + final SequenceDiagramEditPart sdep = EditPartsHelper.getSequenceDiagramPart(thisEvent); + final Range thisRange = thisEvent.getISequenceEvent().getVerticalRange(); + final Point location = request.getLocation().getCopy(); + + final Predicate<SingleEventEnd> toMove = new Predicate<SingleEventEnd>() { + public boolean apply(SingleEventEnd input) { + return !input.getSemanticEvent().equals(thisSemanticEvent); + } + }; + + Dimension resizeDelta = getResizeDelta(location.getCopy(), thisEvent, thisRange, fromTop); + for (CompoundEventEnd cee : Iterables.filter(ends, CompoundEventEnd.class)) { + for (SingleEventEnd see : Iterables.filter(Lists.newArrayList(cee.getEventEnds()), toMove)) { + ISequenceEventEditPart ise = EditPartsHelper.findISequenceEvent(see, sdep); + ISequenceEvent sequenceEvent = ise.getISequenceEvent(); + ChangeBoundsRequest cbr = buildChangeBoundRequest(location.getCopy(), ise, see, resizeDelta); + if (sequenceEvent instanceof AbstractNodeEvent) { + // if sequenveEvent is Execution, we must indicates to the + // ExecutionSelectionValidator that we want resize its + // Execution + cbr.getExtendedData().put(REQUEST_FROM_SEQUENCE_MESSAGE_EDIT_POLICY, true); + } + ctc.compose(new CommandProxy(ise.getCommand(cbr))); + } + } + } + + private ChangeBoundsRequest buildChangeBoundRequest(Point point, ISequenceEventEditPart ise, SingleEventEnd see, Dimension resizeDelta) { + ChangeBoundsRequest cbr = new ChangeBoundsRequest(org.eclipse.gef.RequestConstants.REQ_RESIZE); + cbr.setLocation(point); + cbr.setEditParts(ise); + cbr.setConstrainedResize(true); + if (see.isStart()) { + cbr.setResizeDirection(PositionConstants.NORTH); + cbr.setMoveDelta(new Point(0, resizeDelta.height)); + cbr.setSizeDelta(resizeDelta.getNegated()); + cbr.setConstrainedMove(true); + } else { + cbr.setResizeDirection(PositionConstants.SOUTH); + cbr.setSizeDelta(resizeDelta); + cbr.setConstrainedResize(true); + } + return cbr; + } + + private Dimension getResizeDelta(Point location, ISequenceEventEditPart ise, Range range, boolean fromTop) { + GraphicalHelper.screen2logical(location, ise); + int deltaY = location.y - (fromTop ? range.getLowerBound() : range.getUpperBound()); + deltaY = (int) (deltaY * GraphicalHelper.getZoom(ise)); + return new Dimension(0, deltaY); + } + + /** + * Overridden to resize the targeted RoteExecitionEditPart when moving the + * selected destroy message. + * + * @param baseCommand + * @param smep + * + * @param request + * the current request + * @return a compound command if the destroy message is moved, the super + * command otherwise + */ + private AbstractEMFOperation getMoveDestroyMessageCommand(BendpointRequest br, SequenceMessageEditPart smep, Command baseCommand) { + CompositeTransactionalCommand ctc = new CompositeTransactionalCommand(smep.getEditingDomain(), "Move create message"); + if (smep.getSource() instanceof ISequenceEventEditPart) { + ISequenceEventEditPart source = (ISequenceEventEditPart) smep.getSource(); + Range sourceRange = source.getISequenceEvent().getVerticalRange(); + Range lowerLimit = new Range(sourceRange.getLowerBound() - LayoutConstants.EXECUTION_CHILDREN_MARGIN, sourceRange.getLowerBound() + LayoutConstants.EXECUTION_CHILDREN_MARGIN); + Range upperLimit = new Range(sourceRange.getUpperBound() - LayoutConstants.EXECUTION_CHILDREN_MARGIN, sourceRange.getUpperBound() + LayoutConstants.EXECUTION_CHILDREN_MARGIN); + + Point wantedLocation = br.getLocation().getCopy(); + GraphicalHelper.screen2logical(wantedLocation, smep); + if (lowerLimit.includes(wantedLocation.y) || upperLimit.includes(wantedLocation.y)) { + ctc.compose(org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE); + return ctc; + } + } + EndOfLifeEditPart endOfLifeEditPart = (EndOfLifeEditPart) smep.getTarget(); + final LifelineEditPart lifelineEditPart = (LifelineEditPart) endOfLifeEditPart.getParent(); + + ChangeBoundsRequest cbr = SequenceMessageEditPolicy.getEndOfLifeMoveRequest(endOfLifeEditPart, br.getLocation().getCopy()); + if (cbr != null) { + ctc.compose(new CommandProxy(endOfLifeEditPart.getCommand(cbr))); + } else { + ctc.compose(org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE); + } + + if (ctc.canExecute()) { + ctc.compose(new CommandProxy(baseCommand)); + updateMovingTargetReferencePoint(baseCommand, cbr); + TransactionalEditingDomain editingDomain = smep.getEditingDomain(); + ctc.compose(CommandFactory.createICommand(editingDomain, new ShiftDescendantMessagesOperation(lifelineEditPart.getISequenceEvent(), cbr.getSizeDelta().height, true, false, false))); + } + return ctc; + } + + private static ChangeBoundsRequest getEndOfLifeMoveRequest(EndOfLifeEditPart endOfLifeEditPart, Point location) { + ChangeBoundsRequest cbr = new ChangeBoundsRequest(org.eclipse.gef.RequestConstants.REQ_MOVE); + cbr.setEditParts(endOfLifeEditPart); + cbr.setLocation(location.getCopy()); + cbr.setConstrainedMove(true); + + double zoom = GraphicalHelper.getZoom(endOfLifeEditPart); + GraphicalHelper.screen2logical(location, endOfLifeEditPart); + Rectangle bounds = endOfLifeEditPart.getFigure().getBounds(); + int delta = location.y - (bounds.getCenter().y); + cbr.setMoveDelta(new Point(0, delta * zoom)); + return cbr; + } + + /** + * Overridden to move the targeted InstanceRoleEditPart when moving the + * selected create message. + * + * @param request + * the current request + * @param baseCommand + * @return a compound command if the create message is moved, the super + * command otherwise + */ + private AbstractEMFOperation getMoveCreateMessageCommand(BendpointRequest request, SequenceMessageEditPart smep, Command baseCommand) { + Point normalizedLocation = request.getLocation().getCopy(); + GraphicalHelper.screen2logical(normalizedLocation, smep); + + InstanceRoleEditPart instanceRoleEditPart = (InstanceRoleEditPart) smep.getTarget(); + LifelineEditPart lifelineEditPart = Iterables.getOnlyElement(EditPartsHelper.getAllLifelines(instanceRoleEditPart)); + + CompositeTransactionalCommand cc = new CompositeTransactionalCommand(smep.getEditingDomain(), "Move create message"); + + // limite the move to the first sequence event of the target + int firstMessageInTargetInstanceRole = lifelineEditPart.getISequenceEvent().getVerticalRange().getUpperBound() - LayoutConstants.EXECUTION_CHILDREN_MARGIN; + Range occupiedRange = lifelineEditPart.getISequenceEvent().getOccupiedRange(); + if (!occupiedRange.isEmpty()) { + // limite the move to the first sequence event of the target + firstMessageInTargetInstanceRole = occupiedRange.getLowerBound() - LayoutConstants.EXECUTION_CHILDREN_MARGIN; + } + + int sourceRangeLimit = computeSourceRangeLimit(smep, normalizedLocation, firstMessageInTargetInstanceRole); + if (normalizedLocation.y > sourceRangeLimit) { + cc.compose(org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand.INSTANCE); + return cc; + } + + final ChangeBoundsRequest cbr = new ChangeBoundsRequest(org.eclipse.gef.RequestConstants.REQ_MOVE); + cbr.getMoveDelta().y = sourceRangeLimit - (instanceRoleEditPart.getFigure().getBounds().y + instanceRoleEditPart.getFigure().getBounds().height / 2); + cbr.setConstrainedMove(true); + cbr.setEditParts(instanceRoleEditPart); + cc.compose(new CommandProxy(instanceRoleEditPart.getCommand(cbr))); + cc.compose(new CommandProxy(baseCommand)); + + /* + * These additional commands adjust the positions of the executions and + * messages on the lifeline so that visually they do not move. They are + * dual to the commands we add when moving a normal execution, as in + * that case we want all the executions and messages it contains to move + * along. + */ + final LifelineEditPart lep = EditPartsHelper.getAllLifelines(instanceRoleEditPart).get(0); + + // Avoid EndOfLife Move + TransactionalEditingDomain editingDomain = smep.getEditingDomain(); + cc.compose(CommandFactory.createICommand(editingDomain, new EndOfLifeMoveOperation((Lifeline) lep.getISequenceEvent(), -cbr.getMoveDelta().y))); + cc.compose(CommandFactory.createICommand(editingDomain, new ShiftDirectSubExecutionsOperation(lep.getISequenceEvent(), -cbr.getMoveDelta().y))); + cc.compose(CommandFactory.createICommand(editingDomain, new ShiftDescendantMessagesOperation(lep.getISequenceEvent(), cbr.getMoveDelta().y, true, false, true))); + + return cc; + } + + private void updateMovingTargetReferencePoint(Command baseCommand, final ChangeBoundsRequest cbr) { + if (baseCommand instanceof ICommandProxy && ((ICommandProxy) baseCommand).getICommand() instanceof SetConnectionBendpointsCommand) { + /* + * Update target reference point of the SetConnectionBendpoint base + * command to take into account he move ot the instance role. + */ + SetConnectionBendpointsCommand scbc = (SetConnectionBendpointsCommand) ((ICommandProxy) baseCommand).getICommand(); + scbc.setNewPointList(scbc.getNewPointList(), scbc.getSourceRefPoint(), scbc.getTargetRefPoint().getCopy().getTranslated(cbr.getMoveDelta())); + } + } + + private int computeSourceRangeLimit(SequenceMessageEditPart smep, Point normalizedLocation, int firstMessageInTargetInstanceRole) { + int sourceRangeLimit = normalizedLocation.y; + Range sourceRange = smep.getISequenceEvent().getVerticalRange(); + Range lowerLimit = new Range(sourceRange.getLowerBound(), sourceRange.getLowerBound() + LayoutConstants.EXECUTION_CHILDREN_MARGIN); + Range upperLimit = new Range(sourceRange.getUpperBound() - LayoutConstants.EXECUTION_CHILDREN_MARGIN, sourceRange.getUpperBound()); + + if (firstMessageInTargetInstanceRole < sourceRangeLimit + LayoutConstants.EXECUTION_CHILDREN_MARGIN) { + sourceRangeLimit = firstMessageInTargetInstanceRole - LayoutConstants.EXECUTION_CHILDREN_MARGIN; + } else if (lowerLimit.includes(sourceRangeLimit)) { + sourceRangeLimit = lowerLimit.getUpperBound(); + } else if (upperLimit.includes(sourceRangeLimit)) { + sourceRangeLimit = upperLimit.getLowerBound(); + } + return sourceRangeLimit; + } + + private boolean validateMessageParentOperand(Option<Range> finalRange) { + boolean valid = true; + + SequenceMessageEditPart thisEvent = (SequenceMessageEditPart) getHost(); + Message message = (Message) thisEvent.getISequenceEvent(); + Option<Lifeline> sourceLifeline = message.getSourceLifeline(); + Option<Lifeline> targetLifeline = message.getTargetLifeline(); + + if (finalRange.some() && sourceLifeline.some() && targetLifeline.some()) { + Option<Operand> sourceFinalOperand = Options.newNone(); + Option<Operand> targetFinalOperand = Options.newNone(); + + if (sourceLifeline.get().equals(targetLifeline.get())) { + int lBound = finalRange.get().getLowerBound(); + int uBound = finalRange.get().getUpperBound(); + + sourceFinalOperand = sourceLifeline.get().getParentOperand(new Range(lBound, lBound)); + targetFinalOperand = targetLifeline.get().getParentOperand(new Range(uBound, uBound)); + } else { + sourceFinalOperand = sourceLifeline.get().getParentOperand(finalRange.get()); + targetFinalOperand = targetLifeline.get().getParentOperand(finalRange.get()); + } + + valid = sourceFinalOperand.get() == targetFinalOperand.get(); + } + + return valid; + + } + + private static class MoveType { + private boolean fromTop; + + private boolean needsCompoundMove; + + public MoveType(boolean needsCompoundMove, boolean fromTop) { + this.fromTop = fromTop; + this.needsCompoundMove = needsCompoundMove; + } + + public boolean isFromTop() { + return fromTop; + } + + public boolean needsCompoundMove() { + return needsCompoundMove; + } + + @Override + public String toString() { + return "[fromTop:" + fromTop + ", compound:" + needsCompoundMove + "]"; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceNodeCreationPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceNodeCreationPolicy.java new file mode 100644 index 0000000000..6fce46fe4b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceNodeCreationPolicy.java @@ -0,0 +1,245 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gef.Request; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.requests.CreateRequest; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.description.tool.AbstractToolDescription; +import org.eclipse.sirius.description.tool.ContainerCreationDescription; +import org.eclipse.sirius.description.tool.NodeCreationDescription; +import org.eclipse.sirius.diagram.graphical.edit.policies.CreationUtil; +import org.eclipse.sirius.diagram.graphical.edit.policies.NodeCreationEditPolicy; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.VerticalPositionFunction; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +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.description.tool.CombinedFragmentCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ExecutionCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InteractionUseCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.ObservationPointCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.OperandCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.StateCreationTool; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.command.SequenceDelegatingCommandFactory; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceDiagramEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.StateEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.FrameCreationValidator; +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.tools.api.editor.DDiagramEditor; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutUtils; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactory; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactoryProvider; + +/** + * A node creation edit policy which invokes the ExecutionCreationTool correctly + * (with all the proper variables). + * + * @author pcdavid + */ +public class SequenceNodeCreationPolicy extends NodeCreationEditPolicy { + + /** + * {@inheritDoc} + */ + @Override + protected Command getCreateCommand(CreateRequest request) { + Option<SequenceDiagramEditPart> sdep = shouldRetargetToDiagram(request); + if (sdep.some()) { + return sdep.get().getCommand(request); + } + return super.getCreateCommand(request); + } + + private Option<SequenceDiagramEditPart> shouldRetargetToDiagram(CreateRequest request) { + AbstractToolDescription tool = getTool(request); + if (!(getHost() instanceof StateEditPart) && (tool instanceof InteractionUseCreationTool || tool instanceof CombinedFragmentCreationTool)) { + /* + * If the user is trying to create an IU or CF by clicking on a + * lifeline or execution, redirect the request to the diagram, but + * note the original target so that the initial coverage can be + * computed. + */ + IGraphicalEditPart self = (IGraphicalEditPart) getHost(); + SequenceDiagramEditPart sdep = EditPartsHelper.getSequenceDiagramPart(self); + if (sdep != null) { + @SuppressWarnings("unchecked") + Map<Object, Object> extData = request.getExtendedData(); + extData.put(FrameCreationValidator.ORIGINAL_TARGET, getHost()); + return Options.newSome(sdep); + } + } + + return Options.newNone(); + } + + /** + * Overridden to show feedback for Execution creation. + * + * {@inheritDoc} + */ + @Override + public void showTargetFeedback(Request request) { + if (request instanceof CreateRequest) { + Option<SequenceDiagramEditPart> sdep = shouldRetargetToDiagram((CreateRequest) request); + if (sdep.some() && sdep.get().getEditPolicy(EditPolicy.CONTAINER_ROLE) != null) { + sdep.get().getEditPolicy(EditPolicy.CONTAINER_ROLE).showTargetFeedback(request); + } + } + super.showTargetFeedback(request); + } + + /** + * Overridden to erase feedback for Execution creation. + * + * {@inheritDoc} + */ + @Override + public void eraseTargetFeedback(Request request) { + if (request instanceof CreateRequest) { + Option<SequenceDiagramEditPart> sdep = shouldRetargetToDiagram((CreateRequest) request); + if (sdep.some() && sdep.get().getEditPolicy(EditPolicy.CONTAINER_ROLE) != null) { + sdep.get().getEditPolicy(EditPolicy.CONTAINER_ROLE).eraseTargetFeedback(request); + } + } + super.eraseTargetFeedback(request); + } + + /** + * Overridden to invoke ExecutionCreationTool correctly. + * <p> + * {@inheritDoc} + */ + @Override + protected Command getCreateNodeOnNodeCommand(CreateRequest request, NodeCreationDescription tool, DNode viewnode) { + if (tool instanceof ExecutionCreationTool || tool instanceof StateCreationTool || tool instanceof ObservationPointCreationTool) { + SequenceDiagram sequenceDiagram = EditPartsHelper.getSequenceDiagram(getHost()); + SequenceDDiagram diagram = sequenceDiagram.getSequenceDDiagram(); + + Point location = request.getLocation().getCopy(); + GraphicalHelper.screen2logical(location, (IGraphicalEditPart) getHost()); + EventEnd startingEndPredecessor = SequenceGraphicalHelper.getEndBefore(diagram, location.y); + EventEnd startingEndSuccessor = SequenceGraphicalHelper.getEndAfter(diagram, location.y); + if (tool instanceof ExecutionCreationTool) { + Point bottomRight = location.getCopy().getTranslated(0, 1); + bottomRight = new Point(location.x + 2 * LayoutUtils.SCALE, computeBottomY(diagram, location, startingEndSuccessor)); + GraphicalHelper.logical2screen(bottomRight, (IGraphicalEditPart) getHost()); + request.setSize(new Dimension(LayoutConstants.DEFAULT_EXECUTION_WIDTH, LayoutConstants.DEFAULT_EXECUTION_HEIGHT)); + } + CreationUtil creationUtil = new CreationUtil(request, getDiagramCommandFactory(startingEndPredecessor, startingEndPredecessor, location), getRealLocation(request), request.getSize(), + getHost()); + return creationUtil.getNodeCreationCommand(viewnode, tool); + } else { + return super.getCreateNodeOnNodeCommand(request, tool, viewnode); + } + } + + /** + * Overridden to invoke OperandCreationTool correctly. + * <p> + * {@inheritDoc} + */ + @Override + protected Command getCreateContainerInContainerCommand(CreateRequest request, ContainerCreationDescription tool, DDiagramElementContainer viewNodeContainer) { + final Command result; + if (isCreatingOperandInCombinedFragment(tool, viewNodeContainer)) { + Option<Operand> operand = getOperand(viewNodeContainer); + if (operand.some()) { + SequenceDiagram sequenceDiagram = EditPartsHelper.getSequenceDiagram(getHost()); + SequenceDDiagram diagram = sequenceDiagram.getSequenceDDiagram(); + + EventEnd startingEndPredecessor = SequenceGraphicalHelper.getEndBefore(diagram, operand.get().getVerticalRange().getUpperBound() - 1); + + Point location = request.getLocation().getCopy(); + GraphicalHelper.screen2logical(location, (IGraphicalEditPart) getHost()); + + CreationUtil creationUtil = new CreationUtil(request, getDiagramCommandFactory(startingEndPredecessor, startingEndPredecessor, location), getRealLocation(request), getHost()); + result = creationUtil.getContainerCreationDescription((DDiagramElementContainer) viewNodeContainer.eContainer(), tool); + } else { + result = UnexecutableCommand.INSTANCE; + } + } else { + result = super.getCreateContainerInContainerCommand(request, tool, viewNodeContainer); + } + return result; + } + + private Option<Operand> getOperand(DDiagramElementContainer viewNodeContainer) { + Collection<View> views = ISequenceElementAccessor.getViewsForSemanticElement((SequenceDDiagram) viewNodeContainer.getParentDiagram(), viewNodeContainer.getTarget()); + List<View> operandViews = Lists.newArrayList(Iterables.filter(views, Operand.notationPredicate())); + for (View view : operandViews) { + Option<Operand> optOperand = ISequenceElementAccessor.getOperand(view); + if (optOperand.some()) { + return optOperand; + } + } + return Options.newNone(); + } + + private boolean isCreatingOperandInCombinedFragment(ContainerCreationDescription tool, DDiagramElementContainer viewNodeContainer) { + return tool instanceof OperandCreationTool && Operand.viewpointElementPredicate().apply(viewNodeContainer) + && CombinedFragment.viewpointElementPredicate().apply((DDiagramElement) viewNodeContainer.eContainer()); + } + + private int computeBottomY(SequenceDDiagram diagram, Point location, EventEnd startingEndSuccessor) { + int nextY; + int parentBottomMargin = LayoutConstants.EXECUTION_CHILDREN_MARGIN; + if (startingEndSuccessor != null) { + nextY = new VerticalPositionFunction(diagram).apply(startingEndSuccessor); + } else { + nextY = location.y + LayoutConstants.DEFAULT_EXECUTION_HEIGHT + parentBottomMargin; + } + int bottomY = Math.max(location.y + LayoutConstants.DEFAULT_EXECUTION_HEIGHT, nextY - parentBottomMargin); + return bottomY; + } + + private IDiagramCommandFactory getDiagramCommandFactory(EventEnd startingEndPredecessor, EventEnd finishingEndPredecessor, Point location) { + EditPart host = getHost(); + SequenceDiagram seqDiag = EditPartsHelper.getSequenceDiagram(host); + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(seqDiag.getNotationDiagram()); + final DDiagramEditor diagramEditor = (DDiagramEditor) this.getHost().getViewer().getProperty(DDiagramEditor.EDITOR_ID); + if (diagramEditor == null) { + return null; + } + final Object adapter = diagramEditor.getAdapter(IDiagramCommandFactoryProvider.class); + + final IDiagramCommandFactoryProvider cmdFactoryProvider = (IDiagramCommandFactoryProvider) adapter; + final IDiagramCommandFactory diagramCommandFactory = cmdFactoryProvider.getCommandFactory(domain); + return new SequenceDelegatingCommandFactory(diagramCommandFactory, domain, seqDiag, startingEndPredecessor, finishingEndPredecessor, location); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceSiriusGraphicalNodeEditPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceSiriusGraphicalNodeEditPolicy.java new file mode 100644 index 0000000000..74afd67603 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/SequenceSiriusGraphicalNodeEditPolicy.java @@ -0,0 +1,258 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +import java.util.Map; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.Layer; +import org.eclipse.draw2d.LayeredPane; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.LayerConstants; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.editpolicies.FeedbackHelper; +import org.eclipse.gef.requests.CreateConnectionRequest; +import org.eclipse.gef.requests.ReconnectRequest; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.requests.CreateConnectionViewRequest; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.description.tool.EdgeCreationDescription; +import org.eclipse.sirius.diagram.business.internal.view.EdgeLayoutData; +import org.eclipse.sirius.diagram.graphical.edit.policies.SiriusGraphicalNodeEditPolicy; +import org.eclipse.sirius.diagram.internal.edit.parts.NoteEditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +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.tool.ToolCommandBuilder; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.operation.SequenceEditPartsOperations; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ISequenceEventEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceMessageEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.AbstractMessageCreationValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.CreateMessageCreationValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.DefaultMessageCreationValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.DestroyMessageCreationValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.SequenceGraphicalHelper; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.SequenceMessagesRouter; +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.tools.api.layout.GraphicalHelper; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactoryProvider; + +/** + * This edit policy is overridden to use our own connection router. This way + * during the creation of a message to self, the feedback will just be an edge + * between the first click and the pointer, instead of a polyline connected to + * the bottom of the source edit part (lifeline). + * + * @author smonnier + * + */ +public class SequenceSiriusGraphicalNodeEditPolicy extends SiriusGraphicalNodeEditPolicy { + + /** + * Constant used to store the location in draw2d absolute coordinates of the + * click on the {@link EdgeTarget} source. + */ + protected static final String DRAW2D_EDGE_LOCATION_SOURCE = "edge.absolute.location.source"; + + /** + * {@inheritDoc} + */ + @Override + protected FeedbackHelper getFeedbackHelper(CreateConnectionRequest request) { + if (feedbackHelper == null) { + feedbackHelper = new FeedbackHelper(); + Point p = request.getLocation(); + connectionFeedback = createDummyConnection(request); + connectionFeedback.setConnectionRouter(new SequenceMessagesRouter()); + connectionFeedback.setSourceAnchor(getSourceConnectionAnchor(request)); + feedbackHelper.setConnection(connectionFeedback); + addFeedback(connectionFeedback); + feedbackHelper.update(null, p); + } + return feedbackHelper; + } + + /** + * Overridden to use LayerConstants.SCALED_FEEDBACK_LAYER instead of + * LayerConstants.FEEDBACK_LAYER, to have connection creation feedback + * scalable with zoom & scroll. If LayerConstants.SCALED_FEEDBACK_LAYER + * layer not available, return by default a LayerConstants.FEEDBACK_LAYER + * layer. + * + * {@inheritDoc} + */ + @Override + protected IFigure getFeedbackLayer() { + IFigure feedbackLayer = null; + IFigure scaledLayers = getLayer(LayerConstants.SCALABLE_LAYERS); + if (scaledLayers instanceof LayeredPane) { + LayeredPane layeredPane = (LayeredPane) scaledLayers; + if (scaledLayers != null) { + Layer layer = layeredPane.getLayer(LayerConstants.SCALED_FEEDBACK_LAYER); + if (layer != null) { + feedbackLayer = layer; + } + } + } + if (feedbackLayer == null) { + feedbackLayer = super.getFeedbackLayer(); + } + return feedbackLayer; + } + + /** + * Overridden to save the position on the source. + * + * @param request + * the request to create a new connection targeting an + * {@link org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InstanceRoleEditPart} + * @return super.getConnectionCreateCommand() using CreateConnectionRequest + */ + @SuppressWarnings("unchecked") + @Override + protected Command getConnectionCreateCommand(CreateConnectionRequest request) { + Command connectionCreateCommand = super.getConnectionCreateCommand(request); + if (new RequestQuery(request).isSequenceMessageCreation() && request.getLocation() != null && org.eclipse.gef.RequestConstants.REQ_CONNECTION_START.equals(request.getType())) { + ISequenceEventEditPart host = (ISequenceEventEditPart) getHost(); + ISequenceEvent sequenceEvent = host.getISequenceEvent(); + SequenceDiagram sequenceDiagram = sequenceEvent.getDiagram(); + + Point location = request.getLocation().getCopy(); + GraphicalHelper.screen2logical(location, (IGraphicalEditPart) getHost()); + request.getExtendedData().put(DRAW2D_EDGE_LOCATION_SOURCE, location.getCopy()); + EventEnd startingEndPredecessor = SequenceGraphicalHelper.getEndBefore(sequenceDiagram.getSequenceDDiagram(), location.y); + if (!sequenceEvent.canChildOccupy(null, new Range(location.y, location.y)) || ToolCommandBuilder.isStartingEventEndOfCombinedFragment(sequenceDiagram, startingEndPredecessor)) { + connectionCreateCommand = UnexecutableCommand.INSTANCE; + } + } + return connectionCreateCommand; + } + + /** + * Overridden to manage create message creation. + * + * {@inheritDoc} + */ + @Override + protected Command getConnectionCompleteCommand(CreateConnectionRequest request) { + Command result = UnexecutableCommand.INSTANCE; + if (request instanceof CreateConnectionViewRequest) { + result = super.getConnectionCompleteCommand(request); + } else { + RequestQuery requestQuery = new RequestQuery(request); + if (!requestQuery.isSequenceMessageCreation()) { + result = super.getConnectionCompleteCommand(request); + } else { + ISequenceEventEditPart host = (ISequenceEventEditPart) getHost(); + ISequenceEvent sequenceEvent = host.getISequenceEvent(); + SequenceDiagram sequenceDiagram = sequenceEvent.getDiagram(); + + EditPart sourceEditPart = request.getSourceEditPart(); + EditPart targetEditPart = request.getTargetEditPart(); + + Option<ISequenceElement> sequenceEventSource = ISequenceElementAccessor.getISequenceElement((View) sourceEditPart.getModel()); + Option<ISequenceElement> sequenceEventTarget = ISequenceElementAccessor.getISequenceElement((View) targetEditPart.getModel()); + + Map<?, ?> extendedData = request.getExtendedData(); + Point firstClickLocation = (Point) extendedData.get(DRAW2D_EDGE_LOCATION_SOURCE); + Point secondClickLocation = request.getLocation().getCopy(); + GraphicalHelper.screen2logical(secondClickLocation, (IGraphicalEditPart) getHost()); + + if (firstClickLocation != null && secondClickLocation != null + && !ExecutionSemanticEditPolicy.isCombinedFragmentTitleRangeEdgeCreation(sequenceEvent, sequenceDiagram, firstClickLocation, secondClickLocation)) { + AbstractMessageCreationValidator validator = null; + + if (requestQuery.isCreateMessageCreation()) { + validator = new CreateMessageCreationValidator(); + } else if (requestQuery.isDestroyMessageCreation()) { + validator = new DestroyMessageCreationValidator(); + } else { + validator = new DefaultMessageCreationValidator(); + } + + validator.setSource(sequenceEventSource.get()); + validator.setTarget(sequenceEventTarget.get()); + + validator.setFirstClickLocation(firstClickLocation); + validator.setSecondClickLocation(secondClickLocation); + + if ((request.getSourceEditPart() instanceof NoteEditPart) || firstClickLocation == null || secondClickLocation == null || validator.isValid(request)) { + result = super.getConnectionCompleteCommand(request); + } + } + } + } + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.graphical.edit.policies.SiriusGraphicalNodeEditPolicy#buildCreateEdgeCommand(org.eclipse.gef.requests.CreateConnectionRequest, + * org.eclipse.sirius.EdgeTarget, org.eclipse.sirius.EdgeTarget, + * org.eclipse.sirius.description.tool.EdgeCreationDescription, + * org.eclipse.sirius.tools.api.command.IDiagramCommandFactoryProvider, + * org.eclipse.sirius.diagram.business.internal.view.EdgeLayoutData) + */ + @Override + protected Command buildCreateEdgeCommand(CreateConnectionRequest request, EdgeTarget source, EdgeTarget target, EdgeCreationDescription edgeCreationDescription, + IDiagramCommandFactoryProvider cmdFactoryProvider, EdgeLayoutData edgeLayoutData) { + CompoundCommand result = new CompoundCommand(); + IGraphicalEditPart host = (IGraphicalEditPart) getHost(); + SequenceEditPartsOperations.appendFullRefresh(host, result); + addStoreLayoutDataCommand(result, edgeLayoutData); + SequenceEditPartsOperations.buildCreateEdgeCommand(host, result, request, source, target, edgeCreationDescription, cmdFactoryProvider); + SequenceEditPartsOperations.appendFullRefresh(host, result); + return result; + } + + /** + * Overridden to forbidden explicit source reconnection of Message. + * + * {@inheritDoc} + */ + @Override + protected Command getReconnectSourceCommand(ReconnectRequest request) { + Command result = null; + if (request.getConnectionEditPart() instanceof SequenceMessageEditPart) { + result = UnexecutableCommand.INSTANCE; + } else { + result = super.getReconnectSourceCommand(request); + } + return result; + } + + /** + * Overridden to forbidden explicit target reconnection of Message. + * + * {@inheritDoc} + */ + @Override + protected Command getReconnectTargetCommand(ReconnectRequest request) { + Command result = null; + if (request.getConnectionEditPart() instanceof SequenceMessageEditPart) { + result = UnexecutableCommand.INSTANCE; + } else { + result = super.getReconnectTargetCommand(request); + } + return result; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/StateSelectionEditPolicy.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/StateSelectionEditPolicy.java new file mode 100644 index 0000000000..dac0f468a8 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/policy/StateSelectionEditPolicy.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy; + +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.gef.commands.Command; +import org.eclipse.gef.requests.ChangeBoundsRequest; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.StateEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator.AbstractNodeEventResizeSelectionValidator; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; + +/** + * Specialization of the default policy for states, in order to customize resize + * command and feedback. + * + * @author mporhel + */ +public class StateSelectionEditPolicy extends ExecutionSelectionEditPolicy { + + /** + * {@inheritDoc} + */ + @Override + protected Command getResizeCommand(ChangeBoundsRequest request) { + updateHorizontalResize(request); + return super.getResizeCommand(request); + } + + /* + * Feedback + */ + /** + * Show/update the horizontal feedback lines aligned on the top and bottom + * of the execution. + * <p> + * {@inheritDoc} + */ + @Override + protected void showChangeBoundsFeedback(ChangeBoundsRequest request) { + eraseChangeBoundsFeedback(request); + + StateEditPart hostPart = (StateEditPart) getHost(); + AbstractNodeEvent host = (AbstractNodeEvent) hostPart.getISequenceEvent(); + RequestQuery requestQuery = new RequestQuery(request); + + if (requestQuery.isResize()) { + updateHorizontalResize(request); + customiseFeedbackFigure(request); + + AbstractNodeEventResizeSelectionValidator validator = AbstractNodeEventResizeSelectionValidator.getOrCreateValidator(request, host); + validator.validate(); + showResizeFeedBack(request); + feedBack(validator); + } else { + super.showChangeBoundsFeedback(request); + } + } + + /** + * Customize feedback : State figure is centered regarding the parent + * figure. Don't use BorderItemLocator to locate the feedback. + * + * @param request + * current resuqte to feedback. + */ + private void customiseFeedbackFigure(ChangeBoundsRequest request) { + final IFigure feedback = getDragSourceFeedbackFigure(); + final Rectangle rect = getInitialFeedbackBounds().getCopy(); + getHostFigure().translateToAbsolute(rect); + rect.translate(request.getMoveDelta()); + rect.resize(request.getSizeDelta()); + getHostFigure().translateToRelative(rect); + + getHostFigure().translateToAbsolute(rect); + feedback.translateToRelative(rect); + feedback.setBounds(rect); + } + + /** + * Update the request, to center the horizontal resize. + * + * @param request + * the current request. + */ + private void updateHorizontalResize(ChangeBoundsRequest request) { + Point moveDelta = new Point(request.getMoveDelta()); + Dimension sizeDelta = new Dimension(request.getSizeDelta()); + RequestQuery requestQuery = new RequestQuery(request); + + if (requestQuery.isResize()) { + if (moveDelta.x == 0 && sizeDelta.width != 0) { + moveDelta.x = -sizeDelta.width; + } + + if (sizeDelta.width == 0 && moveDelta.x != 0) { + sizeDelta.width = -moveDelta.x; + } + + if (moveDelta.x == -sizeDelta.width) { + sizeDelta.width -= moveDelta.x; + } + + request.setMoveDelta(moveDelta); + request.setSizeDelta(sizeDelta); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/tools/SequenceMessageSelectConnectionEditPartTracker.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/tools/SequenceMessageSelectConnectionEditPartTracker.java new file mode 100644 index 0000000000..3715cf5431 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/tools/SequenceMessageSelectConnectionEditPartTracker.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.tools; + +import java.util.Map; + +import org.eclipse.draw2d.Connection; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.gef.ConnectionEditPart; +import org.eclipse.gef.DragTracker; +import org.eclipse.gef.Request; +import org.eclipse.gef.requests.BendpointRequest; +import org.eclipse.gmf.runtime.gef.ui.internal.tools.SelectConnectionEditPartTracker; + +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceMessageEditPart; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; + +/** + * Specific connection selection tracker to handle move of messageToSelf + * messages. + * + * @author mporhel + * + */ +@SuppressWarnings("restriction") +public class SequenceMessageSelectConnectionEditPartTracker extends SelectConnectionEditPartTracker implements DragTracker { + + private boolean fromTop = true; + + private BendpointRequest bendpointRequest; + + private boolean msgToSelfMove; + + /** + * Method SequenceMessageSelectConnectionEditPartTracker. + * + * @param owner + * ConnectionNodeEditPart that creates and owns the tracker + * object + */ + public SequenceMessageSelectConnectionEditPartTracker(ConnectionEditPart owner) { + super(owner); + } + + /** + * {@inheritDoc} + */ + @Override + protected Request createSourceRequest() { + Request rq = super.createSourceRequest(); + if (rq instanceof BendpointRequest) { + bendpointRequest = (BendpointRequest) rq; + } + return rq; + } + + /** + * {@inheritDoc} + */ + @Override + protected void updateSourceRequest() { + super.updateSourceRequest(); + if (bendpointRequest != null) { + @SuppressWarnings("unchecked") + Map<Object, Object> extData = bendpointRequest.getExtendedData(); + if (msgToSelfMove) { + extData.put(SequenceMessageEditPart.MSG_TO_SELF_TOP_MOVE, fromTop); + } else { + extData.remove(SequenceMessageEditPart.MSG_TO_SELF_TOP_MOVE); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean handleButtonDown(int button) { + boolean res = super.handleButtonDown(button); + SequenceMessageEditPart smep = (SequenceMessageEditPart) getSourceEditPart(); + if (new ISequenceEventQuery(smep.getISequenceEvent()).isReflectiveMessage()) { + Range range = smep.getISequenceEvent().getVerticalRange(); + Point location = getLocation().getCopy(); + GraphicalHelper.screen2logical(location, smep); + + Connection connection = smep.getConnectionFigure(); + + int x = connection.getPoints().getMidpoint().x; + if (x == location.x) { + msgToSelfMove = false; + } else { + fromTop = location.y <= range.getLowerBound() || location.y < range.middleValue(); + msgToSelfMove = true; + } + } + return res; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractInstanceRoleValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractInstanceRoleValidator.java new file mode 100644 index 0000000000..05cfc0fc52 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractInstanceRoleValidator.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.notation.Bounds; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; + +/** + * Abstract class to validate InstanceRole move & resize request and get from it + * a command. + * + * @author edugueperoux + * + */ +public abstract class AbstractInstanceRoleValidator { + + /** + * Common map of future location for instanceRoles in move/resize. + */ + protected Map<InstanceRole, Point> moveDeltas = new HashMap<InstanceRole, Point>(); + + /** + * Common comparator to have calculated ordering in x coordinate. + */ + protected InstanceRoleGraphicalHorizontalOrderingComparator comparator = new InstanceRoleGraphicalHorizontalOrderingComparator(); + + /** + * List of instanceRoles in selection of the request. + */ + protected List<InstanceRole> instanceRoles; + + /** + * {@inheritDoc} + */ + public void setSequenceElements(List<InstanceRole> sequenceElements) { + this.instanceRoles = sequenceElements; + } + + /** + * {@inheritDoc} + */ + public boolean isValid(ChangeBoundsRequest request) { + Preconditions.checkNotNull(instanceRoles, "validator must know on which instanceRoles check the request validation"); + + return true; + } + + /** + * Get the index in the instanceRolesToMove left to right ordered list where + * to insert the current instanceRole in move at the future location + * locationAfterMoving. + * + * @param locationAfterMoving + * the requested location for the instanceRole in move + * @param instanceRolesToMove + * the instanceRolesToMove left to right ordered list + * @return the index in the instanceRolesToMove left to right ordered list + * where to insert the current instanceRole in move at the future + * location locationAfterMoving + */ + protected int getDropIndex(Point locationAfterMoving, List<InstanceRole> instanceRolesToMove) { + ListIterator<InstanceRole> iterator = instanceRolesToMove.listIterator(); + InstanceRole instanceRoleEditPart; + int x = 0; + int i = 0; + while (iterator.hasNext()) { + instanceRoleEditPart = iterator.next(); + Bounds bounds = (Bounds) instanceRoleEditPart.getNotationNode().getLayoutConstraint(); + x = bounds.getX(); + if (moveDeltas != null && moveDeltas.containsKey(instanceRoleEditPart)) { + x += moveDeltas.get(instanceRoleEditPart).x; + } + if (locationAfterMoving.x <= x) { + return i; + } else { + i++; + } + } + return i; + } + + /** + * Test if dropIndex is same as initialIndex. + * + * @param dropIndex + * future index of the instanceRole in move. + * @param instanceRole + * the instanceRole in move + * + * @return true if the dropIndex is same as initialIndex + */ + protected boolean dropIndexSameAsInitialIndex(int dropIndex, InstanceRole instanceRole) { + List<InstanceRole> allInstanceRoleEditParts = getOrderedMovableInstanceRoles(Collections.<InstanceRole> emptyList()); + return allInstanceRoleEditParts.indexOf(instanceRole) == dropIndex; + } + + /** + * Get a left-to-right ordered list of movable InstanceRole. + * + * @param instanceRoleToMove + * the InstanceRole in move + * + * @return the left-to-right ordered list of movable InstanceRole + */ + protected List<InstanceRole> getOrderedMovableInstanceRoles(List<InstanceRole> instanceRoleToMove) { + List<InstanceRole> allInstanceRoles = Lists.newArrayList(instanceRoles.get(0).getDiagram().getAllInstanceRoles()); + allInstanceRoles.removeAll(instanceRoleToMove); + Collections.sort(allInstanceRoles, comparator); + + return allInstanceRoles; + } + + /** + * Move instanceRoles at the right of the requested location of the + * instanceRole in move. + * + * @param boundsAfterMoving + * requested Bounds of InstanceRole in move + * @param iteratorForRight + * iterator on the left-right ordered list of instanceRoles at + * the right of requested location of the instanceRole in move + */ + protected void moveOverLappedAtTheRightForMoving(Rectangle boundsAfterMoving, ListIterator<InstanceRole> iteratorForRight) { + + } + + public Map<InstanceRole, Point> getMoveDeltas() { + return moveDeltas; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractInteractionFrameValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractInteractionFrameValidator.java new file mode 100644 index 0000000000..78b7bc69cd --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractInteractionFrameValidator.java @@ -0,0 +1,387 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.requests.ChangeBoundsRequest; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractFrame; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * This class is responsible to check whether a request on an interaction use + * should be accepted (i.e. it would produce a well-formed diagram). While doing + * the validation, it also stores all the relevant information required to + * actually perform the interaction properly. + * + * @author mporhel + */ +public abstract class AbstractInteractionFrameValidator { + private static final String FRAME_RESIZE_VALIDATOR = "org.eclipse.sirius.sequence.resize.frame.validator"; + + /** + * Expansion zone. + */ + protected Range expansionZone = Range.emptyRange(); + + /** + * Current interaction use or combined fragment. + */ + protected final AbstractFrame frame; + + /** + * Final Range of the current interaction use. + */ + protected Range finalRange; + + /** + * Initial Range of the current interaction use. + */ + protected Range initialRange; + + /** + * The default interaction use/combined fragment height. + */ + protected int defaultFrameHeight; + + /** + * Validation status. + */ + protected boolean valid = true; + + /** + * Other moved elements. + */ + protected final Set<ISequenceEvent> movedElements = Sets.newHashSet(); + + /** + * Not in moved elements. + */ + protected final Predicate<ISequenceEvent> unmoved = Predicates.not(Predicates.in(movedElements)); + + private boolean initialized; + + private final Collection<Integer> invalidPositions = Lists.newArrayList(); + + private Predicate<Object> unMove = Predicates.instanceOf(Lifeline.class); + + private Predicate<Object> invalidParents; + + private Function<ISequenceEvent, Range> futureRangeFunction = new Function<ISequenceEvent, Range>() { + + public Range apply(ISequenceEvent from) { + Range range = from.getVerticalRange(); + if (frame.equals(from)) { + range = finalRange; + } else if (expansionZone != null && !expansionZone.isEmpty()) { + if (range.includes(expansionZone.getLowerBound())) { + range = new Range(range.getLowerBound(), range.getUpperBound() + expansionZone.width()); + } else if (range.getLowerBound() >= expansionZone.getLowerBound()) { + range = range.shifted(expansionZone.width()); + } + } + + return range; + } + }; + + /** + * Allow to query the request. + */ + private final RequestQuery requestQuery; + + /** + * Constructor. + * + * @param frame + * the interaction use or combined fragment which will be + * resized. + * @param requestQuery + * a query on the request targeting the execution. + */ + public AbstractInteractionFrameValidator(AbstractFrame frame, RequestQuery requestQuery) { + this.frame = frame; + this.requestQuery = requestQuery; + this.valid = false; + + this.invalidParents = Predicates.or(Predicates.instanceOf(AbstractFrame.class), Predicates.instanceOf(State.class)); + + } + + /** + * Return the validation status. Validate the request result in the first + * call only. + * + * @return the validation status. + */ + public final boolean isValid() { + if (!initialized) { + validate(); + initialized = true; + } + return valid; + } + + public Range getFinalRange() { + return finalRange; + } + + /** + * Performs all the computations required to validate the resizing, and + * stores any important information which will be useful to actually execute + * the resize if it is valid, like for example avoid contact with siblings. + */ + protected void validate() { + valid = checkAndComputeRanges(); + if (valid) { + Collection<ISequenceEvent> finalParents = getFinalParentsWithAutoExpand(); + + Collection<ISequenceEvent> movableParents = Lists.newArrayList(Iterables.filter(finalParents, Predicates.not(unMove))); + Collection<ISequenceEvent> fixedParents = Lists.newArrayList(Iterables.filter(finalParents, unMove)); + if (movableParents.isEmpty() || !movedElements.containsAll(movableParents)) { + + valid = valid && Iterables.isEmpty(Iterables.filter(finalParents, invalidParents)); + valid = valid && (!Iterables.any(finalParents, Predicates.instanceOf(Operand.class)) || finalParents.size() == 1); + valid = valid && checkFinalRangeStrictlyIncludedInParents(movableParents); + valid = valid && checkLocalSiblings(movableParents); + } + valid = valid && checkFinalRangeStrictlyIncludedInParents(fixedParents); + valid = valid && checkLocalSiblings(fixedParents); + } + + if (getRequestQuery().isResize()) { + valid = valid && checkGlobalPositions(); + } + } + + private Collection<ISequenceEvent> getFinalParentsWithAutoExpand() { + Collection<ISequenceEvent> finalParentsWithAutoExpand = Lists.newArrayList(); + Collection<ISequenceEvent> finalParents = getFinalParents(); + Collection<Lifeline> coveredLifelines = frame.computeCoveredLifelines(); + for (ISequenceEvent localParent : finalParents) { + // check the need of space expansion + if (localParent != null) { + if (!movedElements.contains(localParent) && !localParent.canChildOccupy(frame, finalRange, Lists.newArrayList(movedElements), coveredLifelines)) { + expansionZone = computeExpansionZone(); + } + + finalParentsWithAutoExpand.add(localParent); + } + } + + return finalParentsWithAutoExpand; + } + + /** + * Computes, checks and stores the initial and final range of the + * interaction use if the resize is performed. + */ + private boolean checkAndComputeRanges() { + // Proper range + initialRange = frame.getVerticalRange(); + Rectangle newBounds = getResizedBounds(new Rectangle(0, initialRange.getLowerBound(), 0, initialRange.width())); + + if (newBounds.height < defaultFrameHeight) { + finalRange = initialRange; + return false; + } + + finalRange = Range.verticalRange(newBounds); + return true; + } + + /** + * Resizing an interaction use can not change which parents it is on, and + * can not have any impact on that parents's ranges, so the final range of + * the interaction use after the resize must be strictly included in the + * ranges of the parents. + */ + private boolean checkFinalRangeStrictlyIncludedInParents(Collection<ISequenceEvent> parentEvents) { + boolean checked = true; + + Iterable<ISequenceEvent> unMovedParents = Iterables.filter(parentEvents, unmoved); + Iterator<ISequenceEvent> iterator = unMovedParents.iterator(); + while (checked && iterator.hasNext()) { + ISequenceEvent parent = iterator.next(); + Range parentRange = parent.getVerticalRange(); + + if (expansionZone != null && !expansionZone.isEmpty()) { + parentRange = new Range(parentRange.getLowerBound(), parentRange.getUpperBound() + expansionZone.width()); + } + /* + * We make two tests separately so that is is easier when debugging + * to determine which of the conditions went wrong, if any. + */ + boolean interactionInRange = parentRange.includes(finalRange.grown(LayoutConstants.EXECUTION_CHILDREN_MARGIN)); + checked = checked && interactionInRange; + } + + return checked; + } + + /** + * Get final parents event after application of the current interaction. + * + * @return final parents. + */ + protected abstract Collection<ISequenceEvent> getFinalParents(); + + private boolean checkLocalSiblings(Collection<ISequenceEvent> finalParents) { + boolean okForSiblings = true; + for (ISequenceEvent localParent : finalParents) { + for (ISequenceEvent localSibling : Iterables.filter(localParent.getSubEvents(), unmoved)) { + if (frame.equals(localSibling)) { + continue; + } + + okForSiblings = checkSibling(localSibling); + if (!okForSiblings) { + break; + } + } + if (!okForSiblings) { + break; + } + } + return okForSiblings; + } + + private boolean checkSibling(ISequenceEvent sibling) { + Range siblingRange = sibling.getVerticalRange(); + + if (canExpand()) { + if (expansionZone != null && !expansionZone.isEmpty()) { + siblingRange = getExpandedRange(siblingRange); + } else if (siblingRange.intersects(finalRange)) { + expansionZone = computeExpansionZone(); + siblingRange = getExpandedRange(siblingRange); + } + // Uncomment to avoid forbidden feedback between resize and resize + + // auto-expand + // return !siblingRange.intersects(finalRange); + } + return !siblingRange.intersects(finalRange); + } + + private Range getExpandedRange(Range siblingRange) { + if (expansionZone != null && !expansionZone.isEmpty() && siblingRange.intersects(expansionZone)) { + return siblingRange.shifted(expansionZone.width()); + } + return siblingRange; + } + + private Rectangle getResizedBounds(Rectangle bounds) { + return getRequestQuery().getLogicalTransformedRectangle(bounds); + } + + /** + * Get the expansion zone requested to validate the move. + * + * @return an expansion zone. + */ + public Range getExpansionZone() { + return canExpand() && expansionZone != null ? expansionZone : Range.emptyRange(); + } + + /** + * Check that the current validator handles auto-expand. + * + * @return true if the validator supports the autoexpand. + */ + protected abstract boolean canExpand(); + + /** + * Compute the allowed expansion zone. + * + * @return the computed expansion zone. + */ + protected abstract Range computeExpansionZone(); + + /** + * Other moved elements. + * + * @param otherMovedElements + * the other moved elements. + */ + public void setMovedElements(Collection<ISequenceEvent> otherMovedElements) { + if (otherMovedElements != null && !otherMovedElements.isEmpty()) { + movedElements.addAll(otherMovedElements); + } + } + + /** + * Get the validator from the request extended data or a new one. + * + * @param cbr + * the current resize request. + * @param host + * the host execution + * @return a validator. + */ + @SuppressWarnings("unchecked") + public static AbstractInteractionFrameValidator getOrCreateResizeValidator(ChangeBoundsRequest cbr, AbstractFrame host) { + RequestQuery requestQuery = new RequestQuery(cbr); + Preconditions.checkArgument(requestQuery.isResize()); + AbstractInteractionFrameValidator validator = null; + Object object = cbr.getExtendedData().get(FRAME_RESIZE_VALIDATOR); + if (object instanceof AbstractInteractionFrameValidator) { + validator = (AbstractInteractionFrameValidator) object; + if (!validator.getRequestQuery().getLogicalDelta().equals(requestQuery.getLogicalDelta())) { + validator = null; + } + } + + if (validator == null && requestQuery.isResize()) { + if (host instanceof CombinedFragment) { + validator = new CombinedFragmentResizeValidator((CombinedFragment) host, requestQuery); + } else if (host instanceof InteractionUse) { + validator = new InteractionUseResizeValidator((InteractionUse) host, requestQuery); + } + cbr.getExtendedData().put(FRAME_RESIZE_VALIDATOR, validator); + } + return validator; + } + + public Collection<Integer> getInvalidPositions() { + return invalidPositions; + } + + private boolean checkGlobalPositions() { + boolean safeMove = true; + invalidPositions.addAll(new PositionsChecker(frame.getDiagram(), futureRangeFunction).getInvalidPositions()); + safeMove = invalidPositions.isEmpty(); + return safeMove; + } + + public RequestQuery getRequestQuery() { + return requestQuery; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractMessageCreationValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractMessageCreationValidator.java new file mode 100644 index 0000000000..0ef196f71c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractMessageCreationValidator.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.gef.requests.CreateConnectionRequest; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; + +/** + * Abstract validator to check if a message creation request is valid. + * + * @author edugueperoux + */ +public abstract class AbstractMessageCreationValidator { + + /** + * {@link ISequenceElement} source. + */ + protected ISequenceElement sequenceElementSource; + + /** + * {@link ISequenceElement} target. + */ + protected ISequenceElement sequenceElementTarget; + + /** + * coordinate of the click on the {@link ISequenceElement} source. + */ + protected Point firstClickLocation; + + /** + * coordinate of the click on the {@link ISequenceElement} target. + */ + protected Point secondClickLocation; + + /** + * Check if a message creation request is valid. + * + * @param request + * the {@link CreateConnectionRequest} of a message creation to + * validate + * + * @return true if request is valid + */ + public boolean isValid(CreateConnectionRequest request) { + // Preconditions.checkNotNull(sequenceElementSource, + // "validator must know on which ISequenceElement check the request validation"); + // Preconditions.checkNotNull(sequenceElementTarget, + // "validator must know on which ISequenceElement check the request validation"); + // Preconditions.checkNotNull(firstClickLocation, + // "validator must know the click on the first ISequenceElement"); + // Preconditions.checkNotNull(secondClickLocation, + // "validator must know the click on the second ISequenceElement"); + boolean valid = true; + + valid = valid && sequenceElementSource != null; + valid = valid && sequenceElementTarget != null; + valid = valid && firstClickLocation != null; + valid = valid && secondClickLocation != null; + + return valid; + } + + /** + * Setter for {@link ISequenceElement} source. + * + * @param elementSource + * the {@link ISequenceElement} source + */ + public void setSource(ISequenceElement elementSource) { + this.sequenceElementSource = elementSource; + } + + /** + * Setter for {@link ISequenceElement} target. + * + * @param elementTarget + * the {@link ISequenceElement} target + */ + public void setTarget(ISequenceElement elementTarget) { + this.sequenceElementTarget = elementTarget; + } + + /** + * {@inheritDoc} + */ + public void setFirstClickLocation(Point firstClickLocation) { + this.firstClickLocation = firstClickLocation; + } + + /** + * {@inheritDoc} + */ + public void setSecondClickLocation(Point secondClickLocation) { + this.secondClickLocation = secondClickLocation; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractNodeEventResizeSelectionValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractNodeEventResizeSelectionValidator.java new file mode 100644 index 0000000000..fc1cd6f7b7 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractNodeEventResizeSelectionValidator.java @@ -0,0 +1,491 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.notation.Location; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractFrame; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +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.ordering.CompoundEventEnd; +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.part.ExecutionEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.FinalParentHelper; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Abstract class to validate Execution move & resize request and get from it a + * command. + * + * @author edugueperoux + * + */ +public class AbstractNodeEventResizeSelectionValidator { + + /** + * Key constant use for request on multiple selection. + */ + public static final String GROUP_REQUEST_ALREADY_ANSWERED = "Already answered"; + + private static final String EXECUTION_RESIZE_VALIDATOR = "org.eclipse.sirius.sequence.resize.execution.validator"; + + /** + * The expansionZine. + */ + protected Range expansionZone; + + /** + * The finalParent. + */ + protected ISequenceEvent finalParent; + + /** + * Common map of future location for executions in move/resize. + */ + protected Map<AbstractNodeEvent, Location> moveDeltas = new HashMap<AbstractNodeEvent, Location>(); + + private boolean valid; + + private AbstractNodeEvent host; + + private ChangeBoundsRequest request; + + private boolean initialized; + + private Collection<Integer> invalidPositions = Lists.newArrayList(); + + private RequestQuery requestQuery; + + /** + * Constructor. + * + * @param host + * the main execution to resize/move + * @param request + * the resize request targeting the execution. + */ + protected AbstractNodeEventResizeSelectionValidator(AbstractNodeEvent host, ChangeBoundsRequest request) { + this.host = host; + this.requestQuery = new RequestQuery(request); + Preconditions.checkArgument(requestQuery.isResize()); + this.request = request; + } + + /** + * Return the validation status. Validate the request result in the first + * call only. + * + * @return the validation status. + */ + public final boolean isValid() { + validate(); + return valid; + } + + /** + * Performs all the computations required to validate the resizing, and + * stores any important information which will be useful to actually execute + * the move if it is valid, like for example avoid contact with siblings or + * handle reconnection. + */ + public final void validate() { + if (!initialized) { + doValidation(); + initialized = true; + } + } + + /** + * Performs all the computations required to validate the resizing, and + * stores any important information which will be useful to actually execute + * the resize if it is valid, like for example avoid contact with siblings. + */ + private void doValidation() { + Preconditions.checkNotNull(host, "validator must know on which executions check the request validation"); + + FinalParentHelper finalParentHelper = new FinalParentHelper(host, requestQuery); + finalParentHelper.computeFinalParent(); + expansionZone = finalParentHelper.getRequiredExpansion(); + finalParent = finalParentHelper.getFinalParent(); + + if (finalParent == null && requestQuery.isResizeFromBottom() && expansionZone != null && expansionZone.width() != 0) { + finalParent = host.getParentEvent(); + } + + valid = validateNewBoundsForAllTargets() && finalParent != null; + valid = valid && checkGlobalPositions(); + } + + /** + * Verifies whether the new position/size of the execution would still be + * valid if we accept the request. In particular, ensures sibling events can + * not overlap. + * + * @return true if the request is validated to true + */ + protected boolean validateNewBoundsForAllTargets() { + return Iterables.all(Iterables.filter(request.getEditParts(), ExecutionEditPart.class), new Predicate<ExecutionEditPart>() { + public boolean apply(ExecutionEditPart input) { + return validateNewBounds(input); + } + }); + } + + /** + * Checks whether the new bounds implied by the requested change are valid + * for this execution. Uses Draw2D information to get the current bounds. + */ + private boolean validateNewBounds(ExecutionEditPart self) { + final boolean result; + Rectangle bounds = self.getFigure().getBounds().getCopy(); + Rectangle newBounds = requestQuery.getFinalBounds(self); + ISequenceEvent selfEvent = self.getISequenceEvent(); + ISequenceEvent parent = selfEvent.getParentEvent(); + boolean newBoundsFails = (newBounds.width <= 0 && bounds.width != 0) || newBounds.height <= 0; + if (newBoundsFails || parent == null) { + result = false; + } else { + boolean isMove = requestQuery.isMove(); + /* + * If this is a MOVE, children will move along with us, so it is OK + * for them. Otherwise this is a RESIZE, where children do not move, + * so the resize is constrained by the range currently occupied by + * them. + */ + boolean okForChildren = isMove || Range.verticalRange(newBounds).includes(self.getISequenceEvent().getOccupiedRange()); + // linkedMessages are moved during execution resize too + // boolean okForLinkedMessages = isMove || + // validateMessageToSelfMinRanges(self, request, + // GraphicalHelper.verticalRange(bounds), + // GraphicalHelper.verticalRange(newBounds)); + boolean okForParent = ((AbstractNodeEvent) self.getISequenceEvent()).getLifeline().get().getValidSubEventsRange().includes(Range.verticalRange(newBounds)); + /* + * Do not allow resize to expand beyond the range of the parent. + */ + if (requestQuery.isResize()) { + if (!parent.getVerticalRange().includes(Range.verticalRange(newBounds))) { + okForParent = false; + } + } + /* + * Also check that the messages which will move with us will not + * become inconsistent. + */ + boolean okForMessageEnds = (expansionZone != null) || validateMessageEndsConsistency(self, bounds, newBounds); + result = okForChildren && okForParent && okForMessageEnds; + } + return result; + } + + /** + * If this execution is delimited by a start and finish message, make sure + * they always point to the same remote execution/lifeline. + * + * @param move + * indicates the current action : move or resize. + */ + private boolean validateMessageEndsConsistency(ExecutionEditPart self, Rectangle bounds, Rectangle newBounds) { + boolean result = true; + AbstractNodeEvent abstractNodeEvent = (AbstractNodeEvent) self.getISequenceEvent(); + List<Message> delimitingMessages = Lists.newArrayList(Iterables.filter(EventEndHelper.getCompoundEvents(self.getISequenceEvent()), Message.class)); + if (delimitingMessages.size() == 2) { + Message callMessage = delimitingMessages.get(0); + Message returnMessage = delimitingMessages.get(1); + + Range oldRange = Range.verticalRange(bounds); + Range newRange = Range.verticalRange(newBounds); + int deltaYStart = newRange.getLowerBound() - oldRange.getLowerBound(); + int deltaYFinish = newRange.getUpperBound() - oldRange.getUpperBound(); + int deltaHStart = 0; + int deltaHFinish = 0; + + if (requestQuery.isDirectedByMessage()) { + if (requestQuery.isResizeFromTop() && callMessage.isReflective()) { + deltaHStart = deltaYStart; + deltaYStart = 0; + } + + if (requestQuery.isResizeFromBottom() && returnMessage.isReflective()) { + deltaHFinish = -deltaYFinish; + } + } + + ISequenceEvent startMessageRemote = FinalParentHelper.getFinalRemoteParent(abstractNodeEvent, delimitingMessages.get(0), deltaYStart, deltaHStart); + ISequenceEvent finishMessageRemote = FinalParentHelper.getFinalRemoteParent(abstractNodeEvent, delimitingMessages.get(1), deltaYFinish, deltaHFinish); + result = startMessageRemote == finishMessageRemote; + + if (!result && requestQuery.isResizeFromBottom() && finishMessageRemote != null) { + Range verticalRange = finishMessageRemote.getVerticalRange(); + if (newRange.getLowerBound() < verticalRange.getLowerBound()) { + Range errorRange = new Range(verticalRange.getLowerBound() - 1, newRange.getUpperBound()); + expansionZone = expansionZone == null ? errorRange : errorRange.union(expansionZone); + result = true; + } + } + + result = result && respectLowRangeMarginOfParent(startMessageRemote, callMessage, newRange) && respectUpperRangeMarginOfParent(startMessageRemote, returnMessage, newRange); + + } else if (delimitingMessages.size() == 1) { + List<EventEnd> ends = EventEndHelper.findEndsFromSemanticOrdering(self.getISequenceEvent()); + SingleEventEnd delimitedSee = getDelimitedSingleEventEnd(self, ends); + + Range oldRange = Range.verticalRange(bounds); + Range newRange = Range.verticalRange(newBounds); + int deltaYStart = newRange.getLowerBound() - oldRange.getLowerBound(); + int deltaYFinish = newRange.getUpperBound() - oldRange.getUpperBound(); + + int delimitedDeltaY = 0; + int delimitedDeltaH = 0; + if (delimitedSee != null) { + delimitedDeltaY = delimitedSee.isStart() ? deltaYStart : deltaYFinish; + + if (requestQuery.isDirectedByMessage()) { + Message msg = delimitingMessages.get(0); + if (delimitedSee.isStart() && requestQuery.isResizeFromTop() && msg.isReflective()) { + delimitedDeltaH = delimitedDeltaY; + delimitedDeltaY = 0; + } + + if (!delimitedSee.isStart() && requestQuery.isResizeFromBottom() && msg.isReflective()) { + delimitedDeltaH = -delimitedDeltaY; + } + } + } + + + + + ISequenceEvent prevMessageRemote = FinalParentHelper.getFinalRemoteParent(abstractNodeEvent, delimitingMessages.get(0), 0, 0); + ISequenceEvent delimitingMessageRemote = FinalParentHelper.getFinalRemoteParent(abstractNodeEvent, delimitingMessages.get(0), delimitedDeltaY, delimitedDeltaH); + result = prevMessageRemote == delimitingMessageRemote; + + // prevMessageRemote == delimitingMessageRemote + Message callMessage = delimitingMessages.get(0); + result = result && respectLowRangeMarginOfParent(prevMessageRemote, callMessage, newRange); + + } + return result; + } + + /** + * Check upper range margin between parentSequenceEventEditPart and + * returnMessageEditPart respect LayoutConstants.EXECUTION_CHILDREN_MARGIN + * margin + * + * @param parentSequenceEventEditPart + * @param returnMessageEditPart + * @param newRange + * + * @return + */ + private boolean respectUpperRangeMarginOfParent(ISequenceEvent parentSequenceEvent, Message returnMessage, Range newRange) { + boolean upperRangeMarginOK = true; + if (parentSequenceEvent != null) { + Range remoteParentRange = parentSequenceEvent.getVerticalRange(); + Range finishMessageRange = returnMessage.getVerticalRange(); + if (requestQuery.isDirectedByMessage() && requestQuery.isResizeFromBottom() && returnMessage.isReflective()) { + finishMessageRange = new Range(finishMessageRange.getLowerBound() + requestQuery.getLogicalDelta().height, finishMessageRange.getUpperBound()); + } + int upperRangeMargin = Math.abs(remoteParentRange.getUpperBound() - (newRange.getUpperBound() + finishMessageRange.width())); + upperRangeMarginOK = upperRangeMargin >= LayoutConstants.EXECUTION_CHILDREN_MARGIN; + } + return upperRangeMarginOK; + } + + /** + * Check if lower range margin between parentSequenceEventEditPart and + * callMessageEditPart respect LayoutConstants.EXECUTION_CHILDREN_MARGIN + * margin + * + * @param parentSequenceEventEditPart + * @param callMessageEditPart + * @param newRange + * @return + */ + private boolean respectLowRangeMarginOfParent(ISequenceEvent parentSequenceEvent, Message callMessage, Range newRange) { + boolean lowerRangeMarginOK = true; + if (parentSequenceEvent != null) { + Range remoteParentRange = parentSequenceEvent.getVerticalRange(); + Range startMessageRange = callMessage.getVerticalRange(); + + int lowerRangeMargin = Math.abs((newRange.getLowerBound() - startMessageRange.width()) - remoteParentRange.getLowerBound()); + lowerRangeMarginOK = lowerRangeMargin >= LayoutConstants.EXECUTION_CHILDREN_MARGIN; + } + return lowerRangeMarginOK; + } + + private SingleEventEnd getDelimitedSingleEventEnd(ExecutionEditPart self, List<EventEnd> ends) { + Iterable<CompoundEventEnd> thisDelimitedEnds = Iterables.filter(ends, CompoundEventEnd.class); + if (!Iterables.isEmpty(thisDelimitedEnds)) { + return EventEndHelper.getSingleEventEnd(thisDelimitedEnds.iterator().next(), self.resolveTargetSemanticElement()); + } + return null; + } + + public Range getExpansionZone() { + return expansionZone; + } + + public void setExpansionZone(Range expansionZone) { + this.expansionZone = expansionZone; + } + + /** + * Return the final hierarchical parent. + * + * @return the final hierarchical parent. + */ + public ISequenceEvent getFinalHierarchicalParent() { + return getReconnectionFinalParent(finalParent); + } + + private ISequenceEvent getReconnectionFinalParent(ISequenceEvent ise) { + if (ise instanceof Operand) { + return getReconnectionFinalParent(((Operand) ise).getCombinedFragment()); + } + + ISequenceEvent reconnectionParent = ise; + if (ise instanceof AbstractFrame) { + AbstractFrame frame = (AbstractFrame) ise; + Collection<ISequenceEvent> computeParentEvents = frame.computeParentEvents(Collections.singletonList(host.getLifeline().get())); + ISequenceEvent iSequenceEvent = Iterables.get(computeParentEvents, 0); + + if (iSequenceEvent instanceof Lifeline || iSequenceEvent instanceof AbstractNodeEvent) { + reconnectionParent = iSequenceEvent; + } else if (ise != null) { + reconnectionParent = getReconnectionFinalParent(iSequenceEvent); + } + } + return reconnectionParent; + } + + public void setFinalParent(ISequenceEvent finalParent) { + this.finalParent = finalParent; + } + + /** + * Get the validator from the request extended data or a new one. + * + * @param cbr + * the current request. + * @param host + * the host execution + * @return a validator. + */ + @SuppressWarnings("unchecked") + public static AbstractNodeEventResizeSelectionValidator getOrCreateValidator(ChangeBoundsRequest cbr, AbstractNodeEvent host) { + RequestQuery requestQuery = new RequestQuery(cbr); + AbstractNodeEventResizeSelectionValidator validator = null; + Object object = cbr.getExtendedData().get(EXECUTION_RESIZE_VALIDATOR); + if (object instanceof AbstractNodeEventResizeSelectionValidator) { + validator = (AbstractNodeEventResizeSelectionValidator) object; + if (!validator.getRequestQuery().getLogicalDelta().equals(requestQuery.getLogicalDelta())) { + validator = null; + } + } + + if (validator == null && requestQuery.isResize()) { + validator = new AbstractNodeEventResizeSelectionValidator(host, cbr); + cbr.getExtendedData().put(EXECUTION_RESIZE_VALIDATOR, validator); + } + return validator; + } + + private RequestQuery getRequestQuery() { + return requestQuery; + } + + public Collection<Integer> getInvalidPositions() { + return invalidPositions; + } + + private boolean checkGlobalPositions() { + boolean safeMove = true; + final Option<Message> startMessage; + final Option<Message> endMessage; + + if (host instanceof Execution) { + Execution exec = (Execution) host; + startMessage = exec.getStartMessage(); + endMessage = exec.getEndMessage(); + } else { + startMessage = Options.newNone(); + endMessage = Options.newNone(); + } + + Range actualRange = host.getVerticalRange(); + Rectangle newBounds = requestQuery.getLogicalTransformedRectangle(new Rectangle(0, actualRange.getLowerBound(), 0, actualRange.width())); + if (newBounds.height < 0) { + safeMove = false; + } else { + final Range finalRange = Range.verticalRange(newBounds); + Function<ISequenceEvent, Range> futureRangeFunction = new Function<ISequenceEvent, Range>() { + + public Range apply(ISequenceEvent from) { + Range verticalRange = from.getVerticalRange(); + if (host.equals(from)) { + verticalRange = finalRange; + } else if (startMessage.some() && startMessage.get().equals(from)) { + if (!startMessage.get().isReflective()) { + verticalRange = new Range(finalRange.getLowerBound(), finalRange.getLowerBound()); + } else if (requestQuery.isResizeFromTop()) { + if (requestQuery.isDirectedByMessage()) { + verticalRange = new Range(verticalRange.getLowerBound(), verticalRange.getUpperBound() + requestQuery.getLogicalDelta().y); + } else { + // reflexive and resized -> moved message + verticalRange = verticalRange.shifted(requestQuery.getLogicalDelta().y); + } + } + } else if (endMessage.some() && endMessage.get().equals(from)) { + if (!endMessage.get().isReflective()) { + verticalRange = new Range(finalRange.getUpperBound(), finalRange.getUpperBound()); + } else if (requestQuery.isResizeFromBottom()) { + if (requestQuery.isDirectedByMessage()) { + verticalRange = new Range(verticalRange.getLowerBound() + requestQuery.getLogicalDelta().height, verticalRange.getUpperBound()); + } else { // reflexive and resized -> moved message + verticalRange = verticalRange.shifted(requestQuery.getLogicalDelta().height); + } + } + } + + return verticalRange; + } + }; + invalidPositions.addAll(new PositionsChecker(host.getDiagram(), futureRangeFunction).getInvalidPositions()); + safeMove = invalidPositions.isEmpty(); + } + return safeMove; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractOperandValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractOperandValidator.java new file mode 100644 index 0000000000..cf4acedfec --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractOperandValidator.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * This class is responsible to check whether a request on an operand should be + * accepted (i.e. it would produce a well-formed diagram). While doing the + * validation, it also stores all the relevant information required to actually + * perform the interaction properly. + * + * @author smonnier + */ +public abstract class AbstractOperandValidator { + + /** + * Allow to query the request. + */ + protected final RequestQuery requestQuery; + + /** + * Current Operand. + */ + protected final Operand currentOperand; + + /** + * Sibling Operand that will have the opposite resize. + */ + protected Option<Operand> siblingOperandOption = Options.newNone(); + + private Range initialOperandRange; + + private Range finalOperandRange; + + private Range initialSiblingOperandRange; + + private Range finalSiblingOperandRange; + + private boolean valid; + + /** + * Constructor. + * + * @param operand + * the Operand which will be resized. + * @param requestQuery + * a query on the resize request targeting the execution. + */ + public AbstractOperandValidator(Operand operand, RequestQuery requestQuery) { + this.currentOperand = operand; + this.requestQuery = requestQuery; + this.valid = false; + + if (requestQuery.isResizeFromTop()) { + siblingOperandOption = operand.getPreviousOperand(); + } else if (requestQuery.isResizeFromBottom()) { + siblingOperandOption = operand.getFollowingOperand(); + } + } + + public boolean isValid() { + return valid; + } + + public Range getFinalRange() { + return finalOperandRange; + } + + /** + * Performs all the computations required to validate the resizing, and + * stores any important information which will be useful to actually execute + * the resize if it is valid, like for example avoid contact with siblings. + */ + public void validate() { + valid = checkAndComputeRanges(); + + valid = valid && checkContainedISequenceEvent(); + } + + /** + * Computes, checks and stores the initial and final range of the operand if + * the resize is performed. + */ + private boolean checkAndComputeRanges() { + boolean result = true; + // Proper range + initialOperandRange = currentOperand.getVerticalRange(); + Rectangle newBounds = getResizedBounds(new Rectangle(0, initialOperandRange.getLowerBound(), 0, initialOperandRange.width())); + + if (newBounds.height < LayoutConstants.DEFAULT_OPERAND_HEIGHT) { + result = false; + } else { + // The current operand new range is valid, we can check the sibling + // operand + if (siblingOperandOption.some()) { + Operand siblingOperand = siblingOperandOption.get(); + initialSiblingOperandRange = siblingOperand.getVerticalRange(); + Rectangle siblingOperandNewBounds = getInverseResizedBounds(new Rectangle(0, initialSiblingOperandRange.getLowerBound(), 0, initialSiblingOperandRange.width())); + if (siblingOperandNewBounds.height < LayoutConstants.DEFAULT_OPERAND_HEIGHT) { + result = false; + } else { + // The minimum heights are validated, we can save the final + // ranges + finalSiblingOperandRange = Range.verticalRange(siblingOperandNewBounds); + } + } + finalOperandRange = Range.verticalRange(newBounds); + } + return result; + } + + private boolean checkContainedISequenceEvent() { + boolean result = finalOperandRange.includes(currentOperand.getOccupiedRange().grown(LayoutConstants.EXECUTION_CHILDREN_MARGIN)); + if (siblingOperandOption.some()) { + result = result && finalSiblingOperandRange.includes(siblingOperandOption.get().getOccupiedRange().grown(LayoutConstants.EXECUTION_CHILDREN_MARGIN)); + } + return result; + } + + private Rectangle getResizedBounds(Rectangle bounds) { + return requestQuery.getLogicalTransformedRectangle(bounds); + } + + private Rectangle getInverseResizedBounds(Rectangle bounds) { + Rectangle logicalDelta = requestQuery.getLogicalDelta(); + Point moveDelta = new Point(); + Dimension sizeDelta = logicalDelta.getSize().getCopy().getNegated(); + if (requestQuery.isResizeFromBottom()) { + Dimension size = logicalDelta.getSize().getCopy(); + moveDelta = new Point(size.width, size.height); + } + return bounds.getCopy().translate(moveDelta).resize(sizeDelta); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractSequenceInteractionValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractSequenceInteractionValidator.java new file mode 100644 index 0000000000..9e68de823c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/AbstractSequenceInteractionValidator.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import com.google.common.base.Function; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Abstract validator providing common services for user interactions. + * + * @author mporhel + */ +public abstract class AbstractSequenceInteractionValidator { + + /** Keep the value of the validation. */ + protected boolean valid = true; + + /** {@link RequestQuery} for the current Request. */ + protected final RequestQuery request; + + /** The expansionZine. */ + protected Range expansionZone = Range.emptyRange(); + + /** {@link ISequenceEvent} in errors. */ + protected final Set<ISequenceEvent> eventInError = new HashSet<ISequenceEvent>(); + + /** invalid positions. */ + protected final Set<Integer> invalidPositions = new HashSet<Integer>(); + + /** invalid ranges. */ + protected final Set<Range> invalidRanges = new HashSet<Range>(); + + /** {@link ISequenceEvent}s moved. */ + protected final Collection<ISequenceEvent> movedElements = new ArrayList<ISequenceEvent>(); + + /** {@link ISequenceEvent}s moved. */ + protected final Collection<Range> createdElements = new ArrayList<Range>(); + + /** startReflexiveMessageToResize. */ + protected final Collection<Message> startReflexiveMessageToResize = new HashSet<Message>(); + + /** endReflexiveMessageToResize. */ + protected final Collection<Message> endReflexiveMessageToResize = new HashSet<Message>(); + + private boolean initialized; + + /** + * Constructor. + * + * @param request + * a request query + */ + public AbstractSequenceInteractionValidator(RequestQuery request) { + this.request = request; + } + + /** + * Get the {@link SequenceDiagram}. + * + * @return the {@link SequenceDiagram} + */ + public abstract SequenceDiagram getDiagram(); + + /** + * Get the {@link Function} which give the {@link Range} of a + * {@link ISequenceEvent}. + * + * @return the {@link Range} of a {@link ISequenceEvent} + */ + public abstract Function<ISequenceEvent, Range> getRangeFunction(); + + /** + * Do the validation of the request. + */ + protected abstract void doValidation(); + + /** + * Return the validation status. Validate the request result in the first + * call only. + * + * @return the validation status. + */ + public final boolean isValid() { + validate(); + return valid; + } + + /** + * Performs all the computations required to validate the resizing, and + * stores any important information which will be useful to actually execute + * the move if it is valid, like for example avoid contact with siblings or + * handle reconnection. + */ + public final void validate() { + if (!initialized) { + doValidation(); + initialized = true; + } + } + + public Collection<ISequenceEvent> getMovedElements() { + return movedElements; + } + + public Collection<Range> getCreatedElements() { + return createdElements; + } + + public Collection<Message> getResizedStartMessages() { + return startReflexiveMessageToResize; + } + + public Collection<Message> getResizedEndMessages() { + return endReflexiveMessageToResize; + } + + public Range getExpansionZone() { + return expansionZone; + } + + public Collection<ISequenceEvent> getEventsInError() { + return eventInError; + } + + public Collection<Integer> getInvalidPostions() { + return invalidPositions; + } + + public Collection<Range> getInvalidRanges() { + return invalidRanges; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/CombinedFragmentMoveValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/CombinedFragmentMoveValidator.java new file mode 100644 index 0000000000..62d0093aac --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/CombinedFragmentMoveValidator.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Collection; +import java.util.Collections; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicates; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.business.internal.util.EventFinder; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * This class is responsible to check whether a resize request on a Combined + * Fragment should be accepted (i.e. it would produce a well-formed diagram). + * While doing the validation, it also stores all the relevant information + * required to actually perform the resize properly. + * + * @author smonnier + */ +public class CombinedFragmentMoveValidator extends AbstractInteractionFrameValidator { + + /** + * Constructor. + * + * @param combinedFragment + * the CombinedFragment which will be moved. + * @param requestQuery + * a query on the move request targeting the CombinedFragment. + */ + public CombinedFragmentMoveValidator(CombinedFragment combinedFragment, RequestQuery requestQuery) { + super(combinedFragment, requestQuery); + Preconditions.checkArgument(requestQuery.isMove()); + defaultFrameHeight = LayoutConstants.DEFAULT_COMBINED_FRAGMENT_HEIGHT; + } + + /** + * Overridden to get final parents. + * + * {@inheritDoc} + */ + @Override + protected Collection<ISequenceEvent> getFinalParents() { + // Possibility to handle "reparent" and insertion" + Collection<ISequenceEvent> finalParents = Sets.newLinkedHashSet(); + Range insertionPoint = new Range(finalRange.getLowerBound(), finalRange.getLowerBound()); + Collection<Lifeline> coveredLifelines = frame.computeCoveredLifelines(); + for (Lifeline lifeline : coveredLifelines) { + EventFinder finder = new EventFinder(lifeline); + finder.setEventsToIgnore(Predicates.in(Collections.<ISequenceEvent> singletonList(frame))); + ISequenceEvent localParent = finder.findMostSpecificEvent(insertionPoint); + if (localParent != null) { + finalParents.add(localParent); + } + } + return finalParents; + } + + /** + * Auto-expand is allowed for moves. + * <p> + * {@inheritDoc} + */ + @Override + protected boolean canExpand() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + protected Range computeExpansionZone() { + Range expansionZone = Range.emptyRange(); + if (getRequestQuery().isMove()) { // getLogicalDelta().y > 0) { + expansionZone = finalRange; + } + return expansionZone; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/CombinedFragmentResizeValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/CombinedFragmentResizeValidator.java new file mode 100644 index 0000000000..e6af566134 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/CombinedFragmentResizeValidator.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Collection; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * This class is responsible to check whether a resize request on a Combined + * Fragment should be accepted (i.e. it would produce a well-formed diagram). + * While doing the validation, it also stores all the relevant information + * required to actually perform the resize properly. + * + * @author smonnier + */ +public class CombinedFragmentResizeValidator extends AbstractInteractionFrameValidator { + + /** + * The impacted Operand on Combined Fragment resize. + */ + private Operand impactedOperand; + + /** + * The validator of the impacted Operand. + */ + private OperandResizeValidator impactedOperandResizeValidator; + + /** + * Constructor. + * + * @param combinedFragment + * the CombinedFragment which will be resized. + * @param requestQuery + * a query on the resize request targeting the CombinedFragment. + */ + protected CombinedFragmentResizeValidator(CombinedFragment combinedFragment, RequestQuery requestQuery) { + super(combinedFragment, requestQuery); + Preconditions.checkArgument(requestQuery.isResizeFromTop() || requestQuery.isResizeFromBottom()); + defaultFrameHeight = LayoutConstants.DEFAULT_COMBINED_FRAGMENT_HEIGHT; + if (requestQuery.isResizeFromTop()) { + impactedOperand = combinedFragment.getFirstOperand(); + } else if (requestQuery.isResizeFromBottom()) { + impactedOperand = combinedFragment.getLastOperand(); + } + // A combined fragment always have at least one operand + Preconditions.checkArgument(impactedOperand != null); + impactedOperandResizeValidator = new OperandResizeValidator(impactedOperand, requestQuery); + } + + @Override + protected Collection<ISequenceEvent> getFinalParents() { + return frame.computeParentEvents(); + } + + /** + * {@inheritDoc} + * + * Overridden to trigger the impacted operand resize validation. + */ + @Override + protected void validate() { + super.validate(); + if (!(getRequestQuery().isResizeFromBottom() && getRequestQuery().getLogicalDelta().height > 0)) { + impactedOperandResizeValidator.validate(); + valid = valid && impactedOperandResizeValidator.isValid(); + } + } + + /** + * Resize do not autorize auto expand. + * + * @return false; + */ + @Override + protected boolean canExpand() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + protected Range computeExpansionZone() { + Range expansionZone = Range.emptyRange(); + if (getRequestQuery().isResizeFromBottom() && getRequestQuery().getLogicalDelta().height > 0) { + expansionZone = new Range(initialRange.getUpperBound(), finalRange.getUpperBound()); + } + return expansionZone; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/CreateMessageCreationValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/CreateMessageCreationValidator.java new file mode 100644 index 0000000000..18befe38f1 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/CreateMessageCreationValidator.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import org.eclipse.gef.requests.CreateConnectionRequest; + +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceDiagramQuery; + +/** + * Validator to check if a create message creation request is valid. + * + * @author edugueperoux + */ +public class CreateMessageCreationValidator extends DefaultMessageCreationValidator { + + /** + * {@inheritDoc} + */ + @Override + public boolean isValid(CreateConnectionRequest request) { + boolean valid = super.isValid(request); + + valid = valid && !sequenceElementSource.equals(sequenceElementTarget); + // the following is checked by a EdgeCreationDescriptionQuery in + // super.getConnectionCreateCommand() + // valid = valid && (sequenceElementTarget instanceof Lifeline || + // sequenceElementTarget instanceof InstanceRole); + valid = valid && !sequenceElementTarget.getLifeline().get().isExplicitlyCreated(); + + valid = valid && checkNotEventAtLowerTimeInSameLifeline(); + + return valid; + } + + /** + * Check that there is not {@link ISequenceEvent} on the target lifeline + * with range's lowerbound lower than the firstClickLocation.y . + * + * @return true if not {@link ISequenceEvent} lower than + * firstClickLocation.y + */ + private boolean checkNotEventAtLowerTimeInSameLifeline() { + boolean valid = true; + + SequenceDiagram sequenceDiagram = sequenceElementSource.getDiagram(); + SequenceDiagramQuery sequenceDiagramQuery = new SequenceDiagramQuery(sequenceDiagram); + for (ISequenceEvent sequenceEvent : Iterables.filter(sequenceDiagramQuery.getAllSequenceEventsLowerThan(firstClickLocation.y), Predicates.not(Predicates.instanceOf(Lifeline.class)))) { + if (isSequenceEventOfLifeline(sequenceEvent, sequenceElementTarget.getLifeline()) || isMessageTargeting(sequenceEvent, sequenceElementTarget.getLifeline()) + || isCreateMessageFor(sequenceEvent, sequenceElementTarget.getLifeline().get().getInstanceRole())) { + valid = false; + break; + } + } + return valid; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/DefaultMessageCreationValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/DefaultMessageCreationValidator.java new file mode 100644 index 0000000000..c3d6fb4fb4 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/DefaultMessageCreationValidator.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import org.eclipse.gef.requests.CreateConnectionRequest; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.EndOfLife; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceDiagramQuery; + +/** + * Default validator to check if a message creation request is valid. + * + * @author edugueperoux + */ +public class DefaultMessageCreationValidator extends AbstractMessageCreationValidator { + + /** + * {@inheritDoc} + */ + @Override + public boolean isValid(CreateConnectionRequest request) { + boolean valid = super.isValid(request); + valid = valid && validateNotCreatingMessageOnState() && validateNotCreatingMessageInInteractionUse() && validateNotCreatingMessageInDifferentOperands(); + valid = valid && checkTargetLifelineNotExplicitlyCreatedAtUpperTime() && checkTargetLifelineNotExplicitlyDestroyedAtLowerTime(); + return valid; + } + + private boolean checkTargetLifelineNotExplicitlyCreatedAtUpperTime() { + boolean valid = true; + + SequenceDiagram sequenceDiagram = sequenceElementSource.getDiagram(); + SequenceDiagramQuery sequenceDiagramQuery = new SequenceDiagramQuery(sequenceDiagram); + for (ISequenceEvent sequenceEvent : Iterables.filter(sequenceDiagramQuery.getAllSequenceEventsUpperThan(firstClickLocation.y), Predicates.not(Predicates.instanceOf(Lifeline.class)))) { + if (isCreateMessageFor(sequenceEvent, sequenceElementTarget.getLifeline().get().getInstanceRole())) { + valid = false; + break; + } + } + return valid; + } + + private boolean checkTargetLifelineNotExplicitlyDestroyedAtLowerTime() { + boolean valid = true; + + SequenceDiagram sequenceDiagram = sequenceElementSource.getDiagram(); + SequenceDiagramQuery sequenceDiagramQuery = new SequenceDiagramQuery(sequenceDiagram); + + for (ISequenceEvent sequenceEvent : Iterables.filter(sequenceDiagramQuery.getAllSequenceEventsLowerThan(firstClickLocation.y), Predicates.not(Predicates.instanceOf(Lifeline.class)))) { + if (isDestroyMessageFor(sequenceEvent, sequenceElementTarget.getLifeline().get().getInstanceRole())) { + valid = false; + break; + } + } + return valid; + } + + /** + * Check if the sequenceEvent is a {@link ISequenceEvent} of lifelineTarget. + * + * @param sequenceEvent + * the {@link ISequenceEvent} to check + * + * @param lifelineTarget + * the probable parent (direct or indirect) of sequenceEvent + * + * @return true if sequenceEvent is a {@link ISequenceEvent} of + * lifelineTarget + */ + protected boolean isSequenceEventOfLifeline(ISequenceEvent sequenceEvent, Option<Lifeline> lifelineTarget) { + Option<Lifeline> lifeline = sequenceEvent.getLifeline(); + if (sequenceEvent instanceof Message) { + lifeline = ((Message) sequenceEvent).getSourceElement().getLifeline(); + } + return lifeline.equals(lifelineTarget); + } + + /** + * Check if sequenceEvent is a message targeting (directly or indirectly) + * lifelineTarget. + * + * @param sequenceEvent + * the {@link ISequenceEvent} to check + * @param lifelineTarget + * the probable target (direct or indirect) of sequenceEvent + * + * @return true if sequenceEvent is a {@link Message} targeting (directly or + * indirectly) lifelineTarget + */ + protected boolean isMessageTargeting(ISequenceEvent sequenceEvent, Option<Lifeline> lifelineTarget) { + boolean result = false; + if (sequenceEvent instanceof Message) { + Message message = (Message) sequenceEvent; + result = message.getTargetElement().getLifeline().equals(lifelineTarget); + } + return result; + } + + /** + * Validates that a message is not created between two elements that are not + * in the same operand. + * + * @return the validation that a message is not created between two elements + * that are not in the same operand. + */ + private boolean validateNotCreatingMessageInDifferentOperands() { + boolean result = true; + + Option<Operand> sourceParentOperand = null; + if (sequenceElementSource instanceof Lifeline) { + sourceParentOperand = ((Lifeline) sequenceElementSource).getParentOperand(secondClickLocation.y); + } else if (sequenceElementSource instanceof AbstractNodeEvent) { + sourceParentOperand = ((AbstractNodeEvent) sequenceElementSource).getParentOperand(secondClickLocation.y); + } else if (sequenceElementSource instanceof ISequenceEvent) { + sourceParentOperand = ((ISequenceEvent) sequenceElementSource).getParentOperand(); + } else if (sequenceElementSource instanceof InstanceRole) { + sourceParentOperand = ((InstanceRole) sequenceElementSource).getLifeline().get().getParentOperand(secondClickLocation.y); + } + + Option<Operand> targetParentOperand = null; + if (sequenceElementTarget instanceof Lifeline) { + targetParentOperand = ((Lifeline) sequenceElementTarget).getParentOperand(secondClickLocation.y); + } else if (sequenceElementTarget instanceof AbstractNodeEvent) { + targetParentOperand = ((AbstractNodeEvent) sequenceElementTarget).getParentOperand(secondClickLocation.y); + } else if (sequenceElementTarget instanceof ISequenceEvent) { + targetParentOperand = ((ISequenceEvent) sequenceElementTarget).getParentOperand(); + } else if (sequenceElementTarget instanceof InstanceRole) { + targetParentOperand = ((InstanceRole) sequenceElementTarget).getLifeline().get().getParentOperand(secondClickLocation.y); + } + + if (targetParentOperand != null && sourceParentOperand != null) { + result = targetParentOperand.equals(sourceParentOperand); + } + return result; + } + + /** + * Validates that a message is not created inside an interaction use. + * + * @return the validation that a message is not created inside an + * interaction use. + */ + private boolean validateNotCreatingMessageOnState() { + boolean result = true; + Option<Lifeline> lifelineOption = sequenceElementTarget.getLifeline(); + + if (!lifelineOption.some()) { + result = false; + } else { + Lifeline lifeline = lifelineOption.get(); + for (State state : lifeline.getDiagram().getAllStates()) { + if (state.getLifeline().get().equals(lifeline) && state.getVerticalRange().includes(firstClickLocation.y)) { + result = false; + break; + } + } + } + return result; + } + + /** + * Validates that a message is not created inside an interaction use. + * + * @return the validation that a message is not created inside an + * interaction use. + */ + private boolean validateNotCreatingMessageInInteractionUse() { + Option<Lifeline> lifeline = sequenceElementTarget.getLifeline(); + + Predicate<InteractionUse> interactionUseOnRealTargetLocation = new Predicate<InteractionUse>() { + // Filters interaction use at the vertical position of the + // source but on the targeted lifeline. + public boolean apply(InteractionUse input) { + return input.getVerticalRange().includes(firstClickLocation.y); + } + }; + + return lifeline.some() && !Iterables.any(lifeline.get().getAllCoveringInteractionUses(), interactionUseOnRealTargetLocation); + } + + /** + * Check if sequenceEvent is a create message of createdInstanceRole. + * + * @param sequenceEvent + * the {@link ISequenceEvent} to check + * + * @param createdInstanceRole + * the probably {@link InstanceRole} created by sequenceEvent + * + * @return true if createdInstanceRole is well created by the sequenceEvent + */ + protected boolean isCreateMessageFor(ISequenceEvent sequenceEvent, InstanceRole createdInstanceRole) { + boolean result = false; + if (sequenceEvent instanceof Message) { + Message createMessage = (Message) sequenceEvent; + result = createMessage.getKind() == Message.Kind.CREATION && createMessage.getTargetElement().equals(createdInstanceRole); + } + return result; + } + + /** + * Check if sequenceEvent is a destroy message of destroyedInstanceRole. + * + * @param sequenceEvent + * the {@link ISequenceEvent} to check + * + * @param destroyedInstanceRole + * the probably {@link InstanceRole} destroyed by sequenceEvent + * + * @return true if destroyedInstanceRole is well destroyed by the + * sequenceEvent + */ + protected boolean isDestroyMessageFor(ISequenceEvent sequenceEvent, InstanceRole destroyedInstanceRole) { + boolean result = false; + if (sequenceEvent instanceof Message) { + Message createMessage = (Message) sequenceEvent; + result = createMessage.getKind() == Message.Kind.DESTRUCTION; + if (createMessage.getTargetElement() instanceof EndOfLife) { + EndOfLife endOfLife = (EndOfLife) createMessage.getTargetElement(); + result = endOfLife.getLifeline().get().getInstanceRole().equals(destroyedInstanceRole); + } else { + result = false; + } + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/DestroyMessageCreationValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/DestroyMessageCreationValidator.java new file mode 100644 index 0000000000..35b9432c53 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/DestroyMessageCreationValidator.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import org.eclipse.gef.requests.CreateConnectionRequest; + +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceDiagramQuery; + +/** + * Validator to check if a destroy message creation request is valid. + * + * @author edugueperoux + */ +public class DestroyMessageCreationValidator extends DefaultMessageCreationValidator { + + /** + * {@inheritDoc} + */ + @Override + public boolean isValid(CreateConnectionRequest request) { + boolean valid = super.isValid(request); + + valid = valid && !sequenceElementTarget.getLifeline().get().isExplicitlyDestroyed(); + valid = valid && checkNotEventAtUpperTimeInSameLifeline(); + + return valid; + } + + /** + * Check that there is not {@link ISequenceEvent} on the target lifeline + * with range's upperbound upper than the firstClickLocation.y . + * + * @return true if not {@link ISequenceEvent} upper than + * firstClickLocation.y + */ + private boolean checkNotEventAtUpperTimeInSameLifeline() { + boolean valid = true; + + SequenceDiagram sequenceDiagram = sequenceElementSource.getDiagram(); + SequenceDiagramQuery sequenceDiagramQuery = new SequenceDiagramQuery(sequenceDiagram); + + for (ISequenceEvent sequenceEvent : Iterables.filter(sequenceDiagramQuery.getAllSequenceEventsUpperThan(firstClickLocation.y), Predicates.not(Predicates.instanceOf(Lifeline.class)))) { + if (isSequenceEventOfLifeline(sequenceEvent, sequenceElementTarget.getLifeline()) || isMessageTargeting(sequenceEvent, sequenceElementTarget.getLifeline()) + || isDestroyMessageFor(sequenceEvent, sequenceElementTarget.getLifeline().get().getInstanceRole())) { + valid = false; + break; + } + } + return valid; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/FrameCreationValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/FrameCreationValidator.java new file mode 100644 index 0000000000..48bc5b3fd2 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/FrameCreationValidator.java @@ -0,0 +1,493 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; + +import com.google.common.base.Function; +import com.google.common.base.Predicates; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.description.tool.ContainerCreationDescription; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractFrame; +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.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.query.RangeComparator; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ReversedRangeComparator; +import org.eclipse.sirius.diagram.sequence.business.internal.query.SequenceDiagramQuery; +import org.eclipse.sirius.diagram.sequence.description.tool.CombinedFragmentCreationTool; +import org.eclipse.sirius.diagram.sequence.description.tool.InteractionUseCreationTool; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ISequenceEventEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.SequenceGraphicalHelper; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.CreateRequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Validates that is request for InteractionUse or CombinedFragment is allowed. + * + * @author <a href="mailto:esteban.dugueperoux@obeo.fr">Esteban Dugueperoux</a> + */ +public class FrameCreationValidator extends AbstractSequenceInteractionValidator { + + /** + * The key used in request's extended data map to identify the original + * target edit part of a request which was redirected to us. + */ + public static final String ORIGINAL_TARGET = "original.target"; + + private static final String VALIDATOR = "org.eclipse.sirius.sequence.frame.creation.validator"; + + private SequenceDiagram sequenceDiagram; + + private SequenceDiagramQuery sequenceDiagramQuery; + + private Collection<Lifeline> coverage; + + private EventEnd startingEndPredecessor; + + private EventEnd finishingEndPredecessor; + + private Multimap<Lifeline, ISequenceEvent> sequenceEventsInCreationRange; + + // Store for each lifeline, the first detected event in conflict -> + // expansion zone potential start + private Set<ISequenceEvent> eventsToShift; + + // Store for each lifeline, the event which should carry the frame to create + private Set<ISequenceEvent> localParents; + + private final ContainerCreationDescription ccdTool; + + private Rectangle creationBounds; + + /** + * Default constructor to validate InteractionUse or CombinedFragment + * creation. + * + * @param sequenceDiagram + * the {@link SequenceDiagram} on which do the creation + * @param ccdTool + * {@link ContainerCreationDescription} + * @param createRequestQuery + * {@link CreateRequestQuery} + */ + public FrameCreationValidator(SequenceDiagram sequenceDiagram, ContainerCreationDescription ccdTool, CreateRequestQuery createRequestQuery) { + super(createRequestQuery); + this.creationBounds = createRequestQuery.getLogicalDelta(); + this.sequenceDiagram = sequenceDiagram; + this.ccdTool = ccdTool; + this.sequenceDiagramQuery = new SequenceDiagramQuery(sequenceDiagram); + this.sequenceEventsInCreationRange = HashMultimap.create(); + this.eventsToShift = Sets.newTreeSet(new RangeComparator()); + this.localParents = Sets.newHashSet(); + } + + @Override + public SequenceDiagram getDiagram() { + return sequenceDiagram; + } + + @Override + public Function<ISequenceEvent, Range> getRangeFunction() { + return ISequenceEvent.VERTICAL_RANGE; + } + + /** + * Validate the creation of a InteractionUse or CombinedFragment creation. + */ + protected void doValidation() { + int firstClickY = creationBounds.y; + int secondClickY = creationBounds.bottom(); + Range creationRange = new Range(firstClickY, secondClickY); + + computeCoverage(); + + // Potential creation ? + if (!coverage.isEmpty() || creationBounds.width > 0 || creationBounds.height > 0) { + // Remove tests to always display horizontal guides for two click + // creation, (retarget show target feedback in + // ExecutionAwareNodeCreationPolicy). + createdElements.add(creationRange); + } + + valid = !coverage.isEmpty(); + + if (valid) { + valid = categorizeOverlappedEvents(creationRange); + if (valid) { + computeExpanzionZone(creationRange); + } + computeSemanticPredecessors(firstClickY); + } + } + + private void computeExpanzionZone(Range creationRange) { + if (ccdTool instanceof InteractionUseCreationTool) { + for (ISequenceEvent parent : localParents) { + Range newExpansionZone = new Range(parent.getVerticalRange().getUpperBound() - 1, creationRange.getUpperBound()); + expansionZone = expansionZone.union(newExpansionZone); + } + + SortedSet<ISequenceEvent> overlapped = Sets.newTreeSet(new RangeComparator()); + overlapped.addAll(sequenceEventsInCreationRange.values()); + for (ISequenceEvent ise : Iterables.filter(overlapped, Predicates.not(Predicates.in(localParents)))) { + if (ise.getVerticalRange().getLowerBound() >= creationRange.getLowerBound()) { + Range newExpansionZone = new Range(ise.getVerticalRange().getLowerBound() - 1, creationRange.getUpperBound()); + expansionZone = expansionZone.union(newExpansionZone); + break; + } + } + } else if (ccdTool instanceof CombinedFragmentCreationTool) { + Collection<ISequenceEvent> partialOverlaps = Lists.newArrayList(Iterables.concat(localParents, eventsToShift)); + for (ISequenceEvent parent : localParents) { + checkOtherLifelines(parent, partialOverlaps); + int expansionCut = Math.max(creationRange.getLowerBound(), parent.getVerticalRange().getUpperBound() - 1); + Range newExpansionZone = new Range(expansionCut, creationRange.getUpperBound()); + expansionZone = expansionZone.union(newExpansionZone); + } + + for (ISequenceEvent eventToShift : eventsToShift) { + checkOtherLifelines(eventToShift, partialOverlaps); + int expansionCut = Math.max(creationRange.getLowerBound(), eventToShift.getVerticalRange().getLowerBound() - 1); + Range newExpansionZone = new Range(expansionCut, creationRange.getUpperBound()); + expansionZone = expansionZone.union(newExpansionZone); + } + } + } + + private void computeSemanticPredecessors(int firstClickY) { + // Look for correct finishing end predecessor + SequenceDDiagram sequenceDDiagram = sequenceDiagram.getSequenceDDiagram(); + startingEndPredecessor = SequenceGraphicalHelper.getEndBefore(sequenceDDiagram, firstClickY - 1); + if (expansionZone != null && !expansionZone.isEmpty()) { + finishingEndPredecessor = SequenceGraphicalHelper.getEndBefore(sequenceDDiagram, expansionZone.getLowerBound()); + } else { + finishingEndPredecessor = SequenceGraphicalHelper.getEndBefore(sequenceDDiagram, creationBounds.getBottom().y); + } + } + + private void checkOtherLifelines(ISequenceEvent eventToCheck, Collection<ISequenceEvent> eventsToIgnore) { + Range rangeToCheck = eventToCheck.getVerticalRange(); + for (Lifeline lifeline : sequenceEventsInCreationRange.keySet()) { + Collection<ISequenceEvent> overlap = sequenceEventsInCreationRange.get(lifeline); + // Dot check event to shift lifeline + if (!overlap.contains(eventToCheck)) { + for (ISequenceEvent otherLifelineEvent : Iterables.filter(overlap, Predicates.not(Predicates.in(eventsToIgnore)))) { + Range otherRange = otherLifelineEvent.getVerticalRange(); + Range intersection = otherRange.intersection(rangeToCheck); + if (!intersection.equals(rangeToCheck) && !intersection.equals(otherRange) && !intersection.isEmpty()) { + valid = false; + } + } + } + } + } + + private void computeCoverage() { + coverage = Sets.newLinkedHashSet(); + + Set<Lifeline> graphicallyCoveredLifelines = sequenceDiagram.getGraphicallyCoveredLifelines(creationBounds); + Object originalTarget = request.getExtendedData().get(ORIGINAL_TARGET); + if (originalTarget instanceof ISequenceEventEditPart) { + ISequenceEventEditPart sequenceEventEditPart = (ISequenceEventEditPart) originalTarget; + Lifeline lifeline = sequenceEventEditPart.getISequenceEvent().getLifeline().get(); + request.getExtendedData().remove(ORIGINAL_TARGET); + coverage.add(lifeline); + } else { + for (Lifeline coveredLifeline : computeGraphicalCoverageFromSelectionArea(graphicallyCoveredLifelines)) { + coverage.add(coveredLifeline); + } + + // Remove covered interaction uses + for (InteractionUse iu : sequenceDiagram.getAllInteractionUses()) { + if (iu.getVerticalRange().includes(creationBounds.y)) { + Collection<Lifeline> iuLifelines = iu.computeCoveredLifelines(); + coverage.removeAll(iuLifelines); + // Do not add to events in error, sometimes lifelines cannot + // be clearly layouted and ordered + } + } + } + } + + private boolean categorizeOverlappedEvents(Range creationRange) { + Set<ISequenceEvent> checkedSequenceEvents = Sets.newHashSet(); + for (Lifeline lifeline : coverage) { + categorizeOverlappedEvents(lifeline, creationRange, checkedSequenceEvents); + } + return eventInError.isEmpty(); + } + + /** + * Checks if the specified inclusion range overlap partially + * {@link ISequenceEvent} of the specified {@link Lifeline} vertically (for + * Execution, ...) or horizontally (for {@link Message} or + * {@link AbstractFrame}). + * + * @param lifeline + * the {@link Lifeline} to check + * @param creationRange + * the request future range of the AbstractFrame to create + */ + private void categorizeOverlappedEvents(Lifeline lifeline, Range creationRange, Collection<ISequenceEvent> alreadyChecked) { + + SortedSet<ISequenceEvent> overlappedEvents = sequenceDiagramQuery.getAllSequenceEventsOnLifelineOnRange(lifeline, creationRange); + + if (!overlappedEvents.isEmpty()) { + this.sequenceEventsInCreationRange.putAll(lifeline, overlappedEvents); + + // Check vertical overlap : Check only the first event 's lower + // bound and the last event 's upper bound : other are between those + // elements and are completely graphically included + + // Look for potential parent to expand (event which will carry the + // created event on the current lifeline) + ISequenceEvent lFirstIseInRange = overlappedEvents.first(); + for (ISequenceEvent ise : overlappedEvents) { + if (ise.getVerticalRange().getLowerBound() < creationRange.getLowerBound()) { + lFirstIseInRange = ise; + } // else complete overlap + } + + // Look for potential following sibling to shift. + SortedSet<ISequenceEvent> upperBoundSorted = Sets.newTreeSet(new ReversedRangeComparator()); + upperBoundSorted.addAll(overlappedEvents); + ISequenceEvent lLastIseInRange = upperBoundSorted.last(); + + // If we have the last sequenceEvent only partially covered we mark + // it as to be shifted + if (!creationRange.includes(lFirstIseInRange.getVerticalRange().getLowerBound())) { + localParents.add(lFirstIseInRange); + + if (ccdTool instanceof InteractionUseCreationTool && lFirstIseInRange instanceof InteractionUse) { + eventInError.add(lFirstIseInRange); + } + } + if (!creationRange.includes(lLastIseInRange.getVerticalRange().getUpperBound())) { + eventsToShift.add(lLastIseInRange); + } + + if (ccdTool instanceof CombinedFragmentCreationTool) { + + categorizeCombinedFragmentOverlappedEvents(creationRange, alreadyChecked, overlappedEvents, lFirstIseInRange); + } + } else { + // Check no conflict with interaction use + } + } + + private void categorizeCombinedFragmentOverlappedEvents(Range creationRange, Collection<ISequenceEvent> alreadyChecked, SortedSet<ISequenceEvent> overlappedEvents, ISequenceEvent lFirstIseInRange) { + // Check horizontal overlap. + for (Message message : Iterables.filter(overlappedEvents, Message.class)) { + if (!alreadyChecked.contains(message)) { + alreadyChecked.add(message); + Option<Lifeline> srcLifeline = message.getSourceLifeline(); + if (srcLifeline.some() && !coverage.contains(srcLifeline.get())) { + // As we have coverage incompatibility we do + // insertion then we shift all events in the + // creationRange + + ISequenceEvent parentEvent = null; + ISequenceNode targetElement = message.getTargetElement(); + + if (targetElement instanceof ISequenceEvent) { + parentEvent = getHigherAncestorWithLowerBoundInCreationRange((ISequenceEvent) targetElement, creationRange); + } + + if (parentEvent == null) { + eventsToShift.add(lFirstIseInRange); + } else if (!creationRange.includes(parentEvent.getVerticalRange())) { + eventsToShift.add(message); + } else { + eventsToShift.add(parentEvent); + } + + // eventInError.add(message) : will cause error and + // red feedback, expansion is better + } + + Option<Lifeline> tgtLifeline = message.getTargetLifeline(); + if (tgtLifeline.some() && !coverage.contains(tgtLifeline.get())) { + // As we have coverage incompatibility we do + // insertion then we shift all events in the + // creationRange + ISequenceEvent parentEvent = null; + ISequenceNode sourceElement = message.getSourceElement(); + + if (sourceElement instanceof ISequenceEvent) { + parentEvent = getHigherAncestorWithLowerBoundInCreationRange((ISequenceEvent) sourceElement, creationRange); + } + + if (parentEvent == null) { + eventsToShift.add(lFirstIseInRange); + } else if (!creationRange.includes(parentEvent.getVerticalRange())) { + eventsToShift.add(message); + } else { + eventsToShift.add(parentEvent); + } + + // eventInError.add(message) : will cause error and + // red feedback, expansion is better + } + } + } + + for (AbstractFrame frame : Iterables.filter(overlappedEvents, AbstractFrame.class)) { + if (!alreadyChecked.contains(frame)) { + alreadyChecked.add(frame); + Collection<Lifeline> coveredLifelines = frame.computeCoveredLifelines(); + if (!coverage.containsAll(coveredLifelines) && !eventsToShift.contains(frame) && creationRange.includes(frame.getVerticalRange().getLowerBound())) { + eventsToShift.add(frame); + // eventInError.add(message) : will cause error and + // red feedback, expansion is better + } + } + } + } + + /** + * + * @param event + * @param creationRange + * @return + */ + private ISequenceEvent getHigherAncestorWithLowerBoundInCreationRange(ISequenceEvent event, Range creationRange) { + ISequenceEvent parentEvent = event == null ? null : event.getParentEvent(); + if (parentEvent != null && creationRange.includes(parentEvent.getVerticalRange().getLowerBound())) { + return getHigherAncestorWithLowerBoundInCreationRange(parentEvent, creationRange); + } else { + return event; + } + } + + private Set<Lifeline> computeGraphicalCoverageFromSelectionArea(Set<Lifeline> graphicallyCoveredLifelines) { + Set<Lifeline> validCoverage = new LinkedHashSet<Lifeline>(); + + for (Lifeline lifeline : graphicallyCoveredLifelines) { + if (coveredByDeepestOperand(lifeline, graphicallyCoveredLifelines)) { + validCoverage.add(lifeline); + } + } + return validCoverage; + } + + private boolean coveredByDeepestOperand(Lifeline lifeline, Set<Lifeline> graphicallyCoveredLifelines) { + boolean coveredByDeepestOperand = true; + Range inclusionRange = Range.verticalRange(creationBounds); + Option<Operand> findDeepestCoveringOperand = findDeepestCoveringOperand(graphicallyCoveredLifelines); + if (findDeepestCoveringOperand.some()) { + Option<Operand> parentOperand = lifeline.getParentOperand(inclusionRange); + coveredByDeepestOperand = parentOperand.some() && findDeepestCoveringOperand.get().equals(parentOperand.get()); + } + return coveredByDeepestOperand; + } + + /** + * Finds the deepest operand covering at least one of the lifelines on range + * of rect. + * + * @param graphicallyCoveredLifelines + * the covered lifelines + * @return the deepest operand covering at least one of the lifelines on + * range of rect. + */ + private Option<Operand> findDeepestCoveringOperand(Set<Lifeline> graphicallyCoveredLifelines) { + Option<Operand> deepestOperandOption = Options.newNone(); + for (Lifeline lifeline : graphicallyCoveredLifelines) { + // get the operand option for the current lifeline + Option<Operand> currentOperandOption = lifeline.getParentOperand(Range.verticalRange(creationBounds)); + if (deepestOperandOption.some() && currentOperandOption.some()) { + // save the deepest operand + Operand deepestOperand = deepestOperandOption.get(); + Operand currentOperand = currentOperandOption.get(); + + if (deepestOperand.getVerticalRange().includes(currentOperand.getVerticalRange())) { + deepestOperand = currentOperand; + } + } else if (!deepestOperandOption.some() && currentOperandOption.some()) { + // initialize the deepest operand; + deepestOperandOption = currentOperandOption; + } + } + return deepestOperandOption; + } + + public List<EObject> getCoverage() { + return Lists.newArrayList(Iterables.transform(coverage, ISequenceElement.SEMANTIC_TARGET)); + } + + public Rectangle getCreationBounds() { + return creationBounds; + } + + public EventEnd getStartingEndPredecessor() { + return startingEndPredecessor; + } + + public EventEnd getFinishingEndPredecessor() { + return finishingEndPredecessor; + } + + /** + * Get the validator from the request extended data or a new one. + * + * + * @param sequenceDiagram + * the {@link SequenceDiagram} + * @param containerCreationDescription + * {@link ContainerCreationDescription} + * + * @param createRequestQuery + * a query on the current request. + * + * @return a validator {@link FrameCreationValidator} + */ + @SuppressWarnings("unchecked") + public static FrameCreationValidator getOrCreateValidator(SequenceDiagram sequenceDiagram, ContainerCreationDescription containerCreationDescription, CreateRequestQuery createRequestQuery) { + FrameCreationValidator validator = null; + Object object = createRequestQuery.getExtendedData().get(VALIDATOR); + + if (object instanceof FrameCreationValidator) { + validator = (FrameCreationValidator) object; + if (validator.request == null || !validator.getCreationBounds().equals(createRequestQuery.getLogicalDelta())) { + validator = null; + } + } + + if (validator == null || createRequestQuery.getExtendedData().get(ORIGINAL_TARGET) != null) { + validator = new FrameCreationValidator(sequenceDiagram, containerCreationDescription, createRequestQuery); + createRequestQuery.getExtendedData().put(VALIDATOR, validator); + } + return validator; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/ISEComplexMoveValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/ISEComplexMoveValidator.java new file mode 100644 index 0000000000..053cd26ca6 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/ISEComplexMoveValidator.java @@ -0,0 +1,633 @@ +/******************************************************************************* + * Copyright (c) 2010, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.requests.ChangeBoundsRequest; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractFrame; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceNode; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.EventFinder; +import org.eclipse.sirius.diagram.sequence.business.internal.util.ISequenceElementSwitch; +import org.eclipse.sirius.diagram.sequence.business.internal.util.ParentOperandFinder; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Abstract class to validate Execution move & resize request and get from it a + * command. + * + * @author mporhel + * + */ +public class ISEComplexMoveValidator extends AbstractSequenceInteractionValidator { + + private static final String VALIDATOR = "org.eclipse.sirius.sequence.move.validator"; + + /** List of top levels events of the current move. */ + protected final Set<ISequenceEvent> topLevelElements = new HashSet<ISequenceEvent>(); + + /** List of entry points events of the current move (selection). */ + protected final Set<ISequenceEvent> otherEntryPoints = new HashSet<ISequenceEvent>(); + + /** The global moved range. */ + protected Range globalMovedRange = Range.emptyRange(); + + /** The primary selected {@link ISequenceEvent}. */ + protected ISequenceEvent primarySelected; + + private final int vMove; + + private LinkedHashSet<ISequenceNode> sequenceNodesToMove = Sets.newLinkedHashSet(); + + private Collection<Message> messagesToMove = Sets.newLinkedHashSet(); + + private Function<ISequenceEvent, Range> rangeFunction = new Function<ISequenceEvent, Range>() { + + public Range apply(ISequenceEvent from) { + Range range = from.getVerticalRange(); + if (movedElements.contains(from)) { + range = range.shifted(vMove); + } else if (startReflexiveMessageToResize.contains(from)) { + int minExecStart = Math.max(range.getLowerBound(), range.getUpperBound() + vMove); + range = new Range(range.getLowerBound(), minExecStart); + } else if (endReflexiveMessageToResize.contains(from)) { + int maxExecEnd = Math.min(range.getUpperBound(), range.getLowerBound() + vMove); + range = new Range(maxExecEnd, range.getUpperBound()); + if (expansionZone != null && !expansionZone.isEmpty() && range.includes(expansionZone.getUpperBound())) { + range = new Range(range.getLowerBound(), range.getUpperBound() + expansionZone.width()); + } + } else if (expansionZone != null && !expansionZone.isEmpty()) { + if (range.includes(expansionZone.getLowerBound())) { + range = new Range(range.getLowerBound(), range.getUpperBound() + expansionZone.width()); + } else if (range.getLowerBound() >= expansionZone.getLowerBound()) { + range = range.shifted(expansionZone.width()); + } + } + + return range; + } + }; + + /** + * Constructor. + * + * @param host + * the main execution to resize/move + * @param request + * the resize request targeting the execution. + */ + protected ISEComplexMoveValidator(ISequenceEvent host, RequestQuery request) { + super(request); + this.primarySelected = host; + Preconditions.checkArgument(request.isMove()); + this.vMove = request.getLogicalDelta().y; + } + + public Function<ISequenceEvent, Range> getRangeFunction() { + return rangeFunction; + } + + public SequenceDiagram getDiagram() { + return primarySelected.getDiagram(); + } + + public Range getMovedRange() { + return globalMovedRange; + } + + /** + * Return the nodes to move. + * + * @return a linked hash set of nodes to move. + */ + public LinkedHashSet<ISequenceNode> getSequenceNodeToMove() { + return sequenceNodesToMove; + } + + public Set<ISequenceEvent> getTopLevelElements() { + return topLevelElements; + } + + /** + * Get the message to move. + * + * @return the message to move. + */ + public Collection<Message> getMessageToMove() { + return messagesToMove; + } + + /** + * {@inheritDoc} + */ + public void addAdditionalEntryPoints(Collection<ISequenceEvent> sequenceElements) { + if (sequenceElements != null && !sequenceElements.isEmpty()) { + this.otherEntryPoints.addAll(sequenceElements); + } + } + + /** + * Overridden to do validation. + * + * {@inheritDoc} + */ + protected void doValidation() { + populateMoves(); + populateMessageToResize(); + categorizeMoves(); + + checkMoves(); + + // Avoid expansion with non complete moves + if (!startReflexiveMessageToResize.isEmpty() || !endReflexiveMessageToResize.isEmpty()) { + valid = valid && (expansionZone == null || expansionZone.isEmpty()); + } + } + + private void populateMoves() { + Set<ISequenceEvent> movedEvents = new ISequenceEventQuery(primarySelected).getAllSequenceEventToMoveWith(otherEntryPoints); + if (movedEvents != null && !movedEvents.isEmpty()) { + movedElements.addAll(movedEvents); + } + } + + private void populateMessageToResize() { + for (Execution movedExec : Iterables.filter(movedElements, Execution.class)) { + + Option<Message> startMessage = movedExec.getStartMessage(); + if (startMessage.some() && startMessage.get().surroundsEventOnSameLifeline()) { + startReflexiveMessageToResize.add(startMessage.get()); + } + + Option<Message> endMessage = movedExec.getEndMessage(); + if (endMessage.some() && endMessage.get().surroundsEventOnSameLifeline()) { + endReflexiveMessageToResize.add(endMessage.get()); + } + } + } + + private void categorizeMoves() { + MoveSwitch moveAnalyzer = new MoveSwitch(); + for (ISequenceEvent movedEvent : Lists.newArrayList(movedElements)) { + Range extendedRange = moveAnalyzer.doSwitch(movedEvent); + globalMovedRange = globalMovedRange.union(extendedRange); + } + + Set<ISequenceEvent> move = Sets.newHashSet(); + move.add(primarySelected); + move.addAll(otherEntryPoints); + Iterables.retainAll(move, sequenceNodesToMove); + Iterables.addAll(topLevelElements, move); + + Rectangle movedRange = new Rectangle(0, globalMovedRange.getLowerBound(), 0, globalMovedRange.width()); + globalMovedRange = Range.verticalRange(request.getLogicalTransformedRectangle(movedRange)); + } + + private void checkMoves() { + for (ISequenceEvent ise : topLevelElements) { + if (!moveIsValid(ise, true)) { + valid = false; + eventInError.add(ise); + // break; + } + } + + if (valid) { + Iterable<ISequenceEvent> otherMovedElements = Iterables.filter(movedElements, Predicates.not(Predicates.in(topLevelElements))); + for (ISequenceEvent ise : otherMovedElements) { + if (!(ise instanceof Operand) && !moveIsValid(ise, false)) { + valid = false; + eventInError.add(ise); + } + } + } + + for (Message resizedMsg : Iterables.concat(startReflexiveMessageToResize, endReflexiveMessageToResize)) { + boolean currentResizeIsValid = rangeFunction.apply(resizedMsg).width() >= LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP; + valid = valid && currentResizeIsValid; + if (!currentResizeIsValid) { + eventInError.add(resizedMsg); + } + } + + valid = valid && checkConflictesInFinalPositions() && checkTitleZonesInFinalPositions(); + } + + private boolean checkConflictesInFinalPositions() { + List<Integer> conflicts = Lists.newArrayList(); + conflicts.addAll(new PositionsChecker(getDiagram(), rangeFunction).getInvalidPositions()); + + if (!conflicts.isEmpty()) { + // try with global moved range... + if (!expansionZone.isEmpty() && globalMovedRange != expansionZone) { + expansionZone = globalMovedRange; + conflicts = Lists.newArrayList(); + conflicts.addAll(new PositionsChecker(getDiagram(), rangeFunction).getInvalidPositions()); + } + } + + invalidPositions.addAll(conflicts); + return conflicts.isEmpty(); + } + + private boolean checkTitleZonesInFinalPositions() { + SequenceDiagram diagram = getDiagram(); + Collection<Range> conflicts = Lists.newArrayList(); + Collection<Range> titleZones = getTitleZoneRanges(diagram); + + for (ISequenceEvent movedElement : movedElements) { + Range movedRange = rangeFunction.apply(movedElement); + if (movedElement instanceof State && movedElement.isLogicallyInstantaneous()) { + movedRange = new Range(movedRange.middleValue(), movedRange.middleValue()); + } + for (Range title : titleZones) { + if (title.includes(movedRange.getLowerBound()) || title.includes(movedRange.getUpperBound())) { + conflicts.add(title); + } + } + } + + invalidRanges.addAll(conflicts); + return conflicts.isEmpty(); + } + + private Collection<Range> getTitleZoneRanges(SequenceDiagram diagram) { + Collection<Range> titleZones = Lists.newArrayList(); + for (CombinedFragment unmovedCF : Iterables.filter(diagram.getAllCombinedFragments(), Predicates.not(Predicates.in(movedElements)))) { + int titleZoneLowerBound = rangeFunction.apply(unmovedCF).getLowerBound(); + int titleZoneUpperBound = rangeFunction.apply(unmovedCF.getFirstOperand()).getLowerBound(); + + titleZones.add(new Range(titleZoneLowerBound, titleZoneUpperBound)); + } + return titleZones; + } + + private boolean moveIsValid(ISequenceEvent ise, boolean topLevel) { + final Range futureExtRange = getFutureExtendedRange(ise); + Option<Lifeline> lifeline = ise.getLifeline(); + boolean result = true; + + if (lifeline.some()) { + EventFinder futureParentFinder = new EventFinder(lifeline.get()); + futureParentFinder.setEventsToIgnore(Predicates.equalTo(ise)); + futureParentFinder.setVerticalRangefunction(rangeFunction); + Range insertionPoint = getInsertionPoint(ise, futureExtRange); + ISequenceEvent insertionParent = futureParentFinder.findMostSpecificEvent(insertionPoint); + + if (insertionParent == null) { + if (lifeline.get().isExplicitlyCreated() || lifeline.get().isExplicitlyDestroyed()) { + return false; + } else { + insertionParent = lifeline.get(); + } + } + + // check Operand; + boolean stableOperand = checkOperandStability(ise, topLevel, insertionParent); + + // check expansion need + boolean validExpansion = stableOperand && checkExpansionNeed(ise, topLevel, insertionParent, futureExtRange, insertionPoint, Collections.singletonList(lifeline.get())); + + // Test remote expansion + boolean validRemoteExpansion = validExpansion && checkRemoteExpansion(ise, topLevel, futureExtRange, insertionPoint); + + result = stableOperand && validExpansion && validRemoteExpansion; + } else if (ise instanceof InteractionUse) { + InteractionUseMoveValidator subValidator = new InteractionUseMoveValidator((InteractionUse) ise, request); + subValidator.setMovedElements(movedElements); + result = subValidator.isValid(); + expansionZone = expansionZone.union(subValidator.getExpansionZone()); + } else if (ise instanceof CombinedFragment) { + CombinedFragmentMoveValidator subValidator = new CombinedFragmentMoveValidator((CombinedFragment) ise, request); + subValidator.setMovedElements(movedElements); + result = subValidator.isValid(); + expansionZone = expansionZone.union(subValidator.getExpansionZone()); + } else if (ise instanceof Message) { + result = messageMoveIsValid((Message) ise); + } + return result; + } + + private Range getInsertionPoint(ISequenceEvent ise, Range futureExtRange) { + int insertionY = futureExtRange.getLowerBound(); + + if (ise instanceof State && ise.isLogicallyInstantaneous()) { + insertionY = futureExtRange.middleValue(); + } + + return new Range(insertionY, insertionY); + } + + private boolean messageMoveIsValid(final Message message) { + boolean result = true; + Option<Operand> parentOperand = message.getParentOperand(); + + // If parent operand is moved too, no check for source/target validity : + // it will not change. + if (!(parentOperand.some() && movedElements.contains(parentOperand.get()))) { + + ISequenceEvent potentialSource = getMessageEnd(message, message.getSourceLifeline(), parentOperand); + ISequenceEvent potentialTarget = getMessageEnd(message, message.getTargetLifeline(), parentOperand); + + // Validate that the source and target will not overlap a frame + + if (potentialSource instanceof AbstractFrame || potentialSource instanceof State || potentialTarget instanceof AbstractFrame || potentialTarget instanceof State) { + result = false; + } else if (potentialSource != null && potentialTarget != null) { + // We filter found or lost message because for them move is + // always allowed + + Range finalRange = rangeFunction.apply(message); + int lBound = finalRange.getLowerBound(); + int uBound = finalRange.getUpperBound(); + + Operand srcOperand = null; + if (potentialSource instanceof Operand) { + srcOperand = (Operand) potentialSource; + } else if (potentialSource instanceof Execution) { + srcOperand = new ParentOperandFinder(potentialSource, rangeFunction).getParentOperand(new Range(lBound, lBound)).get(); + } + + Operand tgtOperand = null; + if (potentialTarget instanceof Operand) { + tgtOperand = (Operand) potentialTarget; + } else if (potentialTarget instanceof Execution) { + tgtOperand = new ParentOperandFinder(potentialTarget, rangeFunction).getParentOperand(new Range(uBound, uBound)).get(); + } + result = srcOperand == tgtOperand; + } + } + return result; + } + + private ISequenceEvent getMessageEnd(final Message message, Option<Lifeline> lifeline, Option<Operand> parentOperand) { + if (lifeline.some()) { + EventFinder newEndFinder = new EventFinder(lifeline.get()); + newEndFinder.setReconnection(false); + + Range lookedRange = rangeFunction.apply(message); + newEndFinder.setVerticalRangefunction(rangeFunction); + + return newEndFinder.findMostSpecificEvent(lookedRange); + } + return null; + } + + private boolean checkOperandStability(ISequenceEvent ise, boolean topLevel, ISequenceEvent insertionParent) { + Option<Operand> parentOperand = ise.getParentOperand(); + Operand futureOperand = null; + if (insertionParent instanceof Operand) { + futureOperand = (Operand) insertionParent; + } else { + futureOperand = insertionParent.getParentOperand().get(); + } + + return true; // futureOperand == parentOperand.get(); + } + + private boolean checkExpansionNeed(ISequenceEvent ise, boolean topLevel, ISequenceEvent insertionParent, Range futureExtRange, Range insertionPoint, Collection<Lifeline> lifelines) { + List<ISequenceEvent> toIgnore = Lists.newArrayList(movedElements); + toIgnore.addAll(startReflexiveMessageToResize); + toIgnore.addAll(endReflexiveMessageToResize); + + boolean canChildOccupy = movedElements.contains(insertionParent) || insertionParent.canChildOccupy(ise, futureExtRange, toIgnore, lifelines); + if (!canChildOccupy) { + if (topLevel && !expansionZone.isEmpty()) { + expansionZone = globalMovedRange; + canChildOccupy = true; + } else if (topLevel || expansionZone.isEmpty()) { + if (insertionParent.canChildOccupy(ise, insertionPoint, toIgnore, lifelines)) { + if (!(ise instanceof State && ise.isLogicallyInstantaneous())) { + expansionZone = expansionZone.union(futureExtRange); + } + canChildOccupy = true; + } + } else if (expansionZone.includes(futureExtRange)) { + canChildOccupy = true; + } + } + return canChildOccupy; + } + + private boolean checkRemoteExpansion(ISequenceEvent ise, boolean topLevel, Range futureExtRange, Range insertionPoint) { + if (ise instanceof Execution) { + Execution exec = (Execution) ise; + Option<Message> startMessage = exec.getStartMessage(); + Option<Message> endMessage = exec.getEndMessage(); + + if (startMessage.some() && endMessage.some() && !startReflexiveMessageToResize.contains(startMessage.get()) && !endReflexiveMessageToResize.contains(endMessage.get())) { + Option<Lifeline> startLifeline = startMessage.get().getSourceLifeline(); + // Should be the same... + Option<Lifeline> targetLifeline = endMessage.get().getTargetLifeline(); + + Range srcInsertionRange = rangeFunction.apply(startMessage.get()); + ISequenceEvent remoteSource = getRemoteEnd(startMessage, startLifeline, srcInsertionRange); + + // Expansion ? + Range tgtInsertionRange = rangeFunction.apply(endMessage.get()); + ISequenceEvent remoteTarget = getRemoteEnd(endMessage, targetLifeline, tgtInsertionRange); + + boolean remoteCanChildOccupy = startLifeline.some() && remoteSource != null && remoteTarget == remoteSource || !startLifeline.some(); + boolean tryExpand = topLevel || expansionZone.isEmpty(); + boolean canExpandedChildOccupy = remoteSource != null && remoteSource.canChildOccupy(ise, srcInsertionRange); + if (!remoteCanChildOccupy && tryExpand && canExpandedChildOccupy) { + expansionZone = expansionZone.union(futureExtRange); + remoteCanChildOccupy = true; + } + return remoteCanChildOccupy; + } + } + return true; + } + + private ISequenceEvent getRemoteEnd(Option<Message> message, Option<Lifeline> lifeline, Range insertionRange) { + ISequenceEvent remoteEnd = null; + if (lifeline.some()) { + EventFinder remoteSrcFinder = new EventFinder(lifeline.get()); + remoteSrcFinder.setEventsToIgnore(Predicates.equalTo((ISequenceEvent) message.get())); + remoteSrcFinder.setVerticalRangefunction(rangeFunction); + remoteSrcFinder.setExpansionZone(expansionZone); + remoteSrcFinder.setReconnection(true); + + remoteEnd = remoteSrcFinder.findMostSpecificEvent(insertionRange); + } + return remoteEnd; + } + + private Range getFutureExtendedRange(ISequenceEvent ise) { + Range futureExtRange = rangeFunction.apply(ise); + if (ise instanceof Execution) { + Execution exec = (Execution) ise; + Option<Message> startMessage = exec.getStartMessage(); + if (startMessage.some() && !startReflexiveMessageToResize.contains(startMessage.get())) { + futureExtRange = futureExtRange.union(rangeFunction.apply(startMessage.get())); + } + Option<Message> endMessage = exec.getEndMessage(); + if (endMessage.some() && !endReflexiveMessageToResize.contains(endMessage.get())) { + futureExtRange = futureExtRange.union(rangeFunction.apply(endMessage.get())); + } + } + return futureExtRange; + } + + /** + * Specific switch returning allowing to categorize and event. + * + * The doSwitch will return the extended range occupied by the event + * (including the linked messages for reflexives executions). + * + * @author mporhel + * + */ + public class MoveSwitch extends ISequenceElementSwitch<Range> { + + /** + * {@inheritDoc} + */ + @Override + public Range caseMessage(Message movedEvent) { + Predicate<Message> toMove = new Predicate<Message>() { + public boolean apply(Message input) { + boolean movedBySrc = movedElements.contains(input.getSourceElement()); + boolean movedByTgt = movedElements.contains(input.getTargetElement()); + + return !(movedBySrc && movedByTgt); + } + }; + if (toMove.apply(movedEvent)) { + if (startReflexiveMessageToResize.contains(movedEvent) || endReflexiveMessageToResize.contains(movedEvent)) { + movedElements.remove(movedEvent); + return Range.emptyRange(); + } + messagesToMove.add(movedEvent); + } else { + startReflexiveMessageToResize.remove(movedEvent); + endReflexiveMessageToResize.remove(movedEvent); + } + return movedEvent.getVerticalRange(); + } + + /** + * {@inheritDoc} + */ + @Override + public Range caseExecution(Execution movedEvent) { + ISequenceEvent hierarchicalParentEvent = movedEvent.getHierarchicalParentEvent(); + if (hierarchicalParentEvent instanceof ISequenceNode && !movedElements.contains(hierarchicalParentEvent)) { + sequenceNodesToMove.add(movedEvent); + } + + Range extendedVerticalRange = movedEvent.getExtendedVerticalRange(); + Option<Message> startMessage = movedEvent.getStartMessage(); + if (startMessage.some() && startReflexiveMessageToResize.contains(startMessage.get())) { + extendedVerticalRange = new Range(startMessage.get().getVerticalRange().getUpperBound(), extendedVerticalRange.getUpperBound()); + } + + Option<Message> endMessage = movedEvent.getEndMessage(); + if (endMessage.some() && endReflexiveMessageToResize.contains(endMessage.get())) { + extendedVerticalRange = new Range(extendedVerticalRange.getLowerBound(), endMessage.get().getVerticalRange().getLowerBound()); + } + + return extendedVerticalRange; + } + + /** + * {@inheritDoc} + */ + @Override + public Range caseState(State movedEvent) { + ISequenceEvent hierarchicalParentEvent = movedEvent.getHierarchicalParentEvent(); + if (hierarchicalParentEvent instanceof ISequenceNode && !movedElements.contains(hierarchicalParentEvent)) { + sequenceNodesToMove.add(movedEvent); + } + return movedEvent.getVerticalRange(); + } + + /** + * {@inheritDoc} + */ + @Override + public Range caseFrame(AbstractFrame movedEvent) { + sequenceNodesToMove.add(movedEvent); + return movedEvent.getVerticalRange(); + } + + /** + * {@inheritDoc} + */ + @Override + public Range caseOperand(Operand movedEvent) { + // Do nothing, operand is silently moved by its parent combined + // fragment. + return movedEvent.getVerticalRange(); + } + } + + /** + * Get the validator from the request extended data or a new one. + * + * @param cbr + * the current request + * @param requestQuery + * a query on the current request. + * @param host + * the host + * @return a validator. + */ + @SuppressWarnings("unchecked") + public static ISEComplexMoveValidator getOrCreateValidator(ChangeBoundsRequest cbr, RequestQuery requestQuery, ISequenceEvent host) { + ISEComplexMoveValidator validator = null; + Object object = cbr.getExtendedData().get(VALIDATOR); + if (object instanceof ISEComplexMoveValidator) { + validator = (ISEComplexMoveValidator) object; + if (validator.request == null || !validator.request.getLogicalDelta().equals(requestQuery.getLogicalDelta())) { + validator = null; + } + } + + if (validator == null && requestQuery.isMove()) { + Collection<ISequenceEvent> selectedIses = new RequestQuery(cbr).getISequenceEvents(); + validator = new ISEComplexMoveValidator(host, requestQuery); + validator.addAdditionalEntryPoints(selectedIses); + cbr.getExtendedData().put(VALIDATOR, validator); + } + return validator; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InstanceRoleGraphicalHorizontalOrderingComparator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InstanceRoleGraphicalHorizontalOrderingComparator.java new file mode 100644 index 0000000000..62be688a3b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InstanceRoleGraphicalHorizontalOrderingComparator.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Comparator; +import java.util.Map; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Node; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; + +/** + * Comparator to have calculated ordering in x coordinate. + * + * @author edugueperoux + */ +public class InstanceRoleGraphicalHorizontalOrderingComparator implements Comparator<InstanceRole> { + + private Map<InstanceRole, Point> moveDeltas; + + /** + * Default constructor. + */ + public InstanceRoleGraphicalHorizontalOrderingComparator() { + } + + public Map<InstanceRole, Point> getMoveDeltas() { + return moveDeltas; + } + + public void setMoveDeltas(Map<InstanceRole, Point> moveDeltas) { + this.moveDeltas = moveDeltas; + } + + /** + * {@inheritDoc} + */ + public int compare(InstanceRole o1, InstanceRole o2) { + Node o1Node = o1.getNotationNode(); + Bounds o1NodeBounds = (Bounds) o1Node.getLayoutConstraint(); + int x1 = o1NodeBounds.getX(); + if (getMoveDeltas() != null && getMoveDeltas().containsKey(o1)) { + x1 += getMoveDeltas().get(o1).x; + } + Node o2Node = o2.getNotationNode(); + Bounds o2NodeBounds = (Bounds) o2Node.getLayoutConstraint(); + int x2 = o2NodeBounds.getX(); + if (getMoveDeltas() != null && getMoveDeltas().containsKey(o2)) { + x2 += getMoveDeltas().get(o2).x; + } + return x1 - x2; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InstanceRoleMoveValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InstanceRoleMoveValidator.java new file mode 100644 index 0000000000..9f80978d49 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InstanceRoleMoveValidator.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.requests.ChangeBoundsRequest; + +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.diagram.business.internal.query.RequestQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElement; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; + +/** + * Validator for ChangeBoundsRequest of type RequestConstants#REQ_MOVE. + * + * @author edugueperoux + */ +public class InstanceRoleMoveValidator extends AbstractInstanceRoleValidator { + + /** + * {@inheritDoc} + */ + @Override + public boolean isValid(ChangeBoundsRequest request) { + boolean valid = super.isValid(request); + + if (instanceRoles.size() != 0) { + SequenceDiagram sequenceDiagram = instanceRoles.get(0).getDiagram(); + List<InstanceRole> allInstanceRoles = sequenceDiagram.getSortedInstanceRole(); + + RequestQuery query = new RequestQuery(request); + Point moveDelta = query.getLogicalDelta().getLocation(); + + Collections.sort(instanceRoles, comparator); + + // if move request is constrained then it's a explicit user move + // request + // otherwise it's a indirect request from ConnectionCreationTool for + // a + // Create Message for example + if (!request.isConstrainedMove()) { + moveDelta.y = 0; + } + + Iterable<Rectangle> notMovedIRBounds = Iterables.transform(Iterables.filter(allInstanceRoles, Predicates.not(Predicates.in(instanceRoles))), ISequenceElement.PROPER_LOGICAL_BOUNDS); + + // Iterate on all instanceRoles to move from the request + for (InstanceRole instanceRole : instanceRoles) { + moveDeltas.put(instanceRole, moveDelta.getCopy()); + + Rectangle boundBeforeResizing = instanceRole.getBounds(); + Rectangle boundAfterResizing = boundBeforeResizing.getTranslated(moveDelta); + + for (Rectangle notMovedBounds : notMovedIRBounds) { + int nmbLeftX = notMovedBounds.getLeft().x; + int nmbRightX = notMovedBounds.getRight().x; + int mLeftX = boundAfterResizing.getLeft().x; + boolean leftOverlapFixIR = nmbLeftX <= mLeftX && mLeftX <= nmbRightX; + + if (leftOverlapFixIR) { + valid = false; + break; + } + } + } + } + return valid; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InstanceRoleResizeValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InstanceRoleResizeValidator.java new file mode 100644 index 0000000000..cf83eab9f1 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InstanceRoleResizeValidator.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.requests.ChangeBoundsRequest; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.business.internal.query.RequestQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; + +/** + * Validator for ChangeBoundsRequest of type RequestConstants#REQ_RESIZE. + * + * @author edugueperoux + * + */ +public class InstanceRoleResizeValidator extends AbstractInstanceRoleValidator { + + private Map<InstanceRole, Dimension> sizeDeltas = new HashMap<InstanceRole, Dimension>(); + + /** + * {@inheritDoc} + */ + @Override + public boolean isValid(ChangeBoundsRequest request) { + boolean valid = super.isValid(request); + + if (instanceRoles.size() != 0) { + SequenceDiagram sequenceDiagram = instanceRoles.get(0).getDiagram(); + List<InstanceRole> preResizeOrder = sequenceDiagram.getSortedInstanceRole(); + + RequestQuery query = new RequestQuery(request); + Rectangle logicalDelta = query.getLogicalDelta(); + + Point moveDelta = logicalDelta.getLocation(); + moveDelta.y = 0; + Dimension sizeDelta = logicalDelta.getSize(); + + for (InstanceRole instanceRole : instanceRoles) { + moveDeltas.put(instanceRole, moveDelta.getCopy()); + sizeDeltas.put(instanceRole, sizeDelta.getCopy()); + + Rectangle boundBeforeResizing = instanceRole.getBounds(); + Rectangle boundAfterResizing = boundBeforeResizing.getTranslated(moveDelta).resize(sizeDelta); + + if (boundAfterResizing.width <= 0) { + valid = false; + } + } + + // Avoid all reorders + List<InstanceRole> postResizeOrder = getPostResizeOrder(preResizeOrder); + if (!Iterables.elementsEqual(preResizeOrder, postResizeOrder)) { + valid = false; + } + } + return valid; + } + + private List<InstanceRole> getPostResizeOrder(List<InstanceRole> allInstanceRoles) { + List<InstanceRole> result = Lists.newArrayList(allInstanceRoles); + comparator.setMoveDeltas(moveDeltas); + Collections.sort(result, comparator); + comparator.setMoveDeltas(null); + return result; + } + + public Map<InstanceRole, Dimension> getSizeDeltas() { + return sizeDeltas; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InteractionUseMoveValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InteractionUseMoveValidator.java new file mode 100644 index 0000000000..2194c3af2e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InteractionUseMoveValidator.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Collection; +import java.util.Collections; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicates; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.business.internal.util.EventFinder; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * This class is responsible to check whether a resize request on an interaction + * use should be accepted (i.e. it would produce a well-formed diagram). While + * doing the validation, it also stores all the relevant information required to + * actually perform the resize properly. + * + * @author mporhel + */ +public class InteractionUseMoveValidator extends AbstractInteractionFrameValidator { + + /** + * Constructor. + * + * @param interactionUse + * the interactionUse which will be moved. + * @param requestQuery + * a query on the move request targeting the execution. + */ + public InteractionUseMoveValidator(InteractionUse interactionUse, RequestQuery requestQuery) { + super(interactionUse, requestQuery); + Preconditions.checkArgument(requestQuery.isMove()); + defaultFrameHeight = LayoutConstants.DEFAULT_INTERACTION_USE_HEIGHT; + } + + /** + * {@inheritDoc} + */ + @Override + protected Collection<ISequenceEvent> getFinalParents() { + // Possibility to handle "reparent" and insertion" + Collection<ISequenceEvent> finalParents = Sets.newLinkedHashSet(); + Range insertionPoint = new Range(finalRange.getLowerBound(), finalRange.getLowerBound()); + Collection<Lifeline> coveredLifelines = frame.computeCoveredLifelines(); + for (Lifeline lifeline : coveredLifelines) { + EventFinder finder = new EventFinder(lifeline); + finder.setEventsToIgnore(Predicates.in(Collections.<ISequenceEvent> singletonList(frame))); + ISequenceEvent localParent = finder.findMostSpecificEvent(insertionPoint); + if (localParent != null) { + finalParents.add(localParent); + } + } + return finalParents; + // return frame.computeParentEvents(); + } + + /** + * {@inheritDoc} + * + * expand to handle insertion. + */ + @Override + protected boolean canExpand() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + protected Range computeExpansionZone() { + Range expansionZone = Range.emptyRange(); + RequestQuery requestQuery = getRequestQuery(); + if (requestQuery.isMove()) { // requestQuery.getLogicalDelta().y > 0) { + expansionZone = finalRange; + } + return expansionZone; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InteractionUseResizeValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InteractionUseResizeValidator.java new file mode 100644 index 0000000000..35c3da6fb8 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/InteractionUseResizeValidator.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Collection; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * This class is responsible to check whether a resize request on an interaction + * use should be accepted (i.e. it would produce a well-formed diagram). While + * doing the validation, it also stores all the relevant information required to + * actually perform the resize properly. + * + * @author mporhel + */ +public class InteractionUseResizeValidator extends AbstractInteractionFrameValidator { + /** + * Constructor. + * + * @param interactionUse + * the interactionUse which will be resized. + * @param requestQuery + * a query on the resize request targeting the execution. + */ + protected InteractionUseResizeValidator(InteractionUse interactionUse, RequestQuery requestQuery) { + super(interactionUse, requestQuery); + defaultFrameHeight = LayoutConstants.DEFAULT_INTERACTION_USE_HEIGHT; + } + + /** + * {@inheritDoc} + */ + @Override + protected void validate() { + if (!(getRequestQuery().isResizeFromTop() || getRequestQuery().isResizeFromBottom())) { + valid = false; + } else { + super.validate(); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Collection<ISequenceEvent> getFinalParents() { + return frame.computeParentEvents(); + } + + /** + * Resize do not authorize auto expand. + * + * @return true; + */ + @Override + protected boolean canExpand() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + protected Range computeExpansionZone() { + Range expansionZone = Range.emptyRange(); + if (getRequestQuery().isResizeFromBottom() && getRequestQuery().getLogicalDelta().height > 0) { + expansionZone = new Range(initialRange.getUpperBound(), finalRange.getUpperBound()); + } + return expansionZone; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/OperandResizeValidator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/OperandResizeValidator.java new file mode 100644 index 0000000000..4a1a5e9a0b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/OperandResizeValidator.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.util.RequestQuery; + +/** + * This class is responsible to check whether a resize request on an operand + * should be accepted (i.e. it would produce a well-formed diagram). While doing + * the validation, it also stores all the relevant information required to + * actually perform the resize properly. + * + * @author smonnier + */ +public class OperandResizeValidator extends AbstractOperandValidator { + + /** + * Constructor. + * + * @param operand + * the operand which will be resized. + * @param requestQuery + * a query on the resize request targeting the execution. + */ + public OperandResizeValidator(Operand operand, RequestQuery requestQuery) { + super(operand, requestQuery); + Preconditions.checkArgument(requestQuery.isResizeFromTop() || requestQuery.isResizeFromBottom()); + } + + // /** + // * {@inheritDoc} + // */ + // @Override + // protected Collection<? extends ISequenceEvent> getFinalParents() { + // return operand.computeParentEvents(); + // } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/PositionsChecker.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/PositionsChecker.java new file mode 100644 index 0000000000..2db331bcb3 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/edit/validator/PositionsChecker.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.validator; + +import java.util.Collection; +import java.util.Set; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multiset; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * Helper to check position of events on the diagram. + * + * @author mporhel + * + */ +public class PositionsChecker { + + private SequenceDiagram diagram; + + private Function<ISequenceEvent, Range> rangeFunction = ISequenceEvent.VERTICAL_RANGE; + + /** + * Constructor. Position will be computed using the events vertical range. + * + * @param diagram + * the diagram to check. + */ + public PositionsChecker(SequenceDiagram diagram) { + this.diagram = diagram; + } + + /** + * Constructor. Position will be computed using the provided function. + * + * @param diagram + * the diagram to check. + * @param rangeFunction + * function to compute range (future range for example). + * + */ + public PositionsChecker(SequenceDiagram diagram, Function<ISequenceEvent, Range> rangeFunction) { + this.diagram = diagram; + + if (rangeFunction != null) { + this.rangeFunction = rangeFunction; + } + } + + /** + * Inspect all sequence events and check that there is no conflicts. + * + * @return a collection of invalid positions. + */ + public Collection<Integer> getInvalidPositions() { + final Multiset<Integer> positions = HashMultiset.create(); + // Check conflicts + for (ISequenceEvent ise : diagram.getAllOrderedDelimitedSequenceEvents()) { + Range futureRange = rangeFunction.apply(ise); + int futureLowerBound = futureRange.getLowerBound(); + int futureUpperBound = futureRange.getUpperBound(); + + if (ise instanceof Execution) { + Execution exec = (Execution) ise; + if (!exec.getStartMessage().some()) { + positions.add(futureLowerBound); + } + if (!exec.getEndMessage().some()) { + positions.add(futureUpperBound); + } + } else if (ise instanceof Operand) { + positions.add(futureLowerBound); + } else if (ise instanceof Message) { + positions.add(futureLowerBound); + if (((Message) ise).isReflective()) { + positions.add(futureUpperBound); + } + } else if (ise instanceof State && ise.isLogicallyInstantaneous()) { + positions.add(futureRange.middleValue()); + } else { + positions.add(futureLowerBound); + positions.add(futureUpperBound); + } + } + + Set<Integer> invalidPositions = Sets.newHashSet(); + Iterables.addAll(invalidPositions, Iterables.filter(positions, new Predicate<Integer>() { + public boolean apply(Integer input) { + int count = positions.count(input); + return count != 1; + } + })); + + return invalidPositions; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/CombinedFragmentInvisibleResizableCompartmentFigure.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/CombinedFragmentInvisibleResizableCompartmentFigure.java new file mode 100644 index 0000000000..4d977463b9 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/CombinedFragmentInvisibleResizableCompartmentFigure.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure; + +import org.eclipse.draw2d.MarginBorder; +import org.eclipse.draw2d.ScrollPane; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.gmf.runtime.draw2d.ui.mapmode.IMapMode; + +import org.eclipse.sirius.diagram.ui.tools.api.figure.InvisibleResizableCompartmentFigure; + +public class CombinedFragmentInvisibleResizableCompartmentFigure extends InvisibleResizableCompartmentFigure { + /** + * Create a new compartment figure without borders. Overridden to remove + * border margin. + * + * @param title + * compartment title. + * @param mode + * mapping mode. + */ + public CombinedFragmentInvisibleResizableCompartmentFigure(String title, IMapMode mode) { + super(title, mode); + } + + /** + * {@inheritDoc} Overridden to remove border margin. + */ + @Override + protected void configureFigure(IMapMode mm) { + super.configureFigure(mm); + ScrollPane scrollpane = getScrollPane(); + + int mb = mm.DPtoLP(0); + scrollpane.setBorder(new MarginBorder(mb, mb, mb, mb)); + int sz = mm.DPtoLP(0); + scrollpane.setMinimumSize(new Dimension(sz, sz)); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/ExecutionItemLocator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/ExecutionItemLocator.java new file mode 100644 index 0000000000..7d646c14d9 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/ExecutionItemLocator.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure; + +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.ecore.EObject; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.sequence.description.StateMapping; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.LayoutEditPartConstants; +import org.eclipse.sirius.diagram.ui.tools.api.figure.locator.DBorderItemLocator; + +/** + * Specific DBorderItemLocator to handle border item offset and side + * computation. + * + * @author mporhel + * + */ +public class ExecutionItemLocator extends DBorderItemLocator { + private final IGraphicalEditPart owner; + + /** + * Create an {@link ExecutionItemLocator} with the specified parentFigure. + * + * @param owner + * the owner edit part. + * @param parentFigure + * the parent figure. + */ + public ExecutionItemLocator(IGraphicalEditPart owner, IFigure parentFigure) { + super(parentFigure); + this.owner = owner; + setCurrentSideOfParent(LayoutEditPartConstants.EXECUTION_SIDE); + setPreferredSideOfParent(LayoutEditPartConstants.EXECUTION_SIDE); + } + + /** + * {@inheritDoc} + */ + @Override + public void relocate(IFigure borderItem) { + super.relocate(borderItem); + if (isFigureForStateElement(borderItem)) { + centerFigureOnParent(borderItem); + } + + // Force size + borderItem.setSize(getCollapsedSize(borderItem)); + + unfix(); + } + + private void centerFigureOnParent(IFigure borderItem) { + Rectangle parentBounds = getParentFigure().getBounds(); + int parentCenter = parentBounds.getCenter().x; + Rectangle myBounds = borderItem.getBounds().getCopy(); + int x = parentCenter - (myBounds.width / 2); + borderItem.setLocation(new Point(x, myBounds.getLocation().y)); + } + + private boolean isFigureForStateElement(IFigure borderItem) { + for (IGraphicalEditPart childPart : Iterables.filter(owner.getChildren(), IGraphicalEditPart.class)) { + if (childPart.getFigure() == borderItem) { + return isStateElement(childPart); + } + } + return false; + } + + private boolean isStateElement(IGraphicalEditPart childPart) { + EObject obj = childPart.resolveSemanticElement(); + if (obj instanceof DDiagramElement) { + DDiagramElement dde = (DDiagramElement) obj; + return dde.getMapping() instanceof StateMapping; + } else { + return false; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setBorderItemOffset(Dimension borderItemOffset) { + super.setBorderItemOffset(getExecutionOffset()); + } + + private Dimension getExecutionOffset() { + Dimension offset = new Dimension(LayoutEditPartConstants.EXECUTION_BORDER_ITEM_OFFSET); + Rectangle currentBounds = getConstraint(); + IFigure parentFigure = getParentFigure(); + if (parentFigure instanceof LifelineNodeFigure) { + // we need to have the center of the execution aligned with the + // center of the lifeline + Rectangle parentBounds = parentFigure.getBounds(); + offset.width = parentBounds.width / 2 + currentBounds.width / 2; + } else if (currentBounds.width == 0) { + offset.width = 0; + } + return offset; + } + + /** + * {@inheritDoc} + */ + @Override + public void setCurrentSideOfParent(int side) { + super.setCurrentSideOfParent(LayoutEditPartConstants.EXECUTION_SIDE); + } + + /** + * {@inheritDoc} + */ + @Override + public void setPreferredSideOfParent(int side) { + super.setPreferredSideOfParent(LayoutEditPartConstants.EXECUTION_SIDE); + } + + /** + * {@inheritDoc} + */ + @Override + protected Point locateOnParent(Point suggestedLocation, int suggestedSide, IFigure borderItem) { + Point locateOnParent = super.locateOnParent(suggestedLocation, suggestedSide, borderItem); + return new Point(locateOnParent.x, suggestedLocation.y); + } + + /** + * Gets the size of the border item figure. + * + * @param borderItem + * {@link IFigure} representing a Execution + * @return the size of the border item figure. + */ + protected final Dimension getCollapsedSize(IFigure borderItem) { + Dimension size = getConstraint().getSize().getCopy(); + + // Width can be empty for collapsing, do not use size.isEmpty() + if (size.height == 0) { + Dimension preferredSize = borderItem.getPreferredSize(); + size.height = preferredSize != null ? preferredSize.height : 0; + } + + return size; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/HorizontalGuide.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/HorizontalGuide.java new file mode 100644 index 0000000000..250f54a692 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/HorizontalGuide.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure; + +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.swt.graphics.Color; + +/** + * A horizontal line across the whole diagram, useful as feedback for sequence + * diagrams where the vertical location of elements relative to each others is + * significant. + * + * @author pcdavid + */ +public class HorizontalGuide extends Figure { + private final int y; + + /** + * Creates a new horizontal guide. + * + * @param color + * the color of the line. + * @param y + * the vertical location of the guide. + */ + public HorizontalGuide(Color color, int y) { + this.y = y; + setForegroundColor(color); + super.setOpaque(true); + } + + /** + * {@inheritDoc} + */ + @Override + protected void paintFigure(Graphics graphics) { + Rectangle area = getClientArea(); + graphics.drawLine(area.x, y, area.x + area.width, y); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/LifelineNodeFigure.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/LifelineNodeFigure.java new file mode 100644 index 0000000000..6e94424495 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/LifelineNodeFigure.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.PositionConstants; + +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.ui.tools.api.figure.OneLineMarginBorder; +import org.eclipse.sirius.diagram.ui.tools.api.figure.anchor.AnchorProvider; + +/** + * This specific figure overrides {@link SequenceNodeFigure} to draw a dash line + * over. + * + * @author smonnier + * + */ +public class LifelineNodeFigure extends SequenceNodeFigure { + + /** + * Create a new {@link LifelineNodeFigure}. + * + * @param width + * the width. + * @param height + * the height. + * @param anchorProvider + * the anchor provider. + */ + public LifelineNodeFigure(int width, int height, AnchorProvider anchorProvider) { + super(width, height, anchorProvider); + + OneLineMarginBorder oneLineBorder = new OneLineMarginBorder(PositionConstants.MIDDLE); + oneLineBorder.setStyle(Graphics.LINE_CUSTOM); + oneLineBorder.setLineDash(LayoutConstants.LIFELINE_DASH_STYLE); + setBorder(oneLineBorder); + } + + /** + * This method is overridden to avoid drawing the border. + * + * {@inheritDoc} + */ + @Override + protected void paintClientArea(Graphics graphics) { + // Do nothing. This way the style edit part of the node will not be + // drawn. The lifeline is displayed using the OneLineDashborder, with + // middle position. + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/OperandFigure.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/OperandFigure.java new file mode 100644 index 0000000000..7efa0acae7 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/OperandFigure.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; + +import org.eclipse.sirius.BackgroundStyle; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IContainerLabelOffsets; +import org.eclipse.sirius.diagram.ui.tools.api.figure.GradientRoundedRectangle; +import org.eclipse.sirius.diagram.ui.tools.api.figure.OneLineMarginBorder; + +/** + * Custom figure to paint only a bottom dash line instead of a full border. + * + * @author smonnier + */ +public class OperandFigure extends GradientRoundedRectangle { + + private Operand operand; + + /** + * Create a new {@link LifelineNodeFigure}. + * + * @param dimension + * dimension of the corner (with radius, height radius) + * @param backgroundStyle + * style of the wanted gradient + * @param operand + * the current operand + */ + public OperandFigure(final Dimension dimension, final BackgroundStyle backgroundStyle, Operand operand) { + super(dimension, backgroundStyle); + + this.operand = operand; + + this.setFill(false); + this.setOutline(false); + } + + /** + * {@inheritDoc} + */ + protected void createBorder() { + OneLineMarginBorder oneLineBorder = new OneLineMarginBorder(PositionConstants.BOTTOM); + oneLineBorder.setStyle(Graphics.LINE_CUSTOM); + oneLineBorder.setLineDash(LayoutConstants.OPERAND_DASH_STYLE); + oneLineBorder.setMargin(IContainerLabelOffsets.LABEL_OFFSET - 1, 0, 0, 0); + + setBorder(oneLineBorder); + } + + /** + * {@inheritDoc} + * + * Overridden to paint only a bottom dash line instead of a full border. + */ + @Override + protected void paintBorder(Graphics graphics) { + if (!isLastOperand()) { + super.paintBorder(graphics); + } + } + + /** + * Check if it is the last operand. In that case we do not need to paint the + * operand separator. + */ + private boolean isLastOperand() { + boolean isLast = false; + if (operand != null) { + isLast = operand.isLastOperand(); + } + return isLast; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/RangeGuide.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/RangeGuide.java new file mode 100644 index 0000000000..218ab8d574 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/RangeGuide.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure; + +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.swt.graphics.Color; + +import org.eclipse.sirius.diagram.sequence.util.Range; + +/** + * A rectangle across the whole diagram, useful as feedback for sequence + * diagrams where the vertical location of elements relative to each others is + * significant. + * + * @author mporhel + */ +public class RangeGuide extends Figure { + private final Range range; + + /** + * Creates a feedback figure with two horizontal guide placed at the given + * range. + * + * @param color + * the color of the line. + * @param range + * the range of the guide.e wit + * @param fill + * fills the range with the same color, but lighter and + * transparent. + */ + public RangeGuide(Color color, Range range, boolean fill) { + this.range = range; + setForegroundColor(color); + setBackgroundColor(color); + super.setOpaque(fill); + } + + /** + * {@inheritDoc} + */ + @Override + protected void paintFigure(Graphics graphics) { + Rectangle rect = getBounds().getCopy(); + rect.y = range.getLowerBound(); + rect.height = 1; + + graphics.drawLine(rect.getTopLeft(), rect.getTopRight()); + + // if range have a null width, no need to fill it or to draw the bottom + // line. + // we have to reduce the height of the drawn rectangle to stay in the + // bounds of the figure. + if (range.width() > 1) { + rect.height = range.width() - 1; + graphics.drawLine(rect.getBottomLeft(), rect.getBottomRight()); + + if (super.isOpaque()) { + int alpha = graphics.getAlpha(); + graphics.setAlpha(25); + graphics.fillRectangle(rect); + graphics.setAlpha(alpha); + } + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/SequenceMessageLabelLocator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/SequenceMessageLabelLocator.java new file mode 100644 index 0000000000..99783c32d3 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/SequenceMessageLabelLocator.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.diagram.ui.figures.LabelLocator; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramEdgeEditPart.ViewEdgeFigure; + +/** + * Specific locator for sequence message to avoid vertical move of labels when + * lifelines are moved. (Avoid offset negation by GMF) + * + * @author mporhel + * + */ +public class SequenceMessageLabelLocator extends LabelLocator { + + LabelLocator labelLocator; + + /** + * Constructor for figure who are located and sized. + * + * @param parent + * the parent figure + * @param wrappedlocator + * default locator + */ + public SequenceMessageLabelLocator(IFigure parent, LabelLocator wrappedlocator) { + super(parent, new Rectangle(wrappedlocator.getOffset(), wrappedlocator.getSize()), wrappedlocator.getAlignment()); + Preconditions.checkNotNull(wrappedlocator); + this.labelLocator = wrappedlocator; + } + + /** + * {@inheritDoc} + */ + @Override + public void relocate(IFigure target) { + if (SequenceMessageLabelLocator.isRightToLeft((ViewEdgeFigure) target.getParent())) { + labelLocator.setOffset(getOffset().getCopy().negate()); + } + + labelLocator.relocate(target); + labelLocator.setOffset(getOffset()); + } + + /** + * Test if the given ViewEdgeFigure is from right to left. + * + * @param parent + * a ViewEdgeFigure + * @return true if the given connection figure has its first point on thr + * right of its left point. + */ + public static boolean isRightToLeft(ViewEdgeFigure parent) { + if (parent != null) { + PointList points = parent.getPoints(); + int fpX = points.getFirstPoint().x; + int lpX = points.getLastPoint().x; + + return fpX > lpX; + } + return false; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/SequenceNodeFigure.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/SequenceNodeFigure.java new file mode 100644 index 0000000000..56baa671bb --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/SequenceNodeFigure.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure; + +import org.eclipse.draw2d.ConnectionAnchor; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PrecisionPoint; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.draw2d.ui.figures.BaseSlidableAnchor; + +import org.eclipse.sirius.diagram.ui.tools.api.figure.AirDefaultSizeNodeFigure; +import org.eclipse.sirius.diagram.ui.tools.api.figure.anchor.AnchorProvider; + +/** + * This specific figure overrides {@link AirDefaultSizeNodeFigure} to create + * specific anchor. + * + * @author mporhel + * + */ +public class SequenceNodeFigure extends AirDefaultSizeNodeFigure { + + /** + * Create a new {@link SequenceNodeFigure}. + * + * @param defSize + * the size. + * @param anchorProvider + * the anchor provider. + */ + public SequenceNodeFigure(final Dimension defSize, final AnchorProvider anchorProvider) { + super(defSize, anchorProvider); + } + + /** + * Create a new {@link SequenceNodeFigure}. + * + * @param width + * the width. + * @param height + * the height. + * @param anchorProvider + * the anchor provider. + */ + public SequenceNodeFigure(final int width, final int height, final AnchorProvider anchorProvider) { + super(width, height, anchorProvider); + } + + /** + * {@inheritDoc} + */ + @Override + protected ConnectionAnchor createAnchor(PrecisionPoint p) { + return new SequenceSlidableAnchor(this, p); + } + + /** + * {@inheritDoc} + */ + @Override + protected ConnectionAnchor createDefaultAnchor() { + return new SequenceSlidableAnchor(this); + } + + /** + * {@inheritDoc} + */ + @Override + public ConnectionAnchor getConnectionAnchor(final String terminal) { + ConnectionAnchor connectAnchor = super.getConnectionAnchor(terminal); + if (connectAnchor instanceof SequenceSlidableAnchor) { + ((SequenceSlidableAnchor) connectAnchor).updateCustomStatus(terminal); + } + return connectAnchor; + } + + /** + * Returns a new anchor for this node figure. + * + * Handles sequence nodes with a 0 width and non 0 height (collapse) as if + * they have a 1 pixel width. + * + * {@inheritDoc} + */ + @Override + protected ConnectionAnchor createConnectionAnchor(Point p) { + ConnectionAnchor anchor; + + // Handle Sequence collapsed elements. + Rectangle bounds = getBounds().getCopy(); + if (p != null && bounds.width == 0 && bounds.height != 0) { + bounds.width = 1; + Point temp = p.getCopy(); + translateToRelative(temp); + PrecisionPoint pt = BaseSlidableAnchor.getAnchorRelativeLocation(temp, bounds); + if (isDefaultAnchorArea(pt)) { + anchor = getConnectionAnchor(szAnchor); + } else { + anchor = createAnchor(pt); + } + } else { + anchor = super.createConnectionAnchor(p); + } + return anchor; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/SequenceSlidableAnchor.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/SequenceSlidableAnchor.java new file mode 100644 index 0000000000..86a8805086 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/SequenceSlidableAnchor.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure; + +import org.eclipse.draw2d.ConnectionAnchor; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PrecisionPoint; +import org.eclipse.gmf.runtime.gef.ui.figures.SlidableAnchor; + +/** + * Specialized anchor with some customizations for sequence diagrams. + * + * @author mporhel + */ +public class SequenceSlidableAnchor extends SlidableAnchor { + + boolean custom; + + /** + * Constructor. + * + * @param owner + * the figure that this anchor is associated with.. + */ + public SequenceSlidableAnchor(IFigure owner) { + super(owner); + } + + /** + * Constructor. + * + * @param owner + * the figure that this anchor is associated with. + * @param pp + * the PrecisionPoint that the anchor will initially attach to. + */ + public SequenceSlidableAnchor(IFigure owner, PrecisionPoint pp) { + super(owner, pp); + } + + /** + * Constructor. + * + * @param anchor + * . + * @param pp + * the PrecisionPoint that the anchor will initially attach to. + */ + public SequenceSlidableAnchor(ConnectionAnchor anchor, PrecisionPoint pp) { + super(anchor.getOwner(), pp); + custom = true; + } + + /** + * {@inheritDoc} + */ + @Override + protected Point getLocation(Point ownReference, Point foreignReference) { + Point location = ownReference.getCopy(); + + if (!custom) { + location = super.getLocation(ownReference, foreignReference); + } + + return location; + } + + /** + * {@inheritDoc} + */ + @Override + public String getTerminal() { + String terminal = super.getTerminal(); + if (custom) { + terminal = terminal + " custom"; + } + return terminal; + } + + /** + * Parse ice information and set the corresponding location. + * + * @param terminal + * an anchor terminal id. + */ + public void updateCustomStatus(String terminal) { + if (terminal.contains("custom")) { + custom = true; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/SouthCenteredBorderItemLocator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/SouthCenteredBorderItemLocator.java new file mode 100644 index 0000000000..aae7c5a042 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/figure/SouthCenteredBorderItemLocator.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; + +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.CenteredBorderItemLocator; + +/** + * Specific locator for south centered figures. There is no possibility to + * change the side. The border item offset can be forced. + * + * @author mporhel + * + */ +public class SouthCenteredBorderItemLocator extends CenteredBorderItemLocator { + + private final Dimension forcedBorderItemOffset; + + /** + * Creates a new SouthCenteredBorderItemLocator with the specified + * parentFigure. + * + * @param parentFigure + * the parent figure + */ + public SouthCenteredBorderItemLocator(IFigure parentFigure) { + this(parentFigure, null); + } + + /** + * Creates a new SouthCenteredBorderItemLocator with the specified + * parentFigure. + * + * @param parentFigure + * the parent figure + * @param forcedBorderItemOffset + * unchangeable border item offset. + */ + public SouthCenteredBorderItemLocator(IFigure parentFigure, Dimension forcedBorderItemOffset) { + super(parentFigure); + this.forcedBorderItemOffset = forcedBorderItemOffset; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.figures.BorderItemLocator#setPreferredSideOfParent(int) + */ + @Override + public void setPreferredSideOfParent(int preferredSide) { + super.setPreferredSideOfParent(PositionConstants.SOUTH); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.figures.BorderItemLocator#setCurrentSideOfParent(int) + */ + @Override + public void setCurrentSideOfParent(int side) { + super.setCurrentSideOfParent(PositionConstants.SOUTH); + } + + /** + * {@inheritDoc} + */ + @Override + public void relocate(IFigure borderItem) { + super.relocate(borderItem); + unfix(); + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.figures.BorderItemLocator#setBorderItemOffset(org.eclipse.draw2d.geometry.Dimension) + */ + @Override + public void setBorderItemOffset(Dimension borderItemOffset) { + if (forcedBorderItemOffset != null) { + super.setBorderItemOffset(forcedBorderItemOffset); + } else { + super.setBorderItemOffset(borderItemOffset); + + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/CenteredBorderItemLocator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/CenteredBorderItemLocator.java new file mode 100644 index 0000000000..38b6084470 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/CenteredBorderItemLocator.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; + +import org.eclipse.sirius.diagram.ui.tools.api.figure.locator.DBorderItemLocator; + +/** + * Specific border item locator to keep the RootExecution always in the middle + * of the south face of an InstanceRole. + * + * @author smonnier + * + */ +public class CenteredBorderItemLocator extends DBorderItemLocator { + + /** + * Create an {@link CenteredBorderItemLocator} with the specified + * parentFigure. + * + * @param parentFigure + * the parent figure. + */ + public CenteredBorderItemLocator(final IFigure parentFigure) { + super(parentFigure); + } + + /** + * Create a {@link CenteredBorderItemLocator} with the specified item, + * parentFigure and constraint. + * + * @param borderItem + * the border item. + * @param parentFigure + * the parent figure. + * @param constraint + * the constraint. + */ + public CenteredBorderItemLocator(final IFigure borderItem, final IFigure parentFigure, final Rectangle constraint) { + super(borderItem, parentFigure, constraint); + } + + /** + * Create a {@link CenteredBorderItemLocator} with the specified item and + * preferredSide. + * + * @param parentFigure + * the parent figure. + * @param preferredSide + * the preferred side. + */ + public CenteredBorderItemLocator(final IFigure parentFigure, final int preferredSide) { + super(parentFigure, preferredSide); + } + + /** + * Get the preferred location. This method is overridden to set the position + * of the border item in the middle of the south border. + * + * @param borderItem + * the border item + * @return point + */ + @Override + protected Point getPreferredLocation(IFigure borderItem) { + Point setFigurePosition = setFigurePosition(borderItem, super.getPreferredLocation(borderItem)); + if (setFigurePosition != null) { + return setFigurePosition; + } + return super.getPreferredLocation(borderItem); + } + + /** + * Calculate the position of the middle of the south border. + * + * @param borderItem + * the border item figure + * @param borderItemPoint + * the super getPreferredLocation result that return the south + * west corner. + * @return the position where to set the root execution bordered node + */ + private Point setFigurePosition(IFigure borderItem, Point borderItemPoint) { + Rectangle parentBorder = getParentBorder(); + if (parentBorder != null) { + Rectangle executionFigureBounds = borderItem.getBounds(); + return new Point(parentBorder.x + parentBorder.width / 2 - executionFigureBounds.width / 2, borderItemPoint.y); + } + return null; + } + + /** + * This method is overridden to keep the calculated center in + * setFigurePosition method, even if the bordered node is larger than its + * parent. As instance, the Destroy End node can be larger that its parent + * lifeline. + * + * {@inheritDoc} + * + * @param suggestedLocation + * suggested location + * @param suggestedSide + * suggested side + * @param borderItem + * the border item. + * @return point the location point + */ + @Override + protected Point locateOnParent(Point suggestedLocation, int suggestedSide, IFigure borderItem) { + return new Point(suggestedLocation.x, super.locateOnParent(suggestedLocation, suggestedSide, borderItem).y); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/CombinedFragmentVerticalPositionFunction.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/CombinedFragmentVerticalPositionFunction.java new file mode 100644 index 0000000000..50e0158b02 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/CombinedFragmentVerticalPositionFunction.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout; + +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import com.google.common.base.Function; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; + +/** + * A function which computes the vertical position (in absolute, normalized + * coordinates) of an + * {@link org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.CombinedFragmentEditPart} + * . + * + * @author smonnier + */ +public class CombinedFragmentVerticalPositionFunction implements Function<IGraphicalEditPart, Integer> { + /** + * The value returned by the function to indicate an invalid input from + * which a position can not be determined. + */ + public static final int INVALID_POSITION = 0; + + /** + * Constructor. + * + */ + public CombinedFragmentVerticalPositionFunction() { + } + + /** + * Returns the vertical position of the specified InteractionUseEditPart as + * it appears on the diagram associated to this function. + * + * @param graphicalEditPart + * the CombinedFragmentEditPart for which to compute the + * position. + * @return the vertical position of the end, or + * <code>INVALID_POSITION</code>. + */ + public Integer apply(IGraphicalEditPart graphicalEditPart) { + Option<CombinedFragment> combinedFragment = ISequenceElementAccessor.getCombinedFragment(graphicalEditPart.getNotationView()); + if (combinedFragment.some()) { + return combinedFragment.get().getVerticalRange().getLowerBound(); + } else { + return INVALID_POSITION; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/InstanceUseVerticalPositionFunction.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/InstanceUseVerticalPositionFunction.java new file mode 100644 index 0000000000..db31c68365 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/InstanceUseVerticalPositionFunction.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout; + +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import com.google.common.base.Function; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; + +/** + * A function which computes the vertical position (in absolute, normalized + * coordinates) of an + * {@link org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InteractionUseEditPart} + * . + * + * @author smonnier + */ +public class InstanceUseVerticalPositionFunction implements Function<IGraphicalEditPart, Integer> { + /** + * The value returned by the function to indicate an invalid input from + * which a position can not be determined. + */ + public static final int INVALID_POSITION = 0; + + /** + * Constructor. + * + */ + public InstanceUseVerticalPositionFunction() { + } + + /** + * Returns the vertical position of the specified InteractionUseEditPart as + * it appears on the diagram associated to this function. + * + * @param graphicalEditPart + * the InteractionUseEditPart for which to compute the position. + * @return the vertical position of the end, or + * <code>INVALID_POSITION</code>. + */ + public Integer apply(IGraphicalEditPart graphicalEditPart) { + Option<InteractionUse> interactionUse = ISequenceElementAccessor.getInteractionUse(graphicalEditPart.getNotationView()); + if (interactionUse.some()) { + return interactionUse.get().getVerticalRange().getLowerBound(); + } else { + return INVALID_POSITION; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/LayoutEditPartConstants.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/LayoutEditPartConstants.java new file mode 100644 index 0000000000..4e479b6fd0 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/LayoutEditPartConstants.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout; + +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; + +/** + * A utility class to centralize all the dimensions ("magic number") used during + * the layout of sequence diagrams. Unless otherwise stated, all the dimensions + * and locations are in pixels. + * + * @author mporhel + */ +public final class LayoutEditPartConstants { + + /** + * How much an EndOfLife overlaps its parent lifeline. + */ + public static final Dimension EOL_BORDER_ITEM_OFFSET = new Dimension(0, 0); + + /** + * Which side of its parent should an EndOfLife appear on. + */ + public static final int EOL_SIDE = PositionConstants.SOUTH; + + /** + * How much an execution overlaps its parent execution. + */ + public static final Dimension EXECUTION_BORDER_ITEM_OFFSET = new Dimension(5, 0); + + /** + * Which side of its parent should an execution appear on. + */ + public static final int EXECUTION_SIDE = PositionConstants.EAST; + + /** + * How much a root execution overlaps its parent instance role. + */ + public static final Dimension ROOT_EXECUTION_BORDER_ITEM_OFFSET = new Dimension(0, 0); + + private LayoutEditPartConstants() { + // Prevents instantiation. + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/SequenceGraphicalHelper.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/SequenceGraphicalHelper.java new file mode 100644 index 0000000000..b8d492eae9 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/SequenceGraphicalHelper.java @@ -0,0 +1,219 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PrecisionPoint; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeNodeEditPart; +import org.eclipse.gmf.runtime.draw2d.ui.figures.BaseSlidableAnchor; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.IdentityAnchor; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Node; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.VerticalPositionFunction; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.ordering.EventEnd; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InstanceRoleEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.LifelineEditPart; +import org.eclipse.sirius.diagram.sequence.util.Range; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IBorderItemOffsets; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; +import org.eclipse.sirius.diagram.ui.tools.api.util.GMFNotationHelper; + +/** + * Utility class to collect helper methods which deal with GraphicalOrdering but + * which are not part of its API. + * + * @author pcdavid + */ +public final class SequenceGraphicalHelper { + private SequenceGraphicalHelper() { + // Prevent instantiation. + } + + /** + * Finds and returns the EventEnd which corresponds to the first event + * graphically above the specified Y coordinate in a diagram. + * + * @param diagram + * the diagram. + * @param y + * the Y coordinate (in logical space) + * @return the EventEnd which corresponds to the first event above Y. + */ + public static EventEnd getEndBefore(SequenceDDiagram diagram, int y) { + VerticalPositionFunction vpf = new VerticalPositionFunction(diagram); + for (EventEnd end : Lists.reverse(diagram.getGraphicalOrdering().getEventEnds())) { + int pos = vpf.apply(end); + if (pos != VerticalPositionFunction.INVALID_POSITION && pos <= y) { + return end; + } + } + return null; + } + + /** + * Finds and returns the InstanceRole semantic element which corresponds to + * the first element graphically above the specified X coordinate in a + * diagram. + * + * @param diagram + * the diagram. + * @param x + * the X coordinate (in logical space) + * @return the element which corresponds to the first InstanceRole above X. + */ + public static EObject getInstanceRoleBefore(SequenceDDiagram diagram, int x) { + Iterable<Diagram> diagramViews = Iterables.filter(ISequenceElementAccessor.getViewsForSemanticElement(diagram, diagram.getTarget()), Diagram.class); + if (!Iterables.isEmpty(diagramViews)) { + Option<SequenceDiagram> seqDiag = ISequenceElementAccessor.getSequenceDiagram(diagramViews.iterator().next()); + if (seqDiag.some()) { + for (InstanceRole ir : Lists.reverse(seqDiag.get().getSortedInstanceRole())) { + int pos = ir.getProperLogicalBounds().x; + if (pos <= x) { + return ir.getSemanticTargetElement().get(); + } + } + } + } + return null; + } + + /** + * Finds and returns the EventEnd which corresponds to the first event + * graphically below the specified Y coordinate in a diagram. + * + * @param diagram + * the diagram. + * @param y + * the Y coordinate + * @return the EventEnd which corresponds to the first event below Y. + */ + public static EventEnd getEndAfter(SequenceDDiagram diagram, int y) { + VerticalPositionFunction vpf = new VerticalPositionFunction(diagram); + for (EventEnd end : diagram.getGraphicalOrdering().getEventEnds()) { + int pos = vpf.apply(end); + if (pos != VerticalPositionFunction.INVALID_POSITION && pos > y) { + return end; + } + } + return null; + } + + /** + * Returns the normalized Y location of the Center of an edit part, using + * only information from the GMF model (not Draw2D figures). + * + * @param part + * the edit part. + * @return the absolute, normalized Y location of the Center of the + * DNodeEditPart. + */ + public static int getAbsoluteYCenterFromGMFView(IGraphicalEditPart part) { + if (part instanceof ShapeNodeEditPart) { + Rectangle absoluteboundsRectangle = SequenceGraphicalHelper.getAbsoluteBoundsFromGMFView(part); + return absoluteboundsRectangle.getCenter().y; + } + // FIXME This uses Draw2D info + Point location = part.getFigure().getBounds().getCenter().getCopy(); + GraphicalHelper.screen2logical(location, part); + return location.y; + } + + /** + * Returns the normalized Y location of the Center of an edit part, using + * only information from the GMF model (not Draw2D figures). + * + * Use information from the GMF Notation model if available (which is most + * of the time), and fall back to the Draw2D Figure's bounds otherwise. The + * figure's bounds is always available, but depending on the time of the + * call it may be out of date (pending a revalidate/repaint). + * + * @param part + * the edit part + * + * @return the absolute normalized bounds of the ISequenceEvent edit part + */ + public static Rectangle getAbsoluteBoundsFromGMFView(IGraphicalEditPart part) { + Rectangle rect; + + /* + * Initialize with Draw2D information. May be out of date under some + * conditions. + */ + rect = part.getFigure().getBounds().getCopy(); + if (part instanceof LifelineEditPart) { + SequenceGraphicalHelper.handleLifelineEditPartOffset(rect); + } + + /* + * Handle Node case with Notation information + */ + if (part.getNotationView() instanceof Node) { + Node node = (Node) part.getNotationView(); + Point absLoc = GMFNotationHelper.getAbsoluteLocation(node); + + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Bounds) { + Bounds bounds = (Bounds) layoutConstraint; + rect.setLocation(absLoc.x, absLoc.y); + + // prevent auto size + if (bounds.getWidth() != -1) { + rect.width = bounds.getWidth(); + } + + // prevent auto size + if (bounds.getHeight() != -1) { + rect.height = bounds.getHeight(); + } + + if (!(part instanceof InstanceRoleEditPart)) { + SequenceGraphicalHelper.handleLifelineEditPartOffset(rect); + } + } + } + + return rect; + } + + private static void handleLifelineEditPartOffset(Rectangle rect) { + // FIXME LifelineEditPart are placed with offset which did + // not appear in GMF model + rect.translate(0, IBorderItemOffsets.DEFAULT_OFFSET.height); + } + + /** + * Return the absolute anchor position for the given range and anchor. + * + * @param anchor + * the current anchor. + * @param range + * range of the ISequenceEvent which supports the anchor. + * @return the absolute anchor position. + */ + public static int getAnchorAbsolutePosition(IdentityAnchor anchor, Range range) { + PrecisionPoint rel = anchor != null ? BaseSlidableAnchor.parseTerminalString(anchor.getId()) : new PrecisionPoint(0.5, 0.5); + return range.getLowerBound() + (int) Math.round(rel.preciseY * range.width()); + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/SequenceLayoutProvider.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/SequenceLayoutProvider.java new file mode 100644 index 0000000000..e4680788ad --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/SequenceLayoutProvider.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.commands.Command; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; +import org.eclipse.sirius.diagram.sequence.SequenceDDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.RefreshGraphicalOrderingOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.RefreshSemanticOrderingsOperation; +import org.eclipse.sirius.diagram.sequence.business.internal.operation.SynchronizeGraphicalOrderingOperation; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceDiagramEditPart; +import org.eclipse.sirius.diagram.tools.api.command.DoNothingCommand; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.AbstractLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.api.util.EditPartTools; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; + +/** + * The layout provider for sequence diagrams. + * + * @author ymortier, mporhel + */ +public class SequenceLayoutProvider extends AbstractLayoutProvider { + /** + * {@inheritDoc} + */ + @Override + public Command layoutEditParts(@SuppressWarnings("rawtypes") List selectedObjects, IAdaptable layoutHint) { + SequenceDiagramEditPart sdep = getParentSequenceDiagramEditPart(selectedObjects); + boolean isArrangeAll = isArrangeAll(sdep, selectedObjects); + if (sdep != null && isArrangeAll) { + return createArrangeAllCommand(sdep); + } else { + return DoNothingCommand.INSTANCE; + } + } + + private boolean isArrangeAll(SequenceDiagramEditPart sdep, List selectedObjects) { + boolean result = false; + if (sdep != null && selectedObjects != null) { + result = sdep.getChildren().size() == selectedObjects.size() && sdep.getChildren().containsAll(selectedObjects); + } + return result; + } + + private Command createArrangeAllCommand(SequenceDiagramEditPart sdep) { + TransactionalEditingDomain transactionalEditingDomain = sdep.getEditingDomain(); + SequenceDiagram sequenceDiagram = sdep.getSequenceDiagram(); + SequenceDDiagram sequenceDDiagram = (SequenceDDiagram) sdep.resolveSemanticElement(); + Collection<AbstractModelChangeOperation<Void>> operations = new ArrayList<AbstractModelChangeOperation<Void>>(); + + operations.add(new RefreshGraphicalOrderingOperation(sequenceDiagram)); + operations.add(new RefreshSemanticOrderingsOperation(sequenceDDiagram)); + operations.add(new SynchronizeGraphicalOrderingOperation(sdep.getDiagramView(), true)); + + ICommand cmd = CommandFactory.createICommand(transactionalEditingDomain, operations); + cmd.setLabel("Arrange all"); + + return new ICommandProxy(cmd); + } + + private SequenceDiagramEditPart getParentSequenceDiagramEditPart(List<?> selectedObjects) { + if (!selectedObjects.isEmpty()) { + return EditPartTools.getParentOfType((EditPart) selectedObjects.iterator().next(), SequenceDiagramEditPart.class); + } else { + return null; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/SequenceMessagesRouter.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/SequenceMessagesRouter.java new file mode 100644 index 0000000000..569c951b07 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/SequenceMessagesRouter.java @@ -0,0 +1,436 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.draw2d.AbsoluteBendpoint; +import org.eclipse.draw2d.AbstractRouter; +import org.eclipse.draw2d.Bendpoint; +import org.eclipse.draw2d.Connection; +import org.eclipse.draw2d.ConnectionAnchor; +import org.eclipse.draw2d.ConnectionRouter; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.PrecisionPoint; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramEdgeEditPart; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.layout.LayoutConstants; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.EndOfLifeEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InstanceRoleEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceMessageEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.figure.LifelineNodeFigure; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; + +/** + * A specific router for the messages on a sequence diagram. + * + * @author pcdavid + */ +public class SequenceMessagesRouter extends AbstractRouter implements ConnectionRouter { + + /** + * A point, reused for computations to avoid creating many instances. + */ + private static final PrecisionPoint A_POINT = new PrecisionPoint(); + + /** + * The constraints associated to each connection to route. + */ + private Map<Connection, Object> constraints = new WeakHashMap<Connection, Object>(); + + /** + * {@inheritDoc} + */ + @Override + public void setConstraint(Connection connection, Object constraint) { + this.constraints.put(connection, constraint); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getConstraint(Connection connection) { + return this.constraints.get(connection); + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(Connection connection) { + this.constraints.remove(connection); + } + + /** + * {@inheritDoc} + */ + public void route(Connection conn) { + if (!isValidConnection(conn)) { + return; + } + + List<Bendpoint> bendpoints = getRefreshedConstraint(conn); + + IGraphicalEditPart part = null; + if (conn instanceof AbstractDiagramEdgeEditPart.ViewEdgeFigure) { + part = ((AbstractDiagramEdgeEditPart.ViewEdgeFigure) conn).getEditPart(); + } + + Point sourceRef = getReferencePoint(conn, true, bendpoints); + Point targetRef = getReferencePoint(conn, false, bendpoints); + + boolean leftToRight = conn.getSourceAnchor().getReferencePoint().x < conn.getTargetAnchor().getReferencePoint().x; + boolean msgToSelf = sourceRef.x == targetRef.x && sourceRef.y != targetRef.y || bendpoints.size() >= 4; + msgToSelf = msgToSelf || isReflectiveMessage(part); + + Rectangle sourceOwnerBounds = getAnchorOwnerBounds(conn.getSourceAnchor()); + Rectangle targetOwnerBounds = getAnchorOwnerBounds(conn.getTargetAnchor()); + + sourceRef.x = getRefX(true, leftToRight, msgToSelf, sourceOwnerBounds, conn.getSourceAnchor().getOwner()); + targetRef.x = getRefX(false, leftToRight, msgToSelf, targetOwnerBounds, conn.getTargetAnchor().getOwner()); + + PointList points = new PointList(Math.max(2, bendpoints.size())); + + if (msgToSelf) { + // We are working on a message to self + // We align the 2 extra bendpoint with the relative source and + // target + Point secondPoint = sourceRef.getCopy(); + Point thirdPoint = targetRef.getCopy(); + double zoom = part == null ? 1.0 : GraphicalHelper.getZoom(part); + + int hGap = LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_HORIZONTAL_GAP; + if (part instanceof SequenceMessageEditPart) { + Message msg = (Message) ((SequenceMessageEditPart) part).getISequenceEvent(); + if (msg.isReflective()) { + hGap = msg.getReflexiveMessageWidth(); + } + } + + secondPoint.x = Math.max(sourceRef.x, targetRef.x) + (int) (hGap * zoom); + thirdPoint.x = secondPoint.x; + + conn.translateToRelative(sourceRef); + conn.translateToRelative(secondPoint); + conn.translateToRelative(thirdPoint); + + points.addPoint(sourceRef); + points.addPoint(secondPoint); + points.addPoint(thirdPoint); + } else { + conn.translateToRelative(sourceRef); + points.addPoint(sourceRef); + } + conn.translateToRelative(targetRef); + points.addPoint(targetRef); + conn.setPoints(points); + + // This specific update have no use anymore and there was a problem with + // reflexive sync call and scroll + // updateEdgeBendpointOnMessageToSelfCreation(part, conn); + } + + private int getRefX(boolean source, boolean leftToRight, boolean msgToSelf, Rectangle anchorOwnerBounds, IFigure anchorOwner) { + int refX; + boolean onLeft = (source && !leftToRight) || (leftToRight && !source); + onLeft = onLeft && !msgToSelf; + + if (msgToSelf) { // bendpoints.size() >= 4 || + // Source and Target of message to self are on the right + refX = anchorOwnerBounds.getRight().x; + } else { + refX = onLeft ? anchorOwnerBounds.getLeft().x : anchorOwnerBounds.getRight().x; + } + + // Corrects figure for messages linked to lifelines (gap between line + // and real figure) + if (anchorOwner instanceof LifelineNodeFigure) { + LifelineNodeFigure lnf = (LifelineNodeFigure) anchorOwner; + int refMidWidth = (anchorOwnerBounds.width - lnf.getLineWidth()) / 2; + refX += onLeft ? refMidWidth : -refMidWidth; + } + return refX; + } + + private Point getReferencePoint(Connection conn, boolean source, List<Bendpoint> bendpoints) { + Point ref; + if (bendpoints.isEmpty()) { + ConnectionAnchor anchor = source ? conn.getSourceAnchor() : conn.getTargetAnchor(); + ref = anchor.getReferencePoint().getCopy(); + } else { + int index = source ? 0 : bendpoints.size() - 1; + ref = new Point((bendpoints.get(index)).getLocation()); + conn.translateToAbsolute(ref); + } + return ref; + } + + private Rectangle getAnchorOwnerBounds(ConnectionAnchor anchor) { + Rectangle ownerBounds = anchor.getOwner().getBounds().getCopy(); + anchor.getOwner().getParent().translateToAbsolute(ownerBounds); + return ownerBounds; + } + + private boolean isReflectiveMessage(IGraphicalEditPart part) { + if (part instanceof SequenceMessageEditPart) { + ISequenceEvent ise = ((SequenceMessageEditPart) part).getISequenceEvent(); + return ise instanceof Message && ((Message) ise).isReflective(); + } + return false; + } + + private List<Bendpoint> getRefreshedConstraint(Connection conn) { + boolean noBendpointsAtbeginning = false; + @SuppressWarnings("unchecked") + List<Bendpoint> bendpoints = (List<Bendpoint>) getConstraint(conn); + if (bendpoints == null) { + noBendpointsAtbeginning = true; + bendpoints = Collections.emptyList(); + } + refreshBendpoints(bendpoints, conn); + + if (!(noBendpointsAtbeginning && Collections.emptyList().equals(bendpoints))) { + // There is no need to set constraint if there is no bendpoints at + // the beginning of this method and that is finally empty. + setConstraint(conn, bendpoints); + } + return bendpoints; + } + + private boolean isValidConnection(Connection conn) { + return isValidAnchor(conn.getSourceAnchor()) && isValidAnchor(conn.getTargetAnchor()); + } + + private boolean isValidAnchor(ConnectionAnchor anchor) { + return anchor != null && anchor.getOwner() != null; + } + + private void refreshBendpoints(List<Bendpoint> bendpoints, Connection conn) { + + IGraphicalEditPart part = null; + if (conn instanceof AbstractDiagramEdgeEditPart.ViewEdgeFigure) { + part = ((AbstractDiagramEdgeEditPart.ViewEdgeFigure) conn).getEditPart(); + } + if (bendpoints.size() > 2) { + if (isReflectiveMessage(part)) { + if (bendpoints.size() > 4) { + // The user want to resize a message to self by pulling an + // edge (between bendpoints) + // This has the effect to add a fifth bendpoint + align5BendpointsOfMessageToSelf(bendpoints, conn); + } else if (bendpoints.size() == 4) { + // The user want to resize a message to self by pulling a + // bendpoint + align4BendpointsOfMessageToSelf(bendpoints, conn); + } + } else { + /* + * The only case where we have more than two bendpoints is when + * the user has dragged a connection, which created a temporary + * intermediate bendpoint. We use that new point as the + * reference Y coordinate for the start and end points, but + * remove the intermediate bendpoint itself. + */ + Bendpoint start = bendpoints.get(0); + Bendpoint end = bendpoints.get(bendpoints.size() - 1); + Bendpoint moveRef = bendpoints.get(1); + bendpoints.clear(); + + A_POINT.setLocation(start.getLocation()); + A_POINT.y = moveRef.getLocation().y; + Bendpoint newStart = new AbsoluteBendpoint(A_POINT); + bendpoints.add(newStart); + /* + * I don't understand exactly why, but the intermediate point + * must be kept here, otherwise the edges can only be moved of a + * very small vertical distance at a time. + */ + bendpoints.add(moveRef); + A_POINT.setLocation(end.getLocation()); + A_POINT.y = moveRef.getLocation().y; + Bendpoint newEnd = new AbsoluteBendpoint(A_POINT); + bendpoints.add(newEnd); + } + } else if (bendpoints.size() == 2) { + Bendpoint start = bendpoints.get(0); + Bendpoint end = bendpoints.get(bendpoints.size() - 1); + + bendpoints.clear(); + Bendpoint newStart = new AbsoluteBendpoint(start.getLocation()); + Bendpoint newEnd = new AbsoluteBendpoint(end.getLocation()); + + if (isReflectiveMessage(part)) { + bendpoints.addAll(createMessageToSelf(start, end, (SequenceMessageEditPart) part)); + } else { + if (part instanceof SequenceMessageEditPart + && (((SequenceMessageEditPart) part).getTarget() instanceof InstanceRoleEditPart || ((SequenceMessageEditPart) part).getTarget() instanceof EndOfLifeEditPart)) { + IGraphicalEditPart target = (IGraphicalEditPart) ((SequenceMessageEditPart) part).getTarget(); + Rectangle targetBounds = target.getFigure().getBounds(); + int yCenterPosition = targetBounds.y + targetBounds.height / 2; + newStart.getLocation().y = yCenterPosition; + } + + bendpoints.add(newStart); + // Ensure the message is horizontal. + newEnd.getLocation().y = newStart.getLocation().y; + bendpoints.add(newEnd); + } + } + } + + /** + * Having a connection with 5 bendpoints means that we are working on a + * message to self. We will compare the positions of the bendpoints with the + * previous ones. This way, we will know if the user has pulled a bendpoint + * and will move the other closest bendpoints to keep the layout. + * + * @param bendpoints + * the list of bendpoints on a connection + * @param conn + * the message to self connection + */ + private void align4BendpointsOfMessageToSelf(final List<Bendpoint> bendpoints, Connection conn) { + + Bendpoint newStart = new AbsoluteBendpoint(bendpoints.get(0).getLocation()); + Bendpoint newSecondPoint = new AbsoluteBendpoint(bendpoints.get(1).getLocation()); + Bendpoint newThirdPoint = new AbsoluteBendpoint(bendpoints.get(2).getLocation()); + Bendpoint newEnd = new AbsoluteBendpoint(bendpoints.get(3).getLocation()); + bendpoints.clear(); + + Bendpoint oldSecondPoint; + Bendpoint oldThirdPoint; + if (conn.getPoints() != null && conn.getPoints().size() == 4) { + oldSecondPoint = new AbsoluteBendpoint(conn.getPoints().getPoint(1)); + oldThirdPoint = new AbsoluteBendpoint(conn.getPoints().getPoint(2)); + } else { + oldSecondPoint = newSecondPoint; + oldThirdPoint = newThirdPoint; + } + + if (oldSecondPoint.getLocation().y != newSecondPoint.getLocation().y) { + // Second point Y position has changed. We need to update the start + // point. + if (newSecondPoint.getLocation().y > newThirdPoint.getLocation().y - LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP) { + // limit vertical move to keep the minimum distance + newSecondPoint.getLocation().y = newThirdPoint.getLocation().y - LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP; + } + newStart.getLocation().y = newSecondPoint.getLocation().y; + } + + if (oldThirdPoint.getLocation().y != newThirdPoint.getLocation().y) { + // Second point Y position has changed. We need to update the start + // point. + if (newThirdPoint.getLocation().y < newSecondPoint.getLocation().y + LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP) { + // limit vertical move to keep the minimum distance + newThirdPoint.getLocation().y = newSecondPoint.getLocation().y + LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP; + } + newEnd.getLocation().y = newThirdPoint.getLocation().y; + } + + bendpoints.add(newStart); + bendpoints.add(newSecondPoint); + bendpoints.add(newThirdPoint); + bendpoints.add(newEnd); + } + + /** + * Having a connection with 5 bendpoints means that we are working on a + * message to self that the user is resizing by pulling an edge between 2 + * bendpoints. In that case we will align the bendpoints just before and + * after to keep the same layout. + * + * @param bendpoints + * the list of bendpoints on a connection + * @param conn + * the message to self connection + */ + private void align5BendpointsOfMessageToSelf(final List<Bendpoint> bendpoints, Connection conn) { + + Bendpoint newStart = new AbsoluteBendpoint(bendpoints.get(0).getLocation()); + Bendpoint secondPoint = new AbsoluteBendpoint(bendpoints.get(1).getLocation()); + Bendpoint thirdPoint = new AbsoluteBendpoint(bendpoints.get(2).getLocation()); + Bendpoint fourthPoint = new AbsoluteBendpoint(bendpoints.get(3).getLocation()); + Bendpoint newEnd = new AbsoluteBendpoint(bendpoints.get(4).getLocation()); + bendpoints.clear(); + + Bendpoint oldSecondPoint = new AbsoluteBendpoint(conn.getPoints().getPoint(1)); + Bendpoint oldThirdPoint = new AbsoluteBendpoint(conn.getPoints().getPoint(2)); + + if (oldSecondPoint.getLocation().y != secondPoint.getLocation().y) { + // The new bendpoint is between old start and old second points + if (secondPoint.getLocation().y > oldThirdPoint.getLocation().y - LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP) { + // limit vertical move to keep the minimum distance + secondPoint.getLocation().y = oldThirdPoint.getLocation().y - LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP; + } + newStart.getLocation().y = secondPoint.getLocation().y; + thirdPoint.getLocation().y = secondPoint.getLocation().y; + } else if (oldThirdPoint.getLocation().y != fourthPoint.getLocation().y) { + + // The new bendpoint is between old third and old end points + if (fourthPoint.getLocation().y < oldSecondPoint.getLocation().y + LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP) { + // limit vertical move to keep the minimum distance + fourthPoint.getLocation().y = oldSecondPoint.getLocation().y + LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP; + } + thirdPoint.getLocation().y = fourthPoint.getLocation().y; + newEnd.getLocation().y = fourthPoint.getLocation().y; + } + + bendpoints.add(newStart); + bendpoints.add(secondPoint); + bendpoints.add(thirdPoint); + bendpoints.add(fourthPoint); + bendpoints.add(newEnd); + } + + /** + * Creates a message to self from start to end by adding 2 bendpoints + * shifted on the right by LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_GAP. + * + * @param start + * the message to self start position + * @param end + * the message to self end position + * @return the list of bendpoints to create a message to self from start to + * end + */ + private List<Bendpoint> createMessageToSelf(Bendpoint start, Bendpoint end, SequenceMessageEditPart part) { + ArrayList<Bendpoint> messageToSelfBendpoint = new ArrayList<Bendpoint>(); + + Bendpoint newStart = new AbsoluteBendpoint(start.getLocation()); + Bendpoint newSecondPoint = new AbsoluteBendpoint(start.getLocation()); + Bendpoint newThirdPoint = new AbsoluteBendpoint(end.getLocation()); + Bendpoint newEnd = new AbsoluteBendpoint(end.getLocation()); + + int minGap = LayoutConstants.MESSAGE_TO_SELF_BENDPOINT_VERTICAL_GAP; + if (newEnd.getLocation().y - newStart.getLocation().y < minGap) { + newThirdPoint.getLocation().y = newStart.getLocation().y + minGap; + newEnd.getLocation().y = newThirdPoint.getLocation().y; + } + + messageToSelfBendpoint.add(newStart); + messageToSelfBendpoint.add(newSecondPoint); + messageToSelfBendpoint.add(newThirdPoint); + messageToSelfBendpoint.add(newEnd); + + return messageToSelfBendpoint; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/SequenceZOrderingRefresher.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/SequenceZOrderingRefresher.java new file mode 100644 index 0000000000..9503b906ad --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/layout/SequenceZOrderingRefresher.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Ordering; + +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.CombinedFragmentEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InteractionUseEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ObservationPointEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceDiagramEditPart; + +/** + * Refreshes the Z-ordering in which interaction uses and combined fragments + * appear on a diagram. + * + * @author smonnier + */ +public class SequenceZOrderingRefresher implements Runnable { + private final SequenceDiagramEditPart sequenceDiagramPart; + + /** + * Creates a command which updates the Z ordering of interaction uses and + * combined fragments. + * + * @param sequenceDiagramEditPart + * the diagram whose interaction uses and combined fragments Z + * ordering should be refreshed. + */ + public SequenceZOrderingRefresher(SequenceDiagramEditPart sequenceDiagramEditPart) { + this.sequenceDiagramPart = Preconditions.checkNotNull(sequenceDiagramEditPart); + } + + /** + * {@inheritDoc} + */ + public void run() { + moveInteractionUsesToFront(); + moveCombinedFragmentsToBack(); + moveObservationPointToFront(); + } + + /** + * Shift the interaction uses before every other children so they appear + * visually in front of the rest. + */ + private void moveInteractionUsesToFront() { + moveParts(InteractionUseEditPart.class, true, new InstanceUseVerticalPositionFunction()); + } + + /** + * Shift the interaction uses before every other children so they appear + * visually in front of the rest. + */ + private void moveObservationPointToFront() { + moveParts(ObservationPointEditPart.class, true, null); + } + + /** + * Shift the combined fragments before every other children so they appear + * visually behind the rest. + */ + private void moveCombinedFragmentsToBack() { + moveParts(CombinedFragmentEditPart.class, false, new CombinedFragmentVerticalPositionFunction()); + } + + /** + * Filter the diagram children parts with type to move. Sort them with the + * sorter if not null. Reverse the result if move to front. + */ + private void moveParts(Class<? extends IGraphicalEditPart> typeToMove, boolean moveToFront, Function<IGraphicalEditPart, Integer> sorter) { + List<? extends IGraphicalEditPart> partsToMove = Lists.newArrayList(Iterables.filter(sequenceDiagramPart.getChildren(), typeToMove)); + + if (sorter != null) { + Ordering<IGraphicalEditPart> onResultOf = Ordering.natural().onResultOf(sorter); + if (moveToFront) { + onResultOf = onResultOf.reverse(); + } + Collections.sort(partsToMove, onResultOf); + } + + // Bring combined fragments to back/front + if (!partsToMove.isEmpty()) { + int index = moveToFront ? Iterables.size(Iterables.filter(sequenceDiagramPart.getChildren(), IGraphicalEditPart.class)) - 1 : 0; + for (IGraphicalEditPart frame : partsToMove) { + sequenceDiagramPart.reorderChild(frame, index); + index = index + (moveToFront ? -1 : 1); + } + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/provider/SequenceDiagramEditPartProvider.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/provider/SequenceDiagramEditPartProvider.java new file mode 100644 index 0000000000..0648ad49b3 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/provider/SequenceDiagramEditPartProvider.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.provider; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; +import org.eclipse.gmf.runtime.diagram.ui.services.editpart.AbstractEditPartProvider; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.BracketEdgeStyle; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.SiriusPackage; +import org.eclipse.sirius.diagram.part.SiriusVisualIDRegistry; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.EndOfLife; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InteractionUse; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.LostMessageEnd; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ObservationPoint; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.State; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.CombinedFragmentCompartmentEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.CombinedFragmentEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.EndOfLifeEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ExecutionEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InstanceRoleEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.InteractionUseEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.LifelineEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.LostMessageEndEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ObservationPointEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.OperandCompartmentEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.OperandEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceBracketEdgeEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceDiagramEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceMessageEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceMessageNameEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.StateEditPart; + +/** + * Provides specific edit parts for Sirius Sequence Diagrams. + * + * @author pcdavid + */ +public class SequenceDiagramEditPartProvider extends AbstractEditPartProvider { + /** + * {@inheritDoc} + */ + @Override + protected Class<?> getDiagramEditPartClass(View view) { + if (view instanceof Diagram) { + EObject semanticElement = ViewUtil.resolveSemanticElement(view); + if (semanticElement instanceof DDiagram && SequenceDiagram.viewpointElementPredicate().apply((DDiagram) semanticElement)) { + return SequenceDiagramEditPart.class; + } + } + return super.getDiagramEditPartClass(view); + } + + /** + * {@inheritDoc} + */ + // CHECKSTYLE:OFF + @Override + protected Class<?> getNodeEditPartClass(View view) { + if (view instanceof Node) { + EObject semanticElement = ViewUtil.resolveSemanticElement(view); + if (SiriusPackage.eINSTANCE.getDNode().isInstance(semanticElement)) { + if (InstanceRole.notationPredicate().apply(view)) { + return InstanceRoleEditPart.class; + } else if (Lifeline.notationPredicate().apply(view)) { + return LifelineEditPart.class; + } else if (Execution.notationPredicate().apply(view)) { + return ExecutionEditPart.class; + } else if (State.notationPredicate().apply(view)) { + return StateEditPart.class; + } else if (EndOfLife.notationPredicate().apply(view)) { + return EndOfLifeEditPart.class; + } else if (LostMessageEnd.notationPredicate().apply(view)) { + return LostMessageEndEditPart.class; + } else if (ObservationPoint.notationPredicate().apply(view)) { + return ObservationPointEditPart.class; + } + } else if (SiriusPackage.eINSTANCE.getDNodeContainer().isInstance(semanticElement)) { + if (InteractionUse.notationPredicate().apply(view)) { + return InteractionUseEditPart.class; + } else if (CombinedFragment.notationPredicate().apply(view)) { + return CombinedFragmentEditPart.class; + } else if (CombinedFragment.compartmentNotationPredicate().apply(view)) { + return CombinedFragmentCompartmentEditPart.class; + } else if (Operand.notationPredicate().apply(view)) { + return OperandEditPart.class; + } else if (Operand.compartmentNotationPredicate().apply(view)) { + return OperandCompartmentEditPart.class; + } + } else if (SiriusPackage.eINSTANCE.getDEdge().isInstance(semanticElement)) { + DEdge edge = (DEdge) semanticElement; + if (Message.viewpointElementPredicate().apply(edge) && SiriusVisualIDRegistry.getVisualID(view) == SequenceMessageNameEditPart.VISUAL_ID) { + return SequenceMessageNameEditPart.class; + } + } + } + return super.getNodeEditPartClass(view); + } + + // CHECKSTYLE:ON + + /** + * {@inheritDoc} + */ + @Override + protected Class<?> getEdgeEditPartClass(View view) { + Class<?> edgeEditPartClass = null; + if (view instanceof Edge) { + EObject semanticElement = ViewUtil.resolveSemanticElement(view); + if (semanticElement instanceof DEdge) { + DEdge edge = (DEdge) semanticElement; + if (Message.viewpointElementPredicate().apply((DEdge) semanticElement)) { + edgeEditPartClass = SequenceMessageEditPart.class; + } else if (SequenceDiagram.viewpointElementPredicate().apply(edge.getParentDiagram()) && edge.getStyle() instanceof BracketEdgeStyle) { + // Force the default location. + edgeEditPartClass = SequenceBracketEdgeEditPart.class; + } + } + } + + if (edgeEditPartClass == null) { + edgeEditPartClass = super.getEdgeEditPartClass(view); + } + + return edgeEditPartClass; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/provider/SequenceDiagramLayoutProvider.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/provider/SequenceDiagramLayoutProvider.java new file mode 100644 index 0000000000..73831a1965 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/provider/SequenceDiagramLayoutProvider.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.provider; + +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.layout.SequenceLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider; + +/** + * This provider contributes the appropriate layout provider for sequence + * diagrams. + * + * @author ymortier, mporhel + */ +public class SequenceDiagramLayoutProvider implements LayoutProvider { + + /** + * The GMF layout provider. + */ + private final AbstractLayoutEditPartProvider layoutProvider = new SequenceLayoutProvider(); + + /** + * {@inheritDoc} + */ + public AbstractLayoutEditPartProvider getLayoutNodeProvider(final IGraphicalEditPart container) { + return layoutProvider; + } + + /** + * {@inheritDoc} + */ + public boolean isDiagramLayoutProvider() { + return true; + } + + /** + * {@inheritDoc} + */ + public boolean provides(final IGraphicalEditPart container) { + View notationView = container.getNotationView(); + if (notationView != null) { + Diagram diagram = notationView.getDiagram(); + return ISequenceElementAccessor.getSequenceDiagram(diagram).some(); + } + return false; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/CreateRequestQuery.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/CreateRequestQuery.java new file mode 100644 index 0000000000..6d58972eae --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/CreateRequestQuery.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.util; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.requests.CreateRequest; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceDiagramEditPart; +import org.eclipse.sirius.diagram.ui.tools.api.layout.GraphicalHelper; + +/** + * Queries on GEF and GMF requests. + * + * @author pcdavid + */ +public class CreateRequestQuery extends RequestQuery { + + private CreateRequest createRequest; + + private SequenceDiagramEditPart sdep; + + /** + * Constructor. + * + * @param request + * the request to query. + * @param sdep + * the diagram part to get zoom and scroll information. + */ + public CreateRequestQuery(CreateRequest request, SequenceDiagramEditPart sdep) { + super(request); + this.createRequest = request; + this.sdep = sdep; + } + + @Override + protected List<IGraphicalEditPart> getEditParts() { + return Collections.<IGraphicalEditPart> singletonList(sdep); + } + + /** + * Get {@link Rectangle} image of the delta requested by the current + * {@link CreateRequest}. + * + * {@inheritDoc} + */ + @Override + public Rectangle getLogicalDelta() { + Point location = createRequest.getLocation(); + if (location == null) { + location = new Point(0, 0); + } + + Dimension size = createRequest.getSize(); + if (size == null) { + size = new Dimension(0, 0); + } + Rectangle result = new Rectangle(location, size); + if (sdep != null) { + GraphicalHelper.screen2logical(result, sdep); + } + + return result; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/DelegatingDiagramCommandFactory.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/DelegatingDiagramCommandFactory.java new file mode 100644 index 0000000000..45e991d1a8 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/DelegatingDiagramCommandFactory.java @@ -0,0 +1,331 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.util; + +import java.util.Collection; +import java.util.Set; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.ecore.EObject; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DLabelled; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DRefreshable; +import org.eclipse.sirius.DRepresentationElement; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.DragAndDropTarget; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.business.api.dialect.command.CreateRepresentationCommand; +import org.eclipse.sirius.description.DiagramDescription; +import org.eclipse.sirius.description.tool.BehaviorTool; +import org.eclipse.sirius.description.tool.ContainerCreationDescription; +import org.eclipse.sirius.description.tool.ContainerDropDescription; +import org.eclipse.sirius.description.tool.DirectEditLabel; +import org.eclipse.sirius.description.tool.DoubleClickDescription; +import org.eclipse.sirius.description.tool.EdgeCreationDescription; +import org.eclipse.sirius.description.tool.ExternalJavaAction; +import org.eclipse.sirius.description.tool.NodeCreationDescription; +import org.eclipse.sirius.description.tool.OperationAction; +import org.eclipse.sirius.description.tool.PaneBasedSelectionWizardDescription; +import org.eclipse.sirius.description.tool.PasteDescription; +import org.eclipse.sirius.description.tool.ReconnectEdgeDescription; +import org.eclipse.sirius.description.tool.RepresentationCreationDescription; +import org.eclipse.sirius.description.tool.SelectionWizardDescription; +import org.eclipse.sirius.description.tool.ToolDescription; +import org.eclipse.sirius.description.validation.ValidationFix; +import org.eclipse.sirius.tools.api.command.DCommand; +import org.eclipse.sirius.tools.api.command.IDiagramCommandFactory; +import org.eclipse.sirius.tools.api.command.ui.UICallBack; +import org.eclipse.sirius.tools.api.ui.IExternalJavaAction; + +/** + * An implementation of <code>IDiagramCommandFactory</code> which delegates to + * another one. Useful to customize only some of the methods when one does not + * control the instantiation of the factory to customize. + * + * @author pcdavid + */ +@SuppressWarnings("deprecation") +public class DelegatingDiagramCommandFactory implements IDiagramCommandFactory { + private final IDiagramCommandFactory baseFactory; + + /** + * Constructor. + * + * @param baseFactory + * the factory to delegate to. + */ + public DelegatingDiagramCommandFactory(IDiagramCommandFactory baseFactory) { + this.baseFactory = Preconditions.checkNotNull(baseFactory); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildCreateNodeCommandFromTool(DNode node, NodeCreationDescription tool) { + return baseFactory.buildCreateNodeCommandFromTool(node, tool); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildCreateContainerCommandFromTool(DDiagram diagram, ContainerCreationDescription tool) { + return baseFactory.buildCreateContainerCommandFromTool(diagram, tool); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildCreateContainerCommandFromTool(DDiagramElementContainer nodeContainer, ContainerCreationDescription tool) { + return baseFactory.buildCreateContainerCommandFromTool(nodeContainer, tool); + } + + /** + * {@inheritDoc} + */ + public DCommand buildCreateDiagramFromDescription(DiagramDescription description, EObject semanticElement) { + return baseFactory.buildCreateDiagramFromDescription(description, semanticElement); + } + + /** + * {@inheritDoc} + */ + public DCommand buildCreateDiagramFromDescription(DiagramDescription description, EObject semanticElement, IProgressMonitor monitor) { + return baseFactory.buildCreateDiagramFromDescription(description, semanticElement, monitor); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildCreateEdgeCommandFromTool(EdgeTarget source, EdgeTarget target, EdgeCreationDescription tool) { + return baseFactory.buildCreateEdgeCommandFromTool(source, target, tool); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildCreateNodeCommandFromTool(DDiagram diagram, NodeCreationDescription tool) { + return baseFactory.buildCreateNodeCommandFromTool(diagram, tool); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildCreateNodeCommandFromTool(DDiagramElementContainer container, NodeCreationDescription tool) { + return baseFactory.buildCreateNodeCommandFromTool(container, tool); + } + + /** + * {@inheritDoc} + */ + public CreateRepresentationCommand buildCreateRepresentationFromDescription(RepresentationCreationDescription desc, DRepresentationElement element, String newRepresentationName) { + return baseFactory.buildCreateRepresentationFromDescription(desc, element, newRepresentationName); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildDeleteDiagram(DDiagram vp) { + return baseFactory.buildDeleteDiagram(vp); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildDeleteDiagramElement(DDiagramElement element) { + return baseFactory.buildDeleteDiagramElement(element); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildDeleteFromDiagramCommand(DDiagramElement element) { + return baseFactory.buildDeleteFromDiagramCommand(element); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildDirectEditLabelFromTool(DLabelled labelled, DirectEditLabel directEditTool, String newValue) { + return baseFactory.buildDirectEditLabelFromTool(labelled, directEditTool, newValue); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildDoExecuteDetailsOperation(DSemanticDecorator target, RepresentationCreationDescription desc, String newRepresentationName) { + return baseFactory.buildDoExecuteDetailsOperation(target, desc, newRepresentationName); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildDoubleClickOnElementCommandFromTool(DDiagramElement dDiagramElement, DoubleClickDescription tool) { + return baseFactory.buildDoubleClickOnElementCommandFromTool(dDiagramElement, tool); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildDropInContainerCommandFromTool(DragAndDropTarget dContainer, DDiagramElement element, ContainerDropDescription tool) { + return baseFactory.buildDropInContainerCommandFromTool(dContainer, element, tool); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildDropInContainerCommandFromTool(DragAndDropTarget dContainer, EObject droppedElement, ContainerDropDescription tool) { + return baseFactory.buildDropInContainerCommandFromTool(dContainer, droppedElement, tool); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildGenericToolCommandFromTool(EObject containerView, ToolDescription toolDesc) { + return baseFactory.buildGenericToolCommandFromTool(containerView, toolDesc); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildHideCommand(Set<EObject> elementsToHide) { + return baseFactory.buildHideCommand(elementsToHide); + } + + /** + * {@inheritDoc} + * + */ + public Command buildHideLabelCommand(Set<EObject> elementsToHide) { + return baseFactory.buildHideLabelCommand(elementsToHide); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildJavaActionFromTool(ExternalJavaAction tool, Collection<DSemanticDecorator> selectedViews, IExternalJavaAction javaAction) { + return baseFactory.buildJavaActionFromTool(tool, selectedViews, javaAction); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildLaunchRuleCommandFromTool(DSemanticDecorator rootObject, BehaviorTool tool, boolean executeFromRootContainer, boolean deepProcess) { + return baseFactory.buildLaunchRuleCommandFromTool(rootObject, tool, executeFromRootContainer, deepProcess); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildOperationActionFromTool(OperationAction tool, Collection<DSemanticDecorator> selectedViews) { + return baseFactory.buildOperationActionFromTool(tool, selectedViews); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildPaneBasedSelectionWizardCommandFromTool(PaneBasedSelectionWizardDescription tool, DSemanticDecorator dContainer, + Collection<EObject> selectedElement) { + return baseFactory.buildPaneBasedSelectionWizardCommandFromTool(tool, dContainer, selectedElement); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildQuickFixOperation(ValidationFix fix, EObject fixTarget, DDiagram diagram) { + return baseFactory.buildQuickFixOperation(fix, fixTarget, diagram); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildReconnectEdgeCommandFromTool(ReconnectEdgeDescription tool, DEdge edge, EdgeTarget source, EdgeTarget target) { + return baseFactory.buildReconnectEdgeCommandFromTool(tool, edge, source, target); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildRefreshCommand(DRefreshable refreshableElement) { + return baseFactory.buildRefreshCommand(refreshableElement); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildRevealCommand(DDiagramElement diagramElement) { + return baseFactory.buildRevealCommand(diagramElement); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildRevealElementsCommand(DDiagram diagram) { + return baseFactory.buildRevealElementsCommand(diagram); + } + + /** + * {@inheritDoc} + */ + public Command buildRevealLabelCommand(DDiagramElement diagramElement) { + return baseFactory.buildRevealLabelCommand(diagramElement); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildRevealElementsCommand(Set<DDiagramElement> elementsToReveal) { + return baseFactory.buildRevealElementsCommand(elementsToReveal); + } + + /** + * {@inheritDoc} + */ + public org.eclipse.emf.common.command.Command buildSelectionWizardCommandFromTool(SelectionWizardDescription tool, DSemanticDecorator dContainer, Collection<EObject> selectedElement) { + return baseFactory.buildSelectionWizardCommandFromTool(tool, dContainer, selectedElement); + } + + /** + * {@inheritDoc} + */ + public void setAutoRefreshDView(boolean autoRefreshDView) { + baseFactory.setAutoRefreshDView(autoRefreshDView); + } + + /** + * {@inheritDoc} + */ + public void setUserInterfaceCallBack(UICallBack newCB) { + baseFactory.setUserInterfaceCallBack(newCB); + } + + /** + * {@inheritDoc} + */ + public Command buildPasteCommandFromTool(DSemanticDecorator dContainer, DSemanticDecorator element, PasteDescription tool) { + return baseFactory.buildPasteCommandFromTool(dContainer, element, tool); + } + + /** + * {@inheritDoc} + */ + public Command buildPasteCommandFromTool(DSemanticDecorator dContainer, EObject droppedElement, PasteDescription tool) { + return baseFactory.buildPasteCommandFromTool(dContainer, droppedElement, tool); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/EditPartsHelper.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/EditPartsHelper.java new file mode 100644 index 0000000000..71e3a3dca2 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/EditPartsHelper.java @@ -0,0 +1,329 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature.Setting; +import org.eclipse.emf.ecore.util.ECrossReferenceAdapter; +import org.eclipse.gef.ConnectionEditPart; +import org.eclipse.gef.EditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.SiriusPackage; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.business.api.session.SessionManager; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.SequenceDiagram; +import org.eclipse.sirius.diagram.sequence.ordering.SingleEventEnd; +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.edit.part.SequenceDiagramEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceMessageEditPart; +import org.eclipse.sirius.diagram.ui.tools.internal.util.EditPartQuery; + +/** + * Helper class with utilities to deal with GMF edit parts. + * + * @author pcdavid + */ +public final class EditPartsHelper { + + /** + * A predicate which checks that a given edit part is active. + * + * @author pcdavid + */ + private static enum IsValidPredicate implements Predicate<IGraphicalEditPart> { + INSTANCE; + + public boolean apply(IGraphicalEditPart input) { + return input.getParent() != null; + } + } + + private EditPartsHelper() { + // Prevents instantiation. + } + + /** + * Returns a predicate which checks that a given edit part is active. + * + * @return a predicate which checks that a given edit part is active. + */ + public static Predicate<IGraphicalEditPart> isValid() { + return IsValidPredicate.INSTANCE; + } + + /** + * Finds all the sequence messages whose source or target is the specified + * element or any of its descendant edit parts, without duplicates. + * + * @param element + * the element from which to start the search for messages. + * @return the messages found without duplicates. + * @deprecated + */ + @Deprecated + public static Set<SequenceMessageEditPart> getAllMessages(IGraphicalEditPart element) { + Set<SequenceMessageEditPart> allMessages = Sets.newHashSet(); + allMessages.addAll(EditPartsHelper.getAllMessagesFrom(element)); + allMessages.addAll(EditPartsHelper.getAllMessagesTo(element)); + return allMessages; + } + + /** + * Finds all the sequence messages whose source is the specified element or + * any of its descendant edit parts. + * + * @param element + * the element from which to start the search for messages. + * @return the messages found. + * @deprecated + */ + @Deprecated + public static List<SequenceMessageEditPart> getAllMessagesFrom(IGraphicalEditPart element) { + List<SequenceMessageEditPart> messagesParts = Lists.newArrayList(); + EditPartsHelper.addAllMessagesFrom(element, messagesParts); + return messagesParts; + } + + /** + * Finds all the sequence messages whose target is the specified element or + * any of its descendant edit parts. + * + * @param element + * the element from which to start the search for messages. + * @return the messages found. + * @deprecated + */ + @Deprecated + private static List<SequenceMessageEditPart> getAllMessagesTo(IGraphicalEditPart element) { + List<SequenceMessageEditPart> messagesParts = Lists.newArrayList(); + EditPartsHelper.addAllMessagesTo(element, messagesParts); + return messagesParts; + } + + /** + * Finds all the (non root) executions contained inside the specified + * element or any of its descendant edit parts. + * + * @param element + * the element from which to start the search for executions. + * @return the executions found. + * @deprecated + */ + @Deprecated + public static List<ExecutionEditPart> getAllExecutions(IGraphicalEditPart element) { + return Lists.newArrayList(Iterators.filter(Iterators.filter(new EditPartsTreeIterator(element), ExecutionEditPart.class), EditPartsHelper.isValid())); + } + + /** + * Finds all the lifelines contained inside the specified element or any of + * its descendant edit parts. + * + * @param element + * the element from which to start the search for executions. + * @return the executions found. + * + * @deprecated + */ + @Deprecated + public static List<LifelineEditPart> getAllLifelines(IGraphicalEditPart element) { + return Lists.newArrayList(Iterators.filter(Iterators.filter(new EditPartsTreeIterator(element), LifelineEditPart.class), EditPartsHelper.isValid())); + } + + /** + * Finds all the sequence messages whose source is the specified element or + * any of its descendant edit parts and add them to a collection. + * + * @param element + * the element from which to start the search for messages. + * @param messages + * the collection in which to add the messages. + */ + private static void addAllMessagesFrom(IGraphicalEditPart element, Collection<SequenceMessageEditPart> messages) { + for (IGraphicalEditPart connectionPart : Iterables.filter(element.getSourceConnections(), IGraphicalEditPart.class)) { + if (connectionPart instanceof SequenceMessageEditPart && EditPartsHelper.isValid().apply(connectionPart)) { + messages.add((SequenceMessageEditPart) connectionPart); + } + } + if (element instanceof SequenceMessageEditPart && EditPartsHelper.isValid().apply(element)) { + messages.add((SequenceMessageEditPart) element); + } + for (IGraphicalEditPart child : Iterables.filter(element.getChildren(), IGraphicalEditPart.class)) { + EditPartsHelper.addAllMessagesFrom(child, messages); + } + } + + /** + * Finds all the sequence messages whose target is the specified element or + * any of its descendant edit parts and add them to a collection. + * + * @param element + * the element from which to start the search for messages. + * @param messages + * the collection in which to add the messages. + */ + private static void addAllMessagesTo(IGraphicalEditPart element, Collection<SequenceMessageEditPart> messages) { + for (IGraphicalEditPart connectionPart : Iterables.filter(element.getTargetConnections(), IGraphicalEditPart.class)) { + if (connectionPart instanceof SequenceMessageEditPart && EditPartsHelper.isValid().apply(connectionPart)) { + messages.add((SequenceMessageEditPart) connectionPart); + } + } + if (element instanceof SequenceMessageEditPart && EditPartsHelper.isValid().apply(element)) { + messages.add((SequenceMessageEditPart) element); + } + for (IGraphicalEditPart child : Iterables.filter(element.getChildren(), IGraphicalEditPart.class)) { + EditPartsHelper.addAllMessagesTo(child, messages); + } + } + + /** + * Finds all the events in the specified diagram which have a specific + * semantic target. + * + * @param diagram + * the sequence diagram. + * @param semanticElement + * the semantic element. + * @return all the events in the diagram which have the specified semantic + * element as target. + */ + public static Collection<ISequenceEventEditPart> getEventsForSemanticElement(SequenceDiagramEditPart diagram, EObject semanticElement) { + ECrossReferenceAdapter xref = EditPartsHelper.getCrossReferencer(semanticElement); + if (xref == null) { + return Collections.emptySet(); + } else { + Map<?, ?> registry = diagram.getViewer().getEditPartRegistry(); + Collection<ISequenceEventEditPart> result = Lists.newArrayList(); + for (Setting setting : xref.getInverseReferences(semanticElement)) { + if (EditPartsHelper.isDiagramElementTargetReference(setting)) { + DDiagramElement dde = (DDiagramElement) setting.getEObject(); + Object registered = registry.get(dde); + if (registered instanceof ISequenceEventEditPart && EditPartsHelper.isValid().apply((ISequenceEventEditPart) registered)) { + result.add((ISequenceEventEditPart) registry.get(dde)); + } + } + } + return result; + } + } + + private static boolean isDiagramElementTargetReference(Setting setting) { + EReference targetReference = SiriusPackage.eINSTANCE.getDSemanticDecorator_Target(); + return setting.getEObject() instanceof DDiagramElement && setting.getEStructuralFeature().equals(targetReference); + } + + private static ECrossReferenceAdapter getCrossReferencer(EObject semanticElement) { + Session session = SessionManager.INSTANCE.getSession(semanticElement); + if (session != null) { + return session.getSemanticCrossReferencer(); + } else { + return null; + } + } + + /** + * Finds the parent {@link LifelineEditPart}. + * + * @param part + * the {@link IGraphicalEditPart} to find its parent + * {@link LifelineEditPart}. + * @return the parent {@link LifelineEditPart} + */ + public static LifelineEditPart findParentLifeline(IGraphicalEditPart part) { + final LifelineEditPart result; + if (part instanceof LifelineEditPart && EditPartsHelper.isValid().apply(part)) { + result = (LifelineEditPart) part; + } else if (part != null) { + result = new EditPartQuery(part).getFirstAncestorOfType(LifelineEditPart.class); + } else { + result = null; + } + return result; + } + + /** + * Finds and returns the ISequenceEvent corresponding to the given + * SingleEventEnd. + * + * @param end + * the end to look for + * @param sdep + * the SequenceDiagramEditPart to inspect + * @return the ISequenceEvent corresponding to the given part + */ + public static ISequenceEventEditPart findISequenceEvent(SingleEventEnd end, SequenceDiagramEditPart sdep) { + for (ISequenceEventEditPart ise : Iterables.concat(EditPartsHelper.getAllExecutions(sdep), EditPartsHelper.getAllMessagesFrom(sdep))) { + EObject semanticEvent = ise.resolveTargetSemanticElement(); + if (end.getSemanticEvent().equals(semanticEvent)) { + return ise; + } + } + return null; + } + + /** + * Returns the sequence diagram edit part owning the edit part. + * + * @param editPart + * the edit part. + * @return the sequence diagram edit part owning the part. + */ + public static SequenceDiagramEditPart getSequenceDiagramPart(IGraphicalEditPart editPart) { + if (editPart instanceof SequenceDiagramEditPart) { + return (SequenceDiagramEditPart) editPart; + } + IGraphicalEditPart current = editPart; + if (editPart instanceof ConnectionEditPart) { + ConnectionEditPart conn = (ConnectionEditPart) editPart; + current = (IGraphicalEditPart) conn.getSource(); + } + return new EditPartQuery(current).getFirstAncestorOfType(SequenceDiagramEditPart.class); + } + + /** + * Get a {@link SequenceDiagram} from a EditPart of Sequence representation. + * + * @param host + * editPart of Sequence representation + * @param <T> + * any subtype of EditPart + * @return the {@link SequenceDiagram} of a sequence representation + */ + public static <T extends EditPart> SequenceDiagram getSequenceDiagram(T host) { + EditPart parent = host; + while (parent != null && !(parent instanceof ISequenceEventEditPart || parent instanceof SequenceDiagramEditPart)) { + parent = parent.getParent(); + } + SequenceDiagram sequenceDiagram = null; + if (parent instanceof SequenceDiagramEditPart) { + sequenceDiagram = ((SequenceDiagramEditPart) parent).getSequenceDiagram(); + } else if (parent instanceof ISequenceEventEditPart) { + sequenceDiagram = ((ISequenceEventEditPart) parent).getISequenceEvent().getDiagram(); + } + return sequenceDiagram; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/EditPartsTreeIterator.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/EditPartsTreeIterator.java new file mode 100644 index 0000000000..3a0c432f51 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/EditPartsTreeIterator.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.util; + +import java.util.Iterator; + +import org.eclipse.emf.common.util.AbstractTreeIterator; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; + +/** + * A tree iterator to iterate on hierarchies of GMF edit parts. + * + * @author pcdavid + */ +class EditPartsTreeIterator extends AbstractTreeIterator<IGraphicalEditPart> { + /** + * Generated serial version UID. + */ + private static final long serialVersionUID = 3234398995856675133L; + + /** + * Creates a new tree iterator on the specified edit part and all its + * descendant edit parts. + * + * @param root + * the root edit part of the iteration. + */ + public EditPartsTreeIterator(IGraphicalEditPart root) { + super(root); + } + + /** + * Creates a new tree iterator on the descendants of the specified edit + * part. + * + * @param root + * the root edit part of the iteration. + * @param includeRoot + * if <code>true</code>, the iterator includes the root element. + */ + public EditPartsTreeIterator(IGraphicalEditPart root, boolean includeRoot) { + super(root, includeRoot); + } + + /** + * {@inheritDoc} + */ + @Override + protected Iterator<? extends IGraphicalEditPart> getChildren(Object object) { + if (object instanceof IGraphicalEditPart) { + Iterable<IGraphicalEditPart> children = Iterables.filter(((IGraphicalEditPart) object).getChildren(), IGraphicalEditPart.class); + return children.iterator(); + } else { + return Iterators.emptyIterator(); + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/FinalParentHelper.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/FinalParentHelper.java new file mode 100644 index 0000000000..9be87a22d5 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/FinalParentHelper.java @@ -0,0 +1,307 @@ +/******************************************************************************* + * Copyright (c) 2010, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Set; + +import org.eclipse.draw2d.geometry.Rectangle; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.CombinedFragment; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Lifeline; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Message; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Operand; +import org.eclipse.sirius.diagram.sequence.business.internal.ordering.EventEndHelper; +import org.eclipse.sirius.diagram.sequence.business.internal.query.ISequenceEventQuery; +import org.eclipse.sirius.diagram.sequence.business.internal.util.EventFinder; +import org.eclipse.sirius.diagram.sequence.util.Range; + +public class FinalParentHelper { + private final AbstractNodeEvent self; + + private final RequestQuery request; + + private Range expansionZone; + + private ISequenceEvent globalFinalParent; + + /** + * Constructor. + * + * @param self + * the execution which will be resized. + * @param resizeRequest + * the resize query. + */ + public FinalParentHelper(AbstractNodeEvent self, RequestQuery resizeRequest) { + this.self = Preconditions.checkNotNull(self); + this.request = Preconditions.checkNotNull(resizeRequest); + Preconditions.checkArgument(resizeRequest.isResize()); + } + + /** + * Determines what event will be the direct parent of this execution after + * the execution of the specified request. Returns <code>null</code> if the + * request is invalid. Calling this method also sets the + * <code>expansionZone</code> field: it is set to <code>null</code> if the + * returned parent can accept this execution directly, or to the vertical + * range which needs to be expanded on the future parent if there is not yet + * enough room to contain this execution. + */ + public void computeFinalParent() { + Range fullFinalRange = getFullFinalRange(); + if (fullFinalRange != null) { + if (isInvalidInteractionInsideOperand(fullFinalRange)) { + globalFinalParent = null; + } else if (fullFinalRange != null && request.isResize()) { + globalFinalParent = getFinalParentOnResize(fullFinalRange); + } + } + } + + /** + * Validates if the execution resize is valid if located in an operand. + * + * @param fullFinalRange + * the new range of the resized {@link ISequenceEvent} + * @return if the execution resize is valid if located in an operand. + */ + private boolean isInvalidInteractionInsideOperand(Range fullFinalRange) { + boolean result = false; + Option<Operand> parentOperand = self.getParentOperand(); + if (request.isResize()) { + result = parentOperand.some() && !parentOperand.get().getVerticalRange().includes(fullFinalRange); + } + return result; + } + + /** + * Returns the computed final parent of the execution after the resize. + * + * @return the final parent of the execution after the resize. + */ + public ISequenceEvent getFinalParent() { + return globalFinalParent; + } + + /** + * Returns the auto-expansion required before the resize. + * + * @return the auto-expansion required before the resize. + */ + public Range getRequiredExpansion() { + return expansionZone; + } + + /** + * Computes the full final range which would be occupied by this execution + * and any of the elements resized along with it if we accept the specified + * request. + * + * @param request + * the request for changing the size/location of this execution + * @return the final vertical range of the execution and its linked messages + * (if any) if we accept the request. + */ + private Range getFullFinalRange() { + Rectangle newBounds = request.getLogicalTransformedRectangle(self.getProperLogicalBounds()); + if (newBounds.width < 0 || newBounds.height < 0) { + return null; + } + Range fullFinalRange = Range.verticalRange(newBounds); + + if (self instanceof Execution) { + Execution execution = (Execution) self; + + Option<Message> startMessage = execution.getStartMessage(); + if (startMessage.some() && !startMessage.get().surroundsEventOnSameLifeline()) { + int startY = fullFinalRange.getLowerBound() - startMessage.get().getVerticalRange().width(); + fullFinalRange = new Range(startY, fullFinalRange.getUpperBound()); + } + Option<Message> endMessage = execution.getEndMessage(); + if (endMessage.some() && !endMessage.get().surroundsEventOnSameLifeline()) { + int finishY = fullFinalRange.getUpperBound() + endMessage.get().getVerticalRange().width(); + fullFinalRange = new Range(fullFinalRange.getLowerBound(), finishY); + } + } + + return fullFinalRange; + } + + private ISequenceEvent getFinalParentOnResize(final Range fullFinalRange) { + Preconditions.checkArgument(request.isResize()); + final Collection<ISequenceEvent> remoteErrors = Sets.newHashSet(); + + /* + * A simple resizing can not change the parent of an execution. + */ + ISequenceEvent finalParent = self.getParentEvent(); + /* + * We must still check that the resizing is valid, in that it will not + * cause an overlap/conflict with sibling events. + */ + final Collection<ISequenceEvent> linkedSiblings = FinalParentHelper.computeLinkedSiblings(request); + Iterable<ISequenceEvent> finalSiblings = EventEndHelper.getIndependantEvents(self, finalParent.getSubEvents()); + + final Option<Lifeline> selfLifeline = self.getLifeline(); + Predicate<ISequenceEvent> sameLifeline = new Predicate<ISequenceEvent>() { + public boolean apply(ISequenceEvent input) { + Option<Lifeline> inputLifeline = input.getLifeline(); + boolean same = !inputLifeline.some() || (selfLifeline.some() && inputLifeline.get() == selfLifeline.get()); + + if (input instanceof Message) { + Option<Lifeline> sourceLifeline = ((Message) input).getSourceLifeline(); + same = same || !sourceLifeline.some() || (selfLifeline.some() && sourceLifeline.get() == selfLifeline.get()); + Option<Lifeline> tgtLifeline = ((Message) input).getTargetLifeline(); + same = same || !tgtLifeline.some() || (selfLifeline.some() && tgtLifeline.get() == selfLifeline.get()); + } + + return same; + } + }; + + Predicate<ISequenceEvent> intersectsFinalBounds = new Predicate<ISequenceEvent>() { + public boolean apply(ISequenceEvent input) { + Range inputRange = input.getVerticalRange(); + boolean intersection = inputRange.intersects(fullFinalRange) && !linkedSiblings.contains(input); + // Some event could not be parents : states for examples. + boolean includedInput = !self.getValidSubEventsRange().isEmpty() && fullFinalRange.includes(inputRange.grown(1)); + + if (input instanceof Message) { + Message msg = (Message) input; + + if (msg.isReflective() && !includedInput) { + intersection = inputRange.intersects(fullFinalRange) && !inputRange.includes(fullFinalRange); + includedInput = inputRange.includes(fullFinalRange); + } + + if (intersection && msg.isCompoundMessage()) { + Iterable<Execution> compoundEvents = Iterables.filter(EventEndHelper.getCompoundEvents(input), Execution.class); + if (!Iterables.isEmpty(compoundEvents)) { + Execution remoteExec = compoundEvents.iterator().next(); + if (remoteExec != null && remoteExec.getEndMessage().some() && !fullFinalRange.includes(remoteExec.getExtendedVerticalRange())) { + includedInput = false; + remoteErrors.add(remoteExec); + } + } + } + } + return intersection && !includedInput; + } + }; + + /* + * Removes parent combined fragment to be able to resize an execution in + * a combined fragment + */ + Predicate<ISequenceEvent> notParentCombinedFragment = new Predicate<ISequenceEvent>() { + public boolean apply(ISequenceEvent input) { + if (input instanceof CombinedFragment && self.getLifeline().some()) { + CombinedFragment combinedFragment = (CombinedFragment) input; + return !(combinedFragment.computeCoveredLifelines().contains(self.getLifeline().get()) && combinedFragment.getVerticalRange().includes(fullFinalRange)); + } + return true; + } + }; + + Iterable<ISequenceEvent> invalids = Iterables.filter(finalSiblings, Predicates.and(sameLifeline, notParentCombinedFragment, intersectsFinalBounds)); + if (!Iterables.isEmpty(invalids)) { + finalParent = null; + for (ISequenceEvent ise : Iterables.concat(invalids, remoteErrors)) { + Range verticalRange = ise.getVerticalRange(); + + if (ise instanceof Message && ((Message) ise).isReflective() && verticalRange.includes(self.getVerticalRange())) { + verticalRange = new Range(verticalRange.getUpperBound(), verticalRange.getUpperBound()); + } + + if (request.isResizeFromBottom()) { + Range errorRange = new Range(verticalRange.getLowerBound() - 1, Math.max(verticalRange.getLowerBound(), fullFinalRange.getUpperBound())); + expansionZone = expansionZone == null ? errorRange : errorRange.union(expansionZone); + } + } + } + return finalParent; + } + + /** + * Get list of {@link ISequenceEvent} from the request. + * + * @param request + * request of resize + * + * @return list of {@link ISequenceEvent} + */ + public static ArrayList<ISequenceEvent> computeLinkedSiblings(RequestQuery request) { + final ArrayList<ISequenceEvent> linkedSiblings = Lists.newArrayList(); + Set<Execution> parts = request.getExecutions(); + for (Execution execution : parts) { + linkedSiblings.add(execution); + linkedSiblings.addAll(execution.findLinkedExecutions(true)); + linkedSiblings.addAll(new ISequenceEventQuery(execution).getAllDescendants()); + } + return linkedSiblings; + } + + /** + * Get the {@link ISequenceEvent} for ... + * + * @param self + * {@link AbstractNodeEvent} to consider. + * @param smep + * the {@link Message} to consider. + * @param deltaY + * the deltaY to use + * @param deltaHeight + * the deltaHeight to use + * + * @return the {@link ISequenceEvent} for ... + */ + public static ISequenceEvent getFinalRemoteParent(AbstractNodeEvent self, Message smep, int deltaY, int deltaHeight) { + Range finalMessageRange = smep.getVerticalRange().shifted(deltaY); + if (deltaHeight != 0) { + finalMessageRange = new Range(finalMessageRange.getLowerBound(), finalMessageRange.getUpperBound() + deltaHeight); + } + Set<ISequenceEvent> allMovedElements = new ISequenceEventQuery(self).getAllDescendants(); + allMovedElements.add(self); + + ISequenceEvent sourceParent = (smep.getSourceElement() instanceof ISequenceEvent) ? (ISequenceEvent) smep.getSourceElement() : null; + + /* + * The target will not be an ISequenceEvent for creation and destruction + * messages as instance roles and EOL are not ISequenceEvents. + */ + ISequenceEvent targetParent = (smep.getTargetElement() instanceof ISequenceEvent) ? (ISequenceEvent) smep.getTargetElement() : null; + ISequenceEvent remoteParent = allMovedElements.contains(sourceParent) ? targetParent : sourceParent; + + ISequenceEvent finalRemoteParent = null; + if (remoteParent != null) { + Option<Lifeline> remoteLifeline = remoteParent.getLifeline(); + if (remoteLifeline.some()) { + EventFinder remoteFinder = new EventFinder(remoteLifeline.get()); + remoteFinder.setEventsToIgnore(Predicates.in(Lists.newArrayList(allMovedElements))); + finalRemoteParent = remoteFinder.findMostSpecificEvent(finalMessageRange); + } + } + return finalRemoteParent; + } +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/RequestQuery.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/RequestQuery.java new file mode 100644 index 0000000000..f1c50f4e11 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/RequestQuery.java @@ -0,0 +1,270 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.Request; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gef.requests.CreateRequest; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.AbstractNodeEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.Execution; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceElementAccessor; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.ISequenceEvent; +import org.eclipse.sirius.diagram.sequence.business.internal.elements.InstanceRole; +import org.eclipse.sirius.diagram.sequence.description.BasicMessageMapping; +import org.eclipse.sirius.diagram.sequence.description.CreationMessageMapping; +import org.eclipse.sirius.diagram.sequence.description.DestructionMessageMapping; +import org.eclipse.sirius.diagram.sequence.description.tool.MessageCreationTool; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.ExecutionEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.policy.SequenceMessageEditPolicy; + +/** + * Queries on GEF and GMF requests. + * + * @author pcdavid + */ +public class RequestQuery extends org.eclipse.sirius.diagram.business.internal.query.RequestQuery { + + /** + * Constaint to check if request if from another indirect request. + */ + public static final String IS_MOVED_BY_PARENT_EXECUTION = "isMovedByParentExecution"; + + /** + * Constructor. + * + * @param request + * the request to query. + */ + public RequestQuery(Request request) { + super(request); + } + + /** + * Determines the final bounds for this execution (in logical coordinates) + * if we accept the specified request. Uses Draw2D information to get the + * current bounds. + * + * @param self + * {@link AbstractNodeEvent} in move/resize + * + * @return final bounds of self + */ + public Rectangle getFinalBounds(ExecutionEditPart self) { + Rectangle bounds = self.getFigure().getBounds().getCopy(); + return getLogicalTransformedRectangle(bounds); + } + + /** + * Get the list of AbstractNodeEvents from the current GroupRequest. + * + * @return the list of AbstractNodeEvents + */ + public Set<AbstractNodeEvent> getAbstractNodeEvent() { + List<IGraphicalEditPart> editParts = getEditParts(); + + if (editParts.isEmpty()) { + return Collections.emptySet(); + } + + Set<AbstractNodeEvent> result = Sets.newHashSet(); + for (IGraphicalEditPart part : editParts) { + Option<AbstractNodeEvent> execution = ISequenceElementAccessor.getAbstractNodeEvent(part.getNotationView()); + if (execution.some()) { + result.add(execution.get()); + } + } + return result; + } + + /** + * Get the list of Executions from the current GroupRequest. + * + * @return the list of Executions + */ + public Set<Execution> getExecutions() { + List<IGraphicalEditPart> editParts = getEditParts(); + + if (editParts.isEmpty()) { + return Collections.emptySet(); + } + + Set<Execution> result = Sets.newHashSet(); + for (IGraphicalEditPart part : editParts) { + Option<Execution> execution = ISequenceElementAccessor.getExecution(part.getNotationView()); + if (execution.some()) { + result.add(execution.get()); + } + } + return result; + } + + /** + * Get the list of {@link ISequenceEvent} from the current GroupRequest. + * + * @return the list of Executions + */ + public Set<ISequenceEvent> getISequenceEvents() { + List<IGraphicalEditPart> editParts = getEditParts(); + + if (editParts.isEmpty()) { + return Collections.emptySet(); + } + + Set<ISequenceEvent> result = Sets.newHashSet(); + for (IGraphicalEditPart part : editParts) { + Option<ISequenceEvent> ise = ISequenceElementAccessor.getISequenceEvent(part.getNotationView()); + if (ise.some()) { + result.add(ise.get()); + } + } + return result; + } + + /** + * Get the list of InstanceRoles from the current GroupRequest. + * + * @return the list of InstanceRoles + */ + public List<InstanceRole> getInstanceRoles() { + List<IGraphicalEditPart> editParts = getEditParts(); + + if (editParts.isEmpty()) { + return Collections.emptyList(); + } + + List<InstanceRole> instanceRoles = new ArrayList<InstanceRole>(); + for (IGraphicalEditPart part : editParts) { + Option<InstanceRole> instanceRole = ISequenceElementAccessor.getInstanceRole(part.getNotationView()); + if (instanceRole.some()) { + instanceRoles.add(instanceRole.get()); + } + } + return instanceRoles; + } + + /** + * Tests whether this execution is being moved indirectly by ancestor, or + * because is linked to a moving execution, as part of the request, in which + * case the parent/main execution is responsible for most of the work. + * + * @return true if this request if from another indirect request + */ + public boolean isExecutionMovedIndirectly() { + Map<?, ?> extData = request.getExtendedData(); + if (request instanceof ChangeBoundsRequest && extData != null && extData.get(IS_MOVED_BY_PARENT_EXECUTION) instanceof Boolean) { + Boolean value = (Boolean) extData.get(IS_MOVED_BY_PARENT_EXECUTION); + return value.booleanValue(); + } else { + return false; + } + } + + /** + * Checks if the current request is a creation request for a new create + * message. + * + * @return true if the current request is a creation request for a new + * create message + */ + public boolean isCreateMessageCreation() { + boolean result = false; + if (!isNoteAttachmentCreationRequest() && request instanceof CreateRequest) { + CreateRequest createRequest = (CreateRequest) request; + if (createRequest.getNewObject() instanceof MessageCreationTool) { + MessageCreationTool messageCreationTool = (MessageCreationTool) createRequest.getNewObject(); + result = Iterables.any(messageCreationTool.getEdgeMappings(), Predicates.instanceOf(CreationMessageMapping.class)); + } + } + return result; + } + + /** + * Checks if the current request is a creation request for a new destroy + * message. + * + * @return true if the current request is a creation request for a new + * destroy message + */ + public boolean isDestroyMessageCreation() { + boolean result = false; + if (!isNoteAttachmentCreationRequest() && request instanceof CreateRequest) { + CreateRequest createRequest = (CreateRequest) request; + if (createRequest.getNewObject() instanceof MessageCreationTool) { + MessageCreationTool messageCreationTool = (MessageCreationTool) createRequest.getNewObject(); + result = Iterables.any(messageCreationTool.getEdgeMappings(), Predicates.instanceOf(DestructionMessageMapping.class)); + } + } + return result; + } + + /** + * Checks if the current request is a creation request for a new standard + * message. + * + * @return true if the current request is a creation request for a new + * standard message + */ + public boolean isStandardMessageCreation() { + boolean result = false; + if (!isNoteAttachmentCreationRequest() && request instanceof CreateRequest) { + CreateRequest createRequest = (CreateRequest) request; + if (createRequest.getNewObject() instanceof MessageCreationTool) { + MessageCreationTool messageCreationTool = (MessageCreationTool) createRequest.getNewObject(); + result = Iterables.any(messageCreationTool.getEdgeMappings(), Predicates.instanceOf(BasicMessageMapping.class)); + } + } + return result; + } + + /** + * Request created from SequenceMessageEditPolicy. + * + * @return true if created from message; + */ + public boolean isDirectedByMessage() { + Object object = request.getExtendedData().get(SequenceMessageEditPolicy.REQUEST_FROM_SEQUENCE_MESSAGE_EDIT_POLICY); + return object instanceof Boolean && Boolean.TRUE.equals(object); + } + + public Map getExtendedData() { + return request.getExtendedData(); + } + + /** + * Checks if the current request is a creation request for a message. + * + * @return true if the current request is a creation request for message + */ + public boolean isSequenceMessageCreation() { + boolean result = false; + if (!isNoteAttachmentCreationRequest() && request instanceof CreateRequest) { + CreateRequest createRequest = (CreateRequest) request; + Object tool = createRequest.getNewObject(); + result = tool instanceof MessageCreationTool; + } + return result; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/SequenceDiagramEPQuery.java b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/SequenceDiagramEPQuery.java new file mode 100644 index 0000000000..5b993c79fc --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram.sequence.ui/src/org/eclipse/sirius/diagram/sequence/ui/tool/internal/util/SequenceDiagramEPQuery.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.sequence.ui.tool.internal.util; + +import java.util.List; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.LifelineEditPart; +import org.eclipse.sirius.diagram.sequence.ui.tool.internal.edit.part.SequenceDiagramEditPart; + +/** + * Queries on a sequence diagram. + * + * @author pcdavid + */ +public class SequenceDiagramEPQuery { + private final SequenceDiagramEditPart diagram; + + /** + * Constructor. + * + * @param diagram + * the diagram to query. + */ + public SequenceDiagramEPQuery(SequenceDiagramEditPart diagram) { + this.diagram = Preconditions.checkNotNull(diagram); + } + + public List<LifelineEditPart> getAllLifelines() { + return EditPartsHelper.getAllLifelines(diagram); + } +} |