Jelajahi Sumber

#49: Implemented sub diagram aware copy command. Adjusted pasting process to allow for pasting diagrams as root resource contents.

Thomas Kutz 9 tahun lalu
induk
melakukan
f51f57e3df

File diff ditekan karena terlalu besar
+ 3 - 1
manual-tests/org.yakindu.sct.test.manual/testcases/sct_testcase_12_refactor.textile


+ 1 - 1
plugins/org.yakindu.sct.ui.editor/plugin.xml

@@ -477,7 +477,7 @@
             </ElementType>
          </ViewId>
       </GlobalActionHandlerProvider>
-      <GlobalActionHandlerProvider class="org.eclipse.gmf.runtime.diagram.ui.providers.DiagramGlobalActionHandlerProvider" id="global-actions">
+      <GlobalActionHandlerProvider class="org.yakindu.sct.ui.editor.providers.StatechartDiagramGlobalActionHandlerProvider" id="global-actions">
          <Priority name="Lowest">
          </Priority>
          <ViewId id="org.yakindu.sct.ui.editor.editor.StatechartDiagramEditor">

+ 32 - 0
plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/clipboardsupport/DiagramPasteOperation.java

@@ -0,0 +1,32 @@
+package org.yakindu.sct.ui.editor.clipboardsupport;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.gmf.runtime.emf.clipboard.core.OverridePasteChildOperation;
+import org.eclipse.gmf.runtime.emf.clipboard.core.PasteChildOperation;
+import org.eclipse.gmf.runtime.emf.clipboard.core.PasteTarget;
+
+public class DiagramPasteOperation extends OverridePasteChildOperation {
+
+	public DiagramPasteOperation(PasteChildOperation overriddenChildPasteOperation) {
+		super(overriddenChildPasteOperation);
+	}
+	
+	/**
+	 * We want sub diagrams to be directly pasted into resources
+	 */
+	@Override
+	public PasteTarget getParentTarget() {
+		PasteTarget parentTarget = super.getParentTarget();
+		if (parentTarget.getObject() instanceof EObject) {
+			EObject eObject = (EObject) parentTarget.getObject();
+			return new PasteTarget(eObject.eResource());
+		}
+		return parentTarget;
+	}
+	
+	@Override
+	public void paste() throws Exception {
+		doPasteInto(getParentTarget());
+	}
+
+}

+ 8 - 7
plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/clipboardsupport/NotationClipboardOperationHelper.java

@@ -63,6 +63,8 @@ import org.yakindu.sct.model.sgraph.Vertex;
  * Because this is done in a static method we couldn't inherit from the original
  * NotationClipboardOperationHelper.
  * 
+ * We made also adjustments here to support copying of a state's sub diagram.
+ * 
  * @author muehlbrandt
  */
 @SuppressWarnings("all")
@@ -218,15 +220,14 @@ public class NotationClipboardOperationHelper extends AbstractClipboardSupport {
 				org.eclipse.gmf.runtime.notation.Node node = (org.eclipse.gmf.runtime.notation.Node) eObject;
 				EObject element = node.getElement();
 				if ((element != null)) {
-					return new PositionalGeneralViewPasteOperation(
-							overriddenChildPasteOperation, true);
+					return new PositionalGeneralViewPasteOperation(overriddenChildPasteOperation, true);
 				} else {
-					return new PositionalGeneralViewPasteOperation(
-							overriddenChildPasteOperation, false);
+					return new PositionalGeneralViewPasteOperation(overriddenChildPasteOperation, false);
 				}
 			} else if (eObject instanceof Edge) {
-				return new ConnectorViewPasteOperation(
-						overriddenChildPasteOperation);
+				return new ConnectorViewPasteOperation(overriddenChildPasteOperation);
+			} else if (eObject instanceof Diagram) {
+				return new DiagramPasteOperation(overriddenChildPasteOperation);
 			}
 		}
 		return null;
@@ -283,7 +284,7 @@ public class NotationClipboardOperationHelper extends AbstractClipboardSupport {
 		}
 		return true;
 	}
-	
+
 	/**
 	 * This is a quick fix to remove outgoing transitions of copied vertices if
 	 * they have no target.

+ 80 - 0
plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/providers/StatechartDiagramGlobalActionHandler.java

@@ -0,0 +1,80 @@
+package org.yakindu.sct.ui.editor.providers;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.edit.domain.EditingDomain;
+import org.eclipse.emf.edit.domain.IEditingDomainProvider;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.gmf.runtime.common.core.command.ICommand;
+import org.eclipse.gmf.runtime.common.ui.services.action.global.IGlobalActionContext;
+import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart;
+import org.eclipse.gmf.runtime.diagram.ui.providers.DiagramGlobalActionHandler;
+
+/**
+ * The purpose of this subclass is to provide our own copy command
+ * {@link org.yakindu.sct.ui.editor.providers.SubdiagramAwareCopyCommand.SubdiagramAwareCopyCommand(TransactionalEditingDomain,
+ * String, View, List)} to allow for copying states together with their sub diagrams.
+ * 
+ * @author Thomas Kutz
+ *
+ */
+public class StatechartDiagramGlobalActionHandler extends DiagramGlobalActionHandler {
+
+	@Override
+	protected ICommand getCopyCommand(IGlobalActionContext cntxt, IDiagramWorkbenchPart diagramPart,
+			final boolean isUndoable) {
+
+		TransactionalEditingDomain editingDomain = getEditingDomain(diagramPart);
+
+		if (editingDomain == null) {
+			return null;
+		}
+
+		// return own copy command to also copy sub diagrams for copied states
+		return new SubdiagramAwareCopyCommand(editingDomain, cntxt.getLabel(), diagramPart.getDiagram(),
+				getSelectedViews(cntxt.getSelection())) {
+
+			public boolean canUndo() {
+				return isUndoable;
+			}
+
+			public boolean canRedo() {
+				return isUndoable;
+			}
+
+			protected IStatus doUndo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
+				if (isUndoable) {
+					return Status.OK_STATUS;
+				}
+				return super.doUndo(monitor, info);
+			}
+
+			protected IStatus doRedo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
+				if (isUndoable) {
+					return Status.OK_STATUS;
+				}
+				return super.doRedo(monitor, info);
+			}
+		};
+	}
+
+	/**
+	 * Copied from
+	 * DiagramGlobalActionHandler.getEditingDomain(IDiagramWorkbenchPart) because it is private.
+	 */
+	private TransactionalEditingDomain getEditingDomain(IDiagramWorkbenchPart part) {
+		TransactionalEditingDomain result = null;
+		IEditingDomainProvider provider = (IEditingDomainProvider) part.getAdapter(IEditingDomainProvider.class);
+		if (provider != null) {
+			EditingDomain domain = provider.getEditingDomain();
+
+			if (domain != null && domain instanceof TransactionalEditingDomain) {
+				result = (TransactionalEditingDomain) domain;
+			}
+		}
+		return result;
+	}
+}

+ 125 - 0
plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/providers/StatechartDiagramGlobalActionHandlerProvider.java

@@ -0,0 +1,125 @@
+package org.yakindu.sct.ui.editor.providers;
+
+import java.util.Hashtable;
+
+import org.eclipse.gmf.runtime.common.ui.services.action.global.AbstractGlobalActionHandlerProvider;
+import org.eclipse.gmf.runtime.common.ui.services.action.global.IGlobalActionHandler;
+import org.eclipse.gmf.runtime.common.ui.services.action.global.IGlobalActionHandlerContext;
+import org.eclipse.gmf.runtime.diagram.ui.providers.DiagramGlobalActionHandler;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * This is a copy of
+ * {@link org.eclipse.gmf.runtime.diagram.ui.providers.DiagramGlobalActionHandlerProvider}.
+ * 
+ * We had to copy the original class because it is final, so we cannot subclass
+ * it.
+ * 
+ * The only purpose of this copy is to return
+ * {@link org.yakindu.sct.ui.editor.providers.StatechartDiagramGlobalActionHandler}
+ * which returns our own copy command
+ * {@link org.yakindu.sct.ui.editor.providers.SubdiagramAwareCopyCommand} to
+ * copy sub diagrams when states get copied.
+ * 
+ * @author kutz
+ *
+ */
+@SuppressWarnings("rawtypes")
+public class StatechartDiagramGlobalActionHandlerProvider extends AbstractGlobalActionHandlerProvider {
+
+	/**
+	 * List that contains all the IGlobalActionHandlers mapped to the
+	 * IWorkbenchParts
+	 */
+	private Hashtable handlerList = new Hashtable();
+
+	/**
+	 * Constructor for DiagramGlobalActionHandlerProvider.
+	 */
+	public StatechartDiagramGlobalActionHandlerProvider() {
+		super();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.gmf.runtime.common.ui.services.action.global.IGlobalActionHandlerProvider#getGlobalActionHandler(org.eclipse.gmf.runtime.common.ui.services.action.global.IGlobalActionHandlerContext)
+	 */
+	@SuppressWarnings("unchecked")
+	public IGlobalActionHandler getGlobalActionHandler(
+			final IGlobalActionHandlerContext context) {
+		/* create the handler */
+		if (!getHandlerList().containsKey(context.getActivePart())) {
+			
+			// PATCH START
+			getHandlerList().put(context.getActivePart(),
+				new StatechartDiagramGlobalActionHandler());
+			// PATCH END
+			
+			/*
+			 * register as a part listener so that the cache can be cleared when
+			 * the part is disposed
+			 */
+			context.getActivePart().getSite().getPage().addPartListener(
+				new IPartListener() {
+
+					private IWorkbenchPart localPart = context.getActivePart();
+
+					/**
+					 * @see org.eclipse.ui.IPartListener#partActivated(IWorkbenchPart)
+					 */
+					public void partActivated(IWorkbenchPart part) {
+						// NULL implementation
+					}
+
+					/**
+					 * @see org.eclipse.ui.IPartListener#partBroughtToTop(IWorkbenchPart)
+					 */
+					public void partBroughtToTop(IWorkbenchPart part) {
+						// NULL implementation
+					}
+
+					/**
+					 * @see org.eclipse.ui.IPartListener#partClosed(IWorkbenchPart)
+					 */
+					public void partClosed(IWorkbenchPart part) {
+						/* remove the cache associated with the part */
+						if (part != null && part == localPart
+							&& getHandlerList().containsKey(part)) {
+							getHandlerList().remove(part);
+							localPart.getSite().getPage().removePartListener(
+								this);
+							localPart = null;
+						}
+					}
+
+					/**
+					 * @see org.eclipse.ui.IPartListener#partDeactivated(IWorkbenchPart)
+					 */
+					public void partDeactivated(IWorkbenchPart part) {
+						// NULL implementation
+					}
+
+					/**
+					 * @see org.eclipse.ui.IPartListener#partOpened(IWorkbenchPart)
+					 */
+					public void partOpened(IWorkbenchPart part) {
+						// NULL implementation
+					}
+				});
+		}
+		return (DiagramGlobalActionHandler) getHandlerList().get(
+			context.getActivePart());
+	}
+
+	/**
+	 * Returns the handlerList.
+	 * 
+	 * @return Hashtable
+	 */
+	private Hashtable getHandlerList() {
+		return handlerList;
+	}
+	
+}

+ 171 - 0
plugins/org.yakindu.sct.ui.editor/src/org/yakindu/sct/ui/editor/providers/SubdiagramAwareCopyCommand.java

@@ -0,0 +1,171 @@
+package org.yakindu.sct.ui.editor.providers;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.emf.common.util.TreeIterator;
+import org.eclipse.emf.ecore.EAnnotation;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EcoreFactory;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.gmf.runtime.common.core.command.ICommand;
+import org.eclipse.gmf.runtime.common.ui.action.actions.global.ClipboardManager;
+import org.eclipse.gmf.runtime.common.ui.util.CustomData;
+import org.eclipse.gmf.runtime.common.ui.util.CustomDataTransfer;
+import org.eclipse.gmf.runtime.common.ui.util.ICustomData;
+import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil;
+import org.eclipse.gmf.runtime.diagram.ui.internal.commands.CopyCommand;
+import org.eclipse.gmf.runtime.emf.clipboard.core.ClipboardUtil;
+import org.eclipse.gmf.runtime.notation.Diagram;
+import org.eclipse.gmf.runtime.notation.Edge;
+import org.eclipse.gmf.runtime.notation.View;
+import org.yakindu.sct.model.sgraph.State;
+import org.yakindu.sct.ui.editor.partitioning.DiagramPartitioningUtil;
+
+/**
+ * Copy command that also copies the corresponding sub diagram for a state, if existing.
+ * 
+ * @author kutz
+ *
+ */
+@SuppressWarnings({"restriction", "rawtypes"})
+public class SubdiagramAwareCopyCommand extends CopyCommand implements ICommand {
+
+	public SubdiagramAwareCopyCommand(TransactionalEditingDomain editingDomain, String label, View viewContext,
+			List source) {
+		super(editingDomain, label, viewContext, source);
+	}
+
+	@Override
+	protected void copyToClipboard(List source) {
+		/* Check if the source has elements */
+		if (source == null || source.size() == 0) {
+			return;
+		}
+		CustomData data = copyViews(source);
+		addToClipboardManager(data);
+	}
+
+	protected void addToClipboardManager(CustomData data) {
+		if (data != null) {
+			ClipboardManager.getInstance().addToCache(new ICustomData[]{data}, CustomDataTransfer.getInstance());
+		}
+	}
+
+	protected CustomData copyViews(List<EObject> source) {
+		String copy = copyWithSubdiagrams(source);
+		return new CustomData(DRAWING_SURFACE, copy.getBytes());
+	}
+
+	/**
+	 * This is basically a copy of
+	 * {@link org.eclipse.gmf.runtime.diagram.ui.internal.commands.ClipboardCommand.copyViewsToString(List)}
+	 * which is static and therefore not exchangeable. Only difference is adding of sub diagrams.
+	 */
+	protected String copyWithSubdiagrams(List<EObject> views) {
+		Assert.isNotNull(views);
+		Assert.isTrue(views.size() > 0);
+
+		List<EObject> selection = new ArrayList<EObject>();
+		Iterator iter = views.iterator();
+		while (iter.hasNext()) {
+			EObject viewElement = (View) iter.next();
+			if (viewElement != null) {
+				selection.add(viewElement);
+			}
+		}
+
+		/*
+		 * We must append all inner edges of a node being copied. Edges are
+		 * non-containment references, hence they won't be copied for free.
+		 * Therefore, we add them here to the list of views to copy.
+		 */
+		selection.addAll(getInnerEdges(views));
+
+		// add the measurement unit in an annotation. Put it in the last
+		// position
+		// to work around a limitation in the copy/paste infrastructure, that
+		// selects the ClipboardSupportFactory based on the first element in
+		// the copy list. If the annotation is first, then we get the wrong
+		// clipboard support instance
+		selection.add(getMeasurementUnitAnnotation(views));
+
+		// PATCH START
+		// add all sub diagrams of selected states
+		selection.addAll(getSubDiagrams(views));
+		// PATCH END
+
+		/* Copy the selection to the string */
+		return ClipboardUtil.copyElementsToString(selection, new HashMap(), new NullProgressMonitor());
+	}
+
+	protected EAnnotation getMeasurementUnitAnnotation(List<EObject> views) {
+		View firstView = (View) views.get(0);
+		Diagram dgrm = firstView.getDiagram();
+		EAnnotation measureUnitAnnotation = EcoreFactory.eINSTANCE.createEAnnotation();
+		measureUnitAnnotation.setSource(dgrm.getMeasurementUnit().getName());
+		return measureUnitAnnotation;
+	}
+
+	protected List<Edge> getInnerEdges(List views) {
+		List<Edge> innerEdges = new LinkedList<Edge>();
+		for (Iterator itr = views.iterator(); itr.hasNext();) {
+			View view = (View) itr.next();
+			if (!(view instanceof Diagram)) {
+				innerEdges.addAll(ViewUtil.getAllInnerEdges(view));
+			}
+		}
+		return innerEdges;
+	}
+
+	protected List<Diagram> getSubDiagrams(List views) {
+		List<Diagram> subDiagrams = new ArrayList<Diagram>();
+		Iterator iter = views.iterator();
+		while (iter.hasNext()) {
+			View viewElement = (View) iter.next();
+			if (viewElement != null) {
+				EObject semanticElement = viewElement.getElement();
+				if (semanticElement != null && semanticElement instanceof State) {
+					State semanticState = (State) semanticElement;
+					if (semanticState.isComposite()) {
+						subDiagrams.addAll(getAllSubDiagrams(semanticState));
+					}
+				}
+			}
+		}
+		return subDiagrams;
+	}
+
+	protected Collection<? extends Diagram> getAllSubDiagrams(State semanticState) {
+		List<Diagram> subDiagrams = new ArrayList<>();
+		addSubDiagram(semanticState, subDiagrams);
+
+		TreeIterator<EObject> iter = semanticState.eAllContents();
+		while (iter.hasNext()) {
+			EObject next = iter.next();
+			if (next instanceof State) {
+				State subState = (State) next;
+				if (subState.isComposite()) {
+					addSubDiagram(subState, subDiagrams);
+				} else {
+					iter.prune();
+				}
+			}
+		}
+		return subDiagrams;
+	}
+
+	protected void addSubDiagram(State semanticState, List<Diagram> subDiagrams) {
+		Diagram subDiagram = DiagramPartitioningUtil.getSubDiagram(semanticState);
+		if (subDiagram != null) {
+			subDiagrams.add(subDiagram);
+		}
+	}
+
+}