diff options
author | Camille Letavernier | 2018-05-15 07:54:41 +0000 |
---|---|---|
committer | Nicolas FAUVERGUE | 2018-05-22 15:58:01 +0000 |
commit | cf83366837c594724208ceaec5a6a9f1a951e352 (patch) | |
tree | 309ac55606f46cef6f3d6d41558c84a9f2b4b0f1 | |
parent | 6abb6f2000f2efe7201bcdf989572fffa302a943 (diff) | |
download | org.eclipse.papyrus-cf83366837c594724208ceaec5a6a9f1a951e352.tar.gz org.eclipse.papyrus-cf83366837c594724208ceaec5a6a9f1a951e352.tar.xz org.eclipse.papyrus-cf83366837c594724208ceaec5a6a9f1a951e352.zip |
Bug 533684: [Sequence Diagram] Deletion of an InteractionOperand should
resize other InteractionOperand
https://bugs.eclipse.org/bugs/show_bug.cgi?id=533684
- Add an advice to propagate the size of an Operand to its sibling
operand before deleting it
Change-Id: I12dd0f3def082922f5ec674ac8510ee5931ab066
Signed-off-by: Camille Letavernier <cletavernier@eclipsesource.com>
2 files changed, 188 insertions, 0 deletions
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/edit/helpers/advice/InteractionOperandViewAdvice.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/edit/helpers/advice/InteractionOperandViewAdvice.java new file mode 100644 index 00000000000..f9298f97d59 --- /dev/null +++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/edit/helpers/advice/InteractionOperandViewAdvice.java @@ -0,0 +1,184 @@ +/***************************************************************************** + * Copyright (c) 2018 EclipseSource and others. + * + * 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: + * EclipseSource - Initial API and implementation + * + *****************************************************************************/ +package org.eclipse.papyrus.uml.diagram.sequence.edit.helpers.advice; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.emf.ecore.EStructuralFeature.Setting; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gmf.runtime.common.core.command.CommandResult; +import org.eclipse.gmf.runtime.common.core.command.CompositeCommand; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand; +import org.eclipse.gmf.runtime.emf.type.core.edithelper.AbstractEditHelperAdvice; +import org.eclipse.gmf.runtime.emf.type.core.requests.DestroyDependentsRequest; +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.papyrus.infra.emf.utils.EMFHelper; +import org.eclipse.papyrus.infra.gmfdiag.common.adapter.NotationAndTypeAdapter; +import org.eclipse.papyrus.uml.diagram.sequence.command.SetResizeCommand; +import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.InteractionOperandEditPart; +import org.eclipse.uml2.uml.InteractionOperand; + +/** + * <p> + * Advice for {@link InteractionOperand} View (InteractionOperand_Shape), responsible + * for resize sibling operands during deletion. + * </p> + * <p> + * This advice must be bound before the default GMF's notationDepdendents advice, + * to make sure we can propagate the size of the deleted view before it is actually + * deleted. + * </p> + */ +public class InteractionOperandViewAdvice extends AbstractEditHelperAdvice { + + @Override + protected ICommand getBeforeDestroyDependentsCommand(DestroyDependentsRequest request) { + ICommand beforeDestroyDependentsCommand = super.getBeforeDestroyDependentsCommand(request); + + if (request.getElementToDestroy() instanceof InteractionOperand) { + Set<View> operandViews = findOperandViews((InteractionOperand) request.getElementToDestroy()); + return CompositeCommand.compose(beforeDestroyDependentsCommand, + operandViews.stream() + .map(view -> getSizeCommandFor(view, request.getEditingDomain())) + .reduce(null, CompositeCommand::compose)); + } + + return beforeDestroyDependentsCommand; + } + + private Set<View> findOperandViews(InteractionOperand operand) { + return EMFHelper.getUsages(operand).stream() + .filter(setting -> setting.getEStructuralFeature() == NotationPackage.Literals.VIEW__ELEMENT) + .map(Setting::getEObject) + .filter(View.class::isInstance) + .map(View.class::cast) + .filter(view -> view.getElement() == operand) + .filter(view -> InteractionOperandEditPart.VISUAL_ID.equals(view.getType())) + .collect(Collectors.toSet()); + } + + /** + * Return the command used to propagate the size of the given <code>viewToDestroy</code> + * to one of the sibling operands (The previous one, or the next one if the destroyed view + * if the first in the CF). + * + * The actual resize commands are created on-the-fly during command execution, so + * the commands can be properly chained in case of multi-deletion. + * + * @param viewToDestroy + * @param editingDomain + * @return + */ + private ICommand getSizeCommandFor(View viewToDestroy, TransactionalEditingDomain editingDomain) { + // Do the actual work in the command (At execution time), so we can propagate size changes + // in case of multi-selection (So when deleting A, B and C, A will resize B, then B will resize C, then C will resize D) + return new AbstractTransactionalCommand(editingDomain, "Resize sibling operand", null) { + + @Override + protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { + Dimension deletedSize = getSize(viewToDestroy); + if (deletedSize == null) { + return null; + } + View viewBefore = findViewBefore(viewToDestroy); + if (viewBefore != null) { + Dimension sizeOfViewBefore = getSize(viewBefore); + sizeOfViewBefore.expand(0, deletedSize.height()); + NotationAndTypeAdapter adapter = new NotationAndTypeAdapter(viewBefore.getElement(), viewBefore); + ICommand result = new SetResizeCommand(editingDomain, "Expand previous operand", adapter, sizeOfViewBefore); + result.execute(monitor, info); + } else { + View viewAfter = findViewAfter(viewToDestroy); + if (viewAfter != null) { + Dimension sizeOfViewAfter = getSize(viewAfter); + sizeOfViewAfter.expand(0, deletedSize.height()); + NotationAndTypeAdapter adapter = new NotationAndTypeAdapter(viewAfter.getElement(), viewAfter); + ICommand result = new SetResizeCommand(editingDomain, "Expand previous operand", adapter, sizeOfViewAfter); + result.execute(monitor, info); + } + } + return CommandResult.newOKCommandResult(); + } + }; + + } + + /** + * @param view + * @return + * The sibling view immediately following the given view, or <code>null</code> if there is no such view + */ + private static View findViewAfter(View view) { + if (false == view.eContainer() instanceof View) { + return null; + } + + List<View> viewSiblings = ((View) view.eContainer()).getPersistedChildren(); + + int index = viewSiblings.indexOf(view); + int indexAfter = index + 1; + if (indexAfter < viewSiblings.size()) { + return viewSiblings.get(indexAfter); + } + return null; + } + + /** + * @param view + * @return + * The sibling view immediately preceding the given view, or <code>null</code> if there is no such view + */ + private static View findViewBefore(View view) { + if (false == view.eContainer() instanceof View) { + return null; + } + + List<View> viewSiblings = ((View) view.eContainer()).getPersistedChildren(); + + int index = viewSiblings.indexOf(view) - 1; + + if (index >= 0) { + return viewSiblings.get(index); + } + return null; + } + + /** + * @param view + * @return + * A new {@link Dimension} instance representing the model size of the {@link View}, + * or <code>null</code> if the View doesn't have a size. + */ + private Dimension getSize(View view) { + if (view instanceof Node) { + Node node = (Node) view; + LayoutConstraint constraint = node.getLayoutConstraint(); + if (constraint instanceof Size) { + Size size = (Size) constraint; + return new Dimension(size.getWidth(), size.getHeight()); + } + } + return null; + } +} diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/model/SequenceDiagram.elementtypesconfigurations b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/model/SequenceDiagram.elementtypesconfigurations index c9a64b7b7b2..bb84ce2dda9 100644 --- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/model/SequenceDiagram.elementtypesconfigurations +++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/model/SequenceDiagram.elementtypesconfigurations @@ -3,4 +3,8 @@ <adviceBindingsConfigurations xsi:type="elementtypesconfigurations:AdviceBindingConfiguration" xmi:id="_BvoRIENLEeimO7ZhVBpjkg" description="Advice for configuration of the view for the default operand of a new combined fragment." identifier="org.eclipse.papyrus.uml.diagram.sequence.edit.helpers.advice.defaultInteractionOperand" inheritance="all" editHelperAdviceClassName="org.eclipse.papyrus.uml.diagram.sequence.edit.helpers.advice.DefaultInteractionOperandAdvice"> <target xsi:type="elementtypesconfigurations:SpecializationTypeConfiguration" href="platform:/plugin/org.eclipse.papyrus.uml.service.types/model/umldi.elementtypesconfigurations#org.eclipse.papyrus.umldi.CombinedFragment_Shape"/> </adviceBindingsConfigurations> + <adviceBindingsConfigurations xsi:type="elementtypesconfigurations:AdviceBindingConfiguration" xmi:id="_MdNHsFePEeiIR4R5xrPjKA" description="" identifier="org.eclipse.papyrus.uml.diagram.sequence.edit.helpers.advice.interactionoperandviewadvice" inheritance="all" editHelperAdviceClassName="org.eclipse.papyrus.uml.diagram.sequence.edit.helpers.advice.InteractionOperandViewAdvice"> + <before xsi:type="elementtypesconfigurations:ExternallyRegisteredAdvice" href="platform:/plugin/org.eclipse.papyrus.infra.gmfdiag.common/model/gmfdiag-common.elementtypesconfigurations#_U91U4F6dEeev1-J3e2LKZA"/> + <target xsi:type="elementtypesconfigurations:SpecializationTypeConfiguration" href="platform:/plugin/org.eclipse.papyrus.uml.service.types/model/umldi.elementtypesconfigurations#org.eclipse.papyrus.umldi.InteractionOperand_Shape"/> + </adviceBindingsConfigurations> </elementtypesconfigurations:ElementTypeSetConfiguration> |