From 505dadca2a1da4555712201f81df2726d9877b22 Mon Sep 17 00:00:00 2001
From: Florian Hoelzl <hoelzl@fortiss.org>
Date: Tue, 8 Mar 2011 09:00:22 +0000
Subject: [PATCH] command stack service trial implementation

---
 .../kernel/internal/ActionService.java        |  94 +++++++
 .../kernel/internal/CommandStackService.java  | 264 +++++++++++++++++-
 .../kernel/internal/ContextMenuService.java   |  16 ++
 .../internal/navigator/NavigatorViewPart.java |  25 +-
 .../kernel/internal/navigator/NewMenu.java    |   7 +-
 .../kernel/services/IActionService.java       |  46 +++
 .../kernel/services/ICommandStackService.java |  12 +
 .../kernel/services/IContextMenuService.java  |   4 +-
 .../kernel/util/EObjectSelectionUtils.java    |  17 ++
 9 files changed, 461 insertions(+), 24 deletions(-)
 create mode 100644 org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/ActionService.java
 create mode 100644 org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/services/IActionService.java

diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/ActionService.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/ActionService.java
new file mode 100644
index 000000000..95c5fe5e1
--- /dev/null
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/ActionService.java
@@ -0,0 +1,94 @@
+/*--------------------------------------------------------------------------+
+$Id$
+|                                                                          |
+| Copyright 2011 ForTISS GmbH                     |
+|                                                                          |
+| Licensed under the Apache License, Version 2.0 (the "License");          |
+| you may not use this file except in compliance with the License.         |
+| You may obtain a copy of the License at                                  |
+|                                                                          |
+|    http://www.apache.org/licenses/LICENSE-2.0                            |
+|                                                                          |
+| Unless required by applicable law or agreed to in writing, software      |
+| distributed under the License is distributed on an "AS IS" BASIS,        |
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+| See the License for the specific language governing permissions and      |
+| limitations under the License.                                           |
++--------------------------------------------------------------------------*/
+package org.fortiss.tooling.kernel.internal;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.ActionFactory;
+import org.fortiss.tooling.kernel.services.IActionService;
+import org.fortiss.tooling.kernel.services.ICommandStackService;
+import org.fortiss.tooling.kernel.util.EObjectSelectionUtils;
+
+/**
+ * This class implements the {@link IActionService} interface.
+ * 
+ * @author hoelzlf
+ * @author $Author$
+ * @version $Rev$
+ * @levd.rating RED Rev:
+ */
+public class ActionService implements IActionService {
+
+	/** The global undo action. */
+	public final IAction globalUndoAction = new Action("Undo", PlatformUI
+			.getWorkbench().getSharedImages()
+			.getImageDescriptor(ISharedImages.IMG_TOOL_UNDO)) {
+
+		@Override
+		public void run() {
+			EObject selectedObject = EObjectSelectionUtils
+					.getCurrentSelectionFirstElement();
+			if (selectedObject != null) {
+				ICommandStackService.INSTANCE.undo(selectedObject);
+			}
+		}
+
+		@Override
+		public boolean isEnabled() {
+			EObject selectedObject = EObjectSelectionUtils
+					.getCurrentSelectionFirstElement();
+			return selectedObject != null
+					&& ICommandStackService.INSTANCE.canUndo(selectedObject);
+		}
+	};
+
+	/** {@inheritDoc} */
+	@Override
+	public void registerGlobalActions(IViewSite site) {
+		fillActionBars(site.getActionBars());
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void registerGlobalActions(IEditorSite site) {
+		fillActionBars(site.getActionBars());
+	}
+
+	/** Fills the given action bar with the global actions. */
+	private void fillActionBars(IActionBars actionBars) {
+		globalUndoAction.setId(ActionFactory.UNDO.getId());
+		globalUndoAction.setActionDefinitionId(ActionFactory.UNDO
+				.getCommandId());
+		actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(),
+				globalUndoAction);
+
+		actionBars.updateActionBars();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void update() {
+		globalUndoAction.setEnabled(globalUndoAction.isEnabled());
+	}
+}
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/CommandStackService.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/CommandStackService.java
index 01b057fcc..5c37766d1 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/CommandStackService.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/CommandStackService.java
@@ -17,15 +17,33 @@ $Id$
 +--------------------------------------------------------------------------*/
 package org.fortiss.tooling.kernel.internal;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+
+import org.conqat.ide.commons.ui.logging.LoggingUtils;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.common.notify.impl.AdapterImpl;
 import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.edit.command.ChangeCommand;
+import org.eclipse.emf.edit.domain.EditingDomain;
+import org.fortiss.tooling.kernel.ToolingKernelActivator;
 import org.fortiss.tooling.kernel.services.ICommandStackService;
 import org.unicase.ecp.model.ECPWorkspaceManager;
 import org.unicase.ecp.model.NoWorkspaceException;
 import org.unicase.ecp.model.workSpaceModel.ECPProject;
-import org.unicase.ui.common.commands.ECPCommand;
+import org.unicase.ecp.model.workSpaceModel.ECPProjectListener;
+import org.unicase.ecp.model.workSpaceModel.ECPWorkspace;
+import org.unicase.ecp.model.workSpaceModel.WorkSpaceModelPackage;
+import org.unicase.workspace.ProjectSpace;
 
 /**
- * This class implements the {@link ICommandStackService} interface.
+ * This class implements the {@link ICommandStackService} interface. We have to
+ * mimic the command stack behavior, because EMFStore does not support redo of
+ * commands. Once this capability is available, this service class could be
+ * simplified.
  * 
  * @author hoelzl
  * @author $Author$
@@ -34,22 +52,246 @@ import org.unicase.ui.common.commands.ECPCommand;
  */
 public class CommandStackService implements ICommandStackService {
 
+	/** Stores the workspace listener. */
+	private AdapterImpl workspaceListener;
+
+	/** Stores the EmfStore workspace. */
+	private ECPWorkspace workspace;
+
+	/** Stores the project command stacks. */
+	private Map<ECPProject, CommandStackInfo> commandStackMap = new HashMap<ECPProject, CommandStackInfo>();
+
+	/** Constructor. */
+	public CommandStackService() {
+		try {
+			workspace = ECPWorkspaceManager.getInstance().getWorkSpace();
+		} catch (NoWorkspaceException e) {
+			LoggingUtils.log(ToolingKernelActivator.getDefault(),
+					e.getMessage(), IStatus.ERROR, e);
+			return;
+		}
+
+		createWorkspaceListener();
+		workspace.eAdapters().add(workspaceListener);
+
+		for (ECPProject project : workspace.getProjects()) {
+			addCommandStackInfo(project);
+		}
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void runAsCommand(EObject target, final Runnable runner) {
+		commandStackMap.get(getECPProject(target)).execute(target, runner);
+	}
+
 	/** {@inheritDoc} */
 	@Override
-	public void runAsCommand(final EObject target, final Runnable runner) {
-		ECPProject project;
+	public boolean canUndo(EObject target) {
+		return commandStackMap.get(getECPProject(target)).canUndo();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean canRedo(EObject target) {
+		return commandStackMap.get(getECPProject(target)).canRedo();
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void undo(EObject target) {
+		if (canUndo(target)) {
+			commandStackMap.get(getECPProject(target)).undo();
+		}
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void redo(EObject target) {
+		if (canRedo(target)) {
+			commandStackMap.get(getECPProject(target)).redo();
+		}
+	}
+
+	/**
+	 * Creates the workspace listener, which registers the project listener on
+	 * added projects. It also unregisters the project listener from removed
+	 * projects.
+	 */
+	private void createWorkspaceListener() {
+		workspaceListener = new AdapterImpl() {
+
+			@Override
+			public void notifyChanged(Notification msg) {
+				if ((msg.getFeatureID(ECPWorkspace.class)) == WorkSpaceModelPackage.ECP_WORKSPACE__PROJECTS) {
+					if (msg.getEventType() == Notification.ADD
+							&& WorkSpaceModelPackage.eINSTANCE.getECPProject()
+									.isInstance(msg.getNewValue())) {
+						addCommandStackInfo((ECPProject) msg.getNewValue());
+					} else if (msg.getEventType() == Notification.REMOVE
+							&& WorkSpaceModelPackage.eINSTANCE.getECPProject()
+									.isInstance(msg.getOldValue())) {
+						removeCommandStackInfo((ECPProject) msg.getOldValue());
+					}
+				}
+				super.notifyChanged(msg);
+			}
+		};
+	}
+
+	/**
+	 * Returns the model element's ECP project or <code>null</code> .
+	 */
+	private ECPProject getECPProject(EObject target) {
 		try {
-			project = ECPWorkspaceManager.getInstance().getWorkSpace()
+			return ECPWorkspaceManager.getInstance().getWorkSpace()
 					.getProject(target);
-		} catch (NoWorkspaceException e) {
-			return;
+		} catch (Exception e) {
+			LoggingUtils.error(ToolingKernelActivator.getDefault(),
+					"Unable to find ECP project!", e);
 		}
-		new ECPCommand(project.getRootObject()) {
+		return null;
+	}
+
+	/**
+	 * Stores project-specific information about the command stack of an
+	 * EMFStore proejct.
+	 */
+	private class CommandStackInfo {
+
+		/**
+		 * Stores the initial size of the operations list, which may be greater
+		 * than zero. This happens when uncommitted changes are in a project,
+		 * while the workbench is restarted, i.e., the command stack service
+		 * forgets, while the EMFStore workspace does not.
+		 */
+		private int initialOperationListSize = 0;
+
+		/** Stores the project undo stack. */
+		private final Stack<Command> undoStack = new Stack<Command>();
+
+		/** Stores the roject redo stack. */
+		private final Stack<Command> redoStack = new Stack<Command>();
+
+		/** Stores the ECP project. */
+		private final ECPProject ecpProject;
+
+		/** Stores the workspace project space. */
+		private ProjectSpace projectSpace;
+
+		/** Stores the editing domain. */
+		private EditingDomain editingDomain;
+
+		/** Stores the current command execution flag. */
+		private boolean inExecution = false;
+
+		/** Stores the project listener. */
+		private ECPProjectListener projectListener = new ECPProjectListener() {
+
+			@Override
+			public void projectDeleted() {
+				CommandStackService.this.removeCommandStackInfo(ecpProject);
+			}
+
+			@Override
+			public void projectChanged() {
+				updateCommandStackInfo();
+			}
 
 			@Override
-			protected void doRun() {
-				runner.run();
+			public void modelelementDeleted(EObject eobject) {
+				// ignore for now
 			}
-		}.run(false);
+		};
+
+		/** Constructor. */
+		public CommandStackInfo(ECPProject project) {
+			this.ecpProject = project;
+			this.projectSpace = (ProjectSpace) project.getRootObject();
+			project.addECPProjectListener(projectListener);
+			this.initialOperationListSize = projectSpace.getOperations().size();
+			this.editingDomain = project.getEditingDomain();
+		}
+
+		/** Perform redo. */
+		public void redo() {
+			Command cmd = redoStack.pop();
+			executeOnEMFStore(cmd);
+			undoStack.push(cmd);
+		}
+
+		/** Perform undo. */
+		public void undo() {
+			Command cmd = undoStack.pop();
+			projectSpace.undoLastOperation();
+			redoStack.push(cmd);
+		}
+
+		/** Returns whether undo is possible. */
+		public boolean canUndo() {
+			return !undoStack.isEmpty();
+		}
+
+		/** Returns whether redo is possible. */
+		public boolean canRedo() {
+			return !redoStack.isEmpty();
+		}
+
+		/** Executes the given command using the target's command stack. */
+		public void execute(EObject target, final Runnable runner) {
+			// sanity check
+			if (getECPProject(target) == ecpProject) {
+				ChangeCommand cmd = new ChangeCommand(target) {
+					@Override
+					protected void doExecute() {
+						CommandStackInfo.this.inExecution = true;
+						runner.run();
+						CommandStackInfo.this.inExecution = false;
+					}
+				};
+				executeOnEMFStore(cmd);
+				undoStack.push(cmd);
+				redoStack.clear();
+			}
+		}
+
+		/** Native command execution on EMFStore editing domain. */
+		private void executeOnEMFStore(Command cmd) {
+			editingDomain.getCommandStack().execute(cmd);
+		}
+
+		/** React to project changes and update the command stack. */
+		private void updateCommandStackInfo() {
+			int currentOperationListSize = projectSpace.getOperations().size();
+			if (currentOperationListSize == undoStack.size()
+					+ initialOperationListSize - 1) {
+				System.out.println("Und occured.");
+				redoStack.push(undoStack.pop());
+			} else if (currentOperationListSize == undoStack.size()
+					+ initialOperationListSize) {
+				System.out
+						.println("A push to the undo stack occured. Resetting redo stack. inExecution = "
+								+ inExecution);
+				redoStack.empty();
+			} else {
+				System.out
+						.println("Unknown project change received: undoStackSize = "
+								+ undoStack.size()
+								+ "; redoStackSize = "
+								+ redoStack.size()
+								+ "; operationsListSize = "
+								+ currentOperationListSize);
+			}
+		}
+	}
+
+	/** Adds the command stack for the given project. */
+	private void addCommandStackInfo(ECPProject project) {
+		commandStackMap.put(project, new CommandStackInfo(project));
+	}
+
+	/** Removes the command stack for the given project. */
+	private void removeCommandStackInfo(ECPProject project) {
+		commandStackMap.remove(project);
 	}
 }
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/ContextMenuService.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/ContextMenuService.java
index d9945d0b0..b4a708132 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/ContextMenuService.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/ContextMenuService.java
@@ -17,6 +17,9 @@ $Id$
 +--------------------------------------------------------------------------*/
 package org.fortiss.tooling.kernel.internal;
 
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.fortiss.tooling.kernel.services.IActionService;
 import org.fortiss.tooling.kernel.services.IContextMenuService;
 
 /**
@@ -29,4 +32,17 @@ import org.fortiss.tooling.kernel.services.IContextMenuService;
  */
 public class ContextMenuService implements IContextMenuService {
 
+	/** The group id of the global default action group. */
+	public static final String GROUP_GLOBAL_DEFAULTS = "globalDefaults";
+
+	/** {@inheritDoc} */
+	@Override
+	public void addGlobalDefaultActionSection(MenuManager menuManager) {
+		Separator globalDefaults = new Separator(GROUP_GLOBAL_DEFAULTS);
+		globalDefaults.setVisible(true);
+		menuManager.add(globalDefaults);
+
+		menuManager.appendToGroup(GROUP_GLOBAL_DEFAULTS,
+				((ActionService) IActionService.INSTANCE).globalUndoAction);
+	}
 }
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/navigator/NavigatorViewPart.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/navigator/NavigatorViewPart.java
index 27cfc1351..cc106d17c 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/navigator/NavigatorViewPart.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/navigator/NavigatorViewPart.java
@@ -47,6 +47,8 @@ import org.eclipse.ui.part.ViewPart;
 import org.eclipse.ui.progress.UIJob;
 import org.fortiss.tooling.kernel.ToolingKernelActivator;
 import org.fortiss.tooling.kernel.internal.NavigatorService;
+import org.fortiss.tooling.kernel.services.IActionService;
+import org.fortiss.tooling.kernel.services.IContextMenuService;
 import org.fortiss.tooling.kernel.services.IEditorService;
 import org.fortiss.tooling.kernel.services.INavigatorService;
 import org.fortiss.tooling.kernel.util.EObjectSelectionUtils;
@@ -186,6 +188,8 @@ public final class NavigatorViewPart extends ViewPart implements
 
 		createContextMenu();
 
+		IActionService.INSTANCE.registerGlobalActions(getViewSite());
+
 		PlatformUI.getWorkbench().getActiveWorkbenchWindow()
 				.getSelectionService().addSelectionListener(this);
 		getSite().setSelectionProvider(viewer);
@@ -205,6 +209,8 @@ public final class NavigatorViewPart extends ViewPart implements
 		repositorySection.setVisible(true);
 		menuManager.add(repositorySection);
 
+		IContextMenuService.INSTANCE.addGlobalDefaultActionSection(menuManager);
+
 		Separator additions = new Separator(
 				IWorkbenchActionConstants.MB_ADDITIONS);
 		additions.setVisible(true);
@@ -228,19 +234,26 @@ public final class NavigatorViewPart extends ViewPart implements
 	/** {@inheritDoc} */
 	@Override
 	public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+		IActionService.INSTANCE.update();
 		// TODO (FH): implement link with editor feature here
 	}
 
 	/** {@inheritDoc} */
 	@Override
 	public void selectionChanged(SelectionChangedEvent event) {
-		if (event.getSelection() instanceof IStructuredSelection) {
-			IStructuredSelection selection = (IStructuredSelection) event
-					.getSelection();
-			Object obj = selection.getFirstElement();
-			// FIXME: Whats that good for?
-			// setActiveECPProject(obj);
+		EObject selectedElement = EObjectSelectionUtils
+				.getCurrentSelectionFirstElement();
+		if (selectedElement != null) {
+			IActionService.INSTANCE.update();
 		}
+
+		// if (event.getSelection() instanceof IStructuredSelection) {
+		// IStructuredSelection selection = (IStructuredSelection) event
+		// .getSelection();
+		// Object obj = selection.getFirstElement();
+		// FIXME: Whats that good for?
+		// setActiveECPProject(obj);
+		// }
 	}
 
 	/** Sets the active ECPProject element. */
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/navigator/NewMenu.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/navigator/NewMenu.java
index 54cab87af..583819daf 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/navigator/NewMenu.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/internal/navigator/NewMenu.java
@@ -21,8 +21,6 @@ import org.eclipse.emf.ecore.EObject;
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.ActionContributionItem;
 import org.eclipse.jface.action.IContributionItem;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.actions.CompoundContributionItem;
 import org.fortiss.tooling.kernel.interfaces.IPrototypeProvider;
 import org.fortiss.tooling.kernel.services.ICommandStackService;
@@ -62,11 +60,8 @@ public class NewMenu extends CompoundContributionItem {
 	/** {@inheritDoc} */
 	@Override
 	protected IContributionItem[] getContributionItems() {
-		ISelection selection = PlatformUI.getWorkbench()
-				.getActiveWorkbenchWindow().getSelectionService()
-				.getSelection();
 		EObject selectedObject = EObjectSelectionUtils
-				.getFirstElement(selection);
+				.getCurrentSelectionFirstElement();
 
 		List<IContributionItem> contributionItems = new ArrayList<IContributionItem>();
 		if (selectedObject != null) {
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/services/IActionService.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/services/IActionService.java
new file mode 100644
index 000000000..90cf40fa9
--- /dev/null
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/services/IActionService.java
@@ -0,0 +1,46 @@
+/*--------------------------------------------------------------------------+
+$Id$
+|                                                                          |
+| Copyright 2011 ForTISS GmbH                                              |
+|                                                                          |
+| Licensed under the Apache License, Version 2.0 (the "License");          |
+| you may not use this file except in compliance with the License.         |
+| You may obtain a copy of the License at                                  |
+|                                                                          |
+|    http://www.apache.org/licenses/LICENSE-2.0                            |
+|                                                                          |
+| Unless required by applicable law or agreed to in writing, software      |
+| distributed under the License is distributed on an "AS IS" BASIS,        |
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+| See the License for the specific language governing permissions and      |
+| limitations under the License.                                           |
++--------------------------------------------------------------------------*/
+package org.fortiss.tooling.kernel.services;
+
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IViewSite;
+import org.fortiss.tooling.kernel.internal.ActionService;
+
+/**
+ * The action service manages registration and execution of actions including
+ * global actions like undo and redo.
+ * 
+ * @author hoelzl
+ * @author $Author$
+ * @version $Rev$
+ * @levd.rating RED Rev:
+ */
+public interface IActionService {
+
+	/** Returns the singleton instance of the service. */
+	public static final IActionService INSTANCE = new ActionService();
+
+	/** Registers global actions with a view site. */
+	void registerGlobalActions(IViewSite site);
+
+	/** Registers global actions with an editor site. */
+	void registerGlobalActions(IEditorSite site);
+
+	/** Updates the enabled state of the global actions. */
+	void update();
+}
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/services/ICommandStackService.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/services/ICommandStackService.java
index 4301b8191..e4b941dcb 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/services/ICommandStackService.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/services/ICommandStackService.java
@@ -36,4 +36,16 @@ public interface ICommandStackService {
 
 	/** Runs the given Runnable within a emfStore transaction. */
 	void runAsCommand(EObject target, Runnable runner);
+
+	/** Checks if an undo action is possible. */
+	boolean canUndo(EObject target);
+
+	/** Executes the undo action. */
+	void undo(EObject target);
+
+	/** Checks if a redo action is possible. */
+	boolean canRedo(EObject target);
+
+	/** Executes the redo action. */
+	void redo(EObject target);
 }
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/services/IContextMenuService.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/services/IContextMenuService.java
index e5a94923e..c4aff7d48 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/services/IContextMenuService.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/services/IContextMenuService.java
@@ -17,6 +17,7 @@ $Id$
 +--------------------------------------------------------------------------*/
 package org.fortiss.tooling.kernel.services;
 
+import org.eclipse.jface.action.MenuManager;
 import org.fortiss.tooling.kernel.internal.ContextMenuService;
 
 /**
@@ -33,5 +34,6 @@ public interface IContextMenuService {
 	/** Returns the singleton instance of the service. */
 	public static final IContextMenuService INSTANCE = new ContextMenuService();
 
-	// TODO (FH): define
+	/** Adds the global default actions like undo to the given context menu. */
+	void addGlobalDefaultActionSection(MenuManager menuManager);
 }
diff --git a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/util/EObjectSelectionUtils.java b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/util/EObjectSelectionUtils.java
index 04cd15ce4..146f1a58b 100644
--- a/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/util/EObjectSelectionUtils.java
+++ b/org.fortiss.tooling.kernel/trunk/src/org/fortiss/tooling/kernel/util/EObjectSelectionUtils.java
@@ -20,6 +20,7 @@ package org.fortiss.tooling.kernel.util;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.PlatformUI;
 
 /**
  * Utility methods for {@link EObject} selections.
@@ -31,6 +32,14 @@ import org.eclipse.jface.viewers.IStructuredSelection;
  */
 public final class EObjectSelectionUtils {
 
+	/**
+	 * Returns the first selected element if it is an EObject or
+	 * <code>null</code> otherwise.
+	 */
+	public static EObject getCurrentSelectionFirstElement() {
+		return getFirstElement(getCurrentSelection());
+	}
+
 	/**
 	 * Returns the first selected EObject or <code>null</code> if no EObject is
 	 * selected.
@@ -44,4 +53,12 @@ public final class EObjectSelectionUtils {
 		}
 		return null;
 	}
+
+	/**
+	 * Returns the current selection obtained from the platform.
+	 */
+	public static ISelection getCurrentSelection() {
+		return PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+				.getSelectionService().getSelection();
+	}
 }
-- 
GitLab