| /****************************************************************************** |
| * Copyright (c) 2002, 2009 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| * Nicolas Rouquette (NASA) - Fix for Bug 260812. |
| ****************************************************************************/ |
| |
| package org.eclipse.gmf.runtime.diagram.ui.parts; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.EventObject; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.commands.operations.IOperationHistory; |
| import org.eclipse.core.commands.operations.IOperationHistoryListener; |
| import org.eclipse.core.commands.operations.IUndoContext; |
| import org.eclipse.core.commands.operations.IUndoableOperation; |
| import org.eclipse.core.commands.operations.ObjectUndoContext; |
| import org.eclipse.core.commands.operations.OperationHistoryEvent; |
| import org.eclipse.core.commands.operations.OperationHistoryFactory; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.gef.commands.Command; |
| import org.eclipse.gef.commands.CommandStack; |
| import org.eclipse.gef.commands.CommandStackListener; |
| import org.eclipse.gef.commands.CompoundCommand; |
| 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.common.core.command.ICompositeCommand; |
| import org.eclipse.gmf.runtime.common.core.util.Log; |
| import org.eclipse.gmf.runtime.common.core.util.Trace; |
| 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.internal.DiagramUIDebugOptions; |
| import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIPlugin; |
| import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIStatusCodes; |
| import org.eclipse.gmf.runtime.diagram.ui.internal.tools.ConnectionHandleTool; |
| |
| /** |
| * GEF command stack that delegates to an {@link IOperationHistory}. |
| * |
| * @author sshaw |
| * @author Tauseef A, Israr |
| * @author ldamus |
| */ |
| public class DiagramCommandStack |
| extends CommandStack { |
| |
| private Map stackToManager = new HashMap(); |
| |
| private IDiagramEditDomain editDomain; |
| |
| private Command cmdRecent = null; |
| |
| private IOperationHistory delegate; |
| |
| private IUndoContext undoContext; |
| |
| private final class HistoryEventObject |
| extends EventObject { |
| |
| private final OperationHistoryEvent event; |
| |
| private HistoryEventObject(OperationHistoryEvent event) { |
| super(event.getHistory()); |
| this.event = event; |
| } |
| |
| /** |
| * Gets my operation history event. |
| * |
| * @return my operation history event. |
| */ |
| public OperationHistoryEvent getOperationHistoryEvent() { |
| return event; |
| } |
| } |
| |
| /** |
| * Initializes me with my diagram edit domain and undo context. |
| * |
| * @param editDomain |
| * the editing domain assoicated with this stack |
| * @param undoContext |
| * my undo context |
| */ |
| public DiagramCommandStack(IDiagramEditDomain editDomain) { |
| this.editDomain = editDomain; |
| } |
| |
| /** |
| * Adds a listener to this CommandStack. |
| * |
| * @param listener |
| * The Object listening to this CommandStack. |
| */ |
| public void addCommandStackListener(CommandStackListener listener) { |
| |
| final CommandStackListener csl = listener; |
| // The removal of the listener here is done to avoid multiple |
| // commandchangelisteners added to the commandmanager. |
| // Tauseef Israr |
| removeCommandStackListener(csl); |
| |
| IOperationHistoryListener cmcl = new IOperationHistoryListener() { |
| |
| public void historyNotification(OperationHistoryEvent event) { |
| if (csl != null) { |
| csl.commandStackChanged(new HistoryEventObject(event)); |
| } |
| } |
| }; |
| |
| stackToManager.put(csl, cmcl); |
| getOperationHistory().addOperationHistoryListener(cmcl); |
| } |
| |
| /** |
| * Returns <code>true</code> if there is a Command to redo. |
| * |
| * @return <code>true</code> if there is a Command to redo. |
| */ |
| public boolean canRedo() { |
| return getOperationHistory().canRedo(getUndoContext()); |
| } |
| |
| /** |
| * Returns <code>true</code> if the last Command executed can be undone. |
| * |
| * @return <code>true</code> if the last Command executed can be undone. |
| */ |
| public boolean canUndo() { |
| return getOperationHistory().canUndo(getUndoContext()); |
| } |
| |
| /** |
| * Executes the given Command if it can execute. |
| * |
| * @param command |
| * The Command to execute. |
| */ |
| public void execute(Command command) { |
| execute(command, null); |
| } |
| |
| /** |
| * Executes the given Command if it can execute. |
| * |
| * @param command |
| * The Command to execute. |
| * @param progressMonitor |
| */ |
| public void execute(Command command, IProgressMonitor progressMonitor) { |
| if (command == null || !command.canExecute()) |
| return; |
| execute(getICommand(command), progressMonitor); |
| } |
| |
| /** |
| * exectus a the supplied command |
| * |
| * @param command |
| * the command to execute |
| */ |
| protected void execute(ICommand command) { |
| execute(command, null); |
| } |
| |
| /** |
| * executes the supplied command |
| * |
| * @param command |
| * the command to exectue |
| * @param progressMonitor |
| */ |
| protected void execute(ICommand command, IProgressMonitor progressMonitor) { |
| |
| if (progressMonitor != null) { |
| try { |
| command.addContext(getUndoContext()); |
| getOperationHistory().execute(command, progressMonitor, null); |
| |
| } catch (ExecutionException e) { |
| Trace.catching(DiagramUIPlugin.getInstance(), |
| DiagramUIDebugOptions.EXCEPTIONS_CATCHING, |
| getClass(), "execute", e); //$NON-NLS-1$ |
| Log.error(DiagramUIPlugin.getInstance(), |
| DiagramUIStatusCodes.COMMAND_FAILURE, "execute", e); //$NON-NLS-1$ |
| } |
| } else { |
| try { |
| command.addContext(getUndoContext()); |
| getOperationHistory().execute(command, |
| new NullProgressMonitor(), null); |
| |
| } catch (ExecutionException e) { |
| Trace.catching(DiagramUIPlugin.getInstance(), |
| DiagramUIDebugOptions.EXCEPTIONS_CATCHING, |
| getClass(), "execute", e); //$NON-NLS-1$ |
| Log.error(DiagramUIPlugin.getInstance(), |
| DiagramUIStatusCodes.COMMAND_FAILURE, "execute", e); //$NON-NLS-1$ |
| } |
| } |
| |
| } |
| |
| |
| /** |
| * Converts a GEF {@link Command} into a GMF {@link ICommand} |
| * |
| * @param command |
| * the GEF command |
| * @return the GMF command |
| */ |
| public static ICommand getICommand(Command command) { |
| |
| if (command instanceof CompoundCommand) { |
| |
| CompositeCommand composite = new CompositeCommand( |
| command.getLabel()); |
| Object[] subCommands = ((CompoundCommand) command).getChildren(); |
| |
| for (int i = 0; i < subCommands.length; i++) { |
| composite.compose(getICommand((Command) subCommands[i])); |
| } |
| return composite.reduce(); |
| } |
| |
| if (command instanceof ICommandProxy) { |
| return getICommand(((ICommandProxy) command).getICommand()); |
| } |
| |
| if (null != command) { |
| return new CommandProxy(command); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Removes redundancies from <code>command</code> by stripping out layers |
| * of command wrappers used to accomodate the use of GEF commands on an |
| * {@link IOperationHistory} and {@link ICommand}s on the GEF |
| * {@link CommandStack}. |
| * |
| * @param command |
| * the command to be processed |
| * @return a command representing the simplified form of the input command. |
| * May be a new command. |
| */ |
| public static ICommand getICommand(ICommand command) { |
| |
| ICommand result = command; |
| |
| if (command instanceof ICompositeCommand) { |
| // process composite command |
| List processedCommands = new ArrayList(); |
| |
| ICompositeCommand composite = (ICompositeCommand) command; |
| |
| if (!composite.isEmpty()) { |
| |
| for (Iterator i = composite.iterator(); i.hasNext();) { |
| IUndoableOperation nextOperation = (IUndoableOperation) i |
| .next(); |
| |
| // remove the next child from the composite |
| i.remove(); |
| |
| // convert any GEF commands to GMF commands |
| if (nextOperation instanceof ICommand) { |
| ICommand nextCommand = (ICommand) nextOperation; |
| processedCommands.add(getICommand(nextCommand)); |
| |
| } else { |
| processedCommands.add(nextOperation); |
| } |
| } |
| |
| // add all the children back |
| for (Iterator i = processedCommands.iterator(); i.hasNext();) { |
| composite.add((IUndoableOperation) i.next()); |
| } |
| |
| // reduce to the simplest equivalent form |
| result = composite.reduce(); |
| } |
| |
| } else if (command instanceof CommandProxy) { |
| // process GEF command proxy |
| return getICommand(((CommandProxy) command).getCommand()); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Returns the most recently executed command. |
| * |
| * @return The most recently executed command. |
| */ |
| public Command getMostRecentCommand() { |
| return cmdRecent; |
| } |
| |
| /** |
| * getRedoCommand Returns the command at the top of the redo stack. |
| * |
| * @see org.eclipse.gef.commands.CommandStack#getRedoCommand() |
| */ |
| public Command getRedoCommand() { |
| if (getOperationHistory().canRedo(getUndoContext())) { |
| Command emptyCmd = new Command() { |
| // empty |
| }; |
| |
| IUndoableOperation redo = getOperationHistory().getRedoOperation( |
| getUndoContext()); |
| emptyCmd.setLabel(redo.getLabel()); |
| return emptyCmd; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * getUndoCommand() Returns the next command to be undone. |
| * |
| * @see org.eclipse.gef.commands.CommandStack#getUndoCommand() |
| */ |
| public Command getUndoCommand() { |
| if (getOperationHistory().canUndo(getUndoContext())) { |
| Command emptyCmd = new Command() { |
| // empty |
| }; |
| |
| IUndoableOperation undo = getOperationHistory().getUndoOperation( |
| getUndoContext()); |
| emptyCmd.setLabel(undo.getLabel()); |
| return emptyCmd; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Executes the last undone Command. |
| */ |
| public void redo() { |
| cmdRecent = getRedoCommand(); |
| |
| try { |
| getOperationHistory().redo(getUndoContext(), |
| new NullProgressMonitor(), null); |
| |
| } catch (ExecutionException e) { |
| Trace.catching(DiagramUIPlugin.getInstance(), |
| DiagramUIDebugOptions.EXCEPTIONS_CATCHING, |
| ConnectionHandleTool.class, "redo", e); //$NON-NLS-1$ |
| Log.error(DiagramUIPlugin.getInstance(), |
| DiagramUIStatusCodes.COMMAND_FAILURE, "redo", e); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Removes the given CommandStackListener. |
| * |
| * @param listener |
| * The object to be removed from the list of listeners. |
| */ |
| public void removeCommandStackListener(CommandStackListener listener) { |
| final CommandStackListener csl = listener; |
| |
| if (csl != null) { |
| IOperationHistoryListener historyListener = (IOperationHistoryListener) stackToManager |
| .get(csl); |
| |
| if (historyListener != null) { |
| getOperationHistory().removeOperationHistoryListener( |
| historyListener); |
| } |
| // mgoyal: removing from stack manager |
| stackToManager.remove(csl); |
| } |
| } |
| |
| /** |
| * Undoes the last executed Command. |
| */ |
| public void undo() { |
| cmdRecent = getUndoCommand(); |
| |
| try { |
| getOperationHistory().undo(getUndoContext(), |
| new NullProgressMonitor(), null); |
| |
| } catch (ExecutionException e) { |
| Trace.catching(DiagramUIPlugin.getInstance(), |
| DiagramUIDebugOptions.EXCEPTIONS_CATCHING, |
| ConnectionHandleTool.class, "undo", e); //$NON-NLS-1$ |
| Log.error(DiagramUIPlugin.getInstance(), |
| DiagramUIStatusCodes.COMMAND_FAILURE, "undo", e); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Returns the editDomain. |
| * |
| * @return IDiagramEditDomain |
| */ |
| protected IDiagramEditDomain getDiagramEditDomain() { |
| return editDomain; |
| } |
| |
| /** |
| * Gets my operation history delegate. |
| * |
| * @return my operation history delegate |
| */ |
| protected IOperationHistory getOperationHistory() { |
| |
| if (delegate == null) { |
| delegate = OperationHistoryFactory.getOperationHistory(); |
| } |
| return delegate; |
| } |
| |
| /** |
| * Sets my operation history delegate. |
| * |
| * @param operationHistory |
| * my operation history delegate |
| */ |
| public void setOperationHistory(IOperationHistory operationHistory) { |
| this.delegate = operationHistory; |
| } |
| |
| /** |
| * Gets the return values of the given executed command |
| * |
| * @param c |
| * The command |
| * @return A collection of values returned by the given command |
| */ |
| public static Collection getReturnValues(Command c) { |
| if (c instanceof CompoundCommand) { |
| CompoundCommand cc = (CompoundCommand) c; |
| List l = new ArrayList(cc.size()); |
| for (Iterator i = cc.getCommands().iterator(); i.hasNext();) |
| l.addAll(getReturnValues((Command) i.next())); |
| return l; |
| |
| } else if (c instanceof ICommandProxy) { |
| return getReturnValues((ICommandProxy) c); |
| } |
| return Collections.EMPTY_LIST; |
| } |
| |
| /** |
| * gets the return the values for the supplied command. |
| * |
| * @param cmd |
| * command to use |
| * @return a collection of return values |
| */ |
| public static Collection getReturnValues(ICommandProxy cmd) { |
| return getReturnValues(cmd.getICommand()); |
| } |
| |
| |
| |
| |
| /** |
| * gets the return the values for the supplied command. |
| * @param cmd command to use |
| * @return a collection of return values |
| */ |
| public static Collection getReturnValues( CommandProxy cmd ) { |
| return getReturnValues( cmd.getCommand() ); |
| } |
| |
| /** |
| * gets the return the values for the supplied command. |
| * |
| * @param cmd |
| * command to use |
| * @return a collection of return values |
| */ |
| public static Collection getReturnValues(ICommand cmd) { |
| |
| if (cmd instanceof ICompositeCommand) { |
| ICompositeCommand cc = (ICompositeCommand) cmd; |
| List l = new ArrayList(); |
| for (Iterator i = cc.iterator(); i.hasNext();) { |
| IUndoableOperation child = (IUndoableOperation) i.next(); |
| if (child instanceof ICommand) { |
| l.addAll(getReturnValues((ICommand) child)); |
| } |
| } |
| return l; |
| |
| } else if ( cmd instanceof CommandProxy ) { // |
| // Need to recurse into the proxy command(s) since they |
| // will not have set the CommandProxy result |
| // This Could be moved into CommandProxy but |
| // #getCommandResult() can no longer be final. |
| return getReturnValues((CommandProxy)cmd); |
| |
| } else { |
| CommandResult r = cmd.getCommandResult(); |
| Object o = r != null ? r.getReturnValue() |
| : null; |
| |
| if (o instanceof Collection) { |
| return (Collection) o; |
| |
| } else if (o != null) { |
| return Collections.singletonList(o); |
| } |
| } |
| |
| return Collections.EMPTY_LIST; |
| } |
| |
| /** |
| * Gets my undo context. I add my context to all commands executed through |
| * me. |
| * |
| * @return my undo context |
| */ |
| public IUndoContext getUndoContext() { |
| |
| if (undoContext == null) { |
| undoContext = new ObjectUndoContext(this); |
| } |
| return undoContext; |
| } |
| |
| /** |
| * Sets my undo context. |
| * |
| * @param undoContext |
| * my undo context |
| */ |
| public void setUndoContext(IUndoContext undoContext) { |
| this.undoContext = undoContext; |
| } |
| |
| public void dispose() { |
| // clean up the known listeners (if there is any remaining) |
| // this will prevent clients from causing memory leaks |
| Set entries = stackToManager.entrySet(); |
| for (Iterator iter = entries.iterator(); iter.hasNext();) { |
| Map.Entry element = (Map.Entry) iter.next(); |
| IOperationHistoryListener historyListener = (IOperationHistoryListener) element.getValue(); |
| if (historyListener != null) { |
| getOperationHistory().removeOperationHistoryListener( |
| historyListener); |
| } |
| } |
| super.dispose(); |
| } |
| |
| } |