/** * Perform the redo. All validity checks have already occurred. * * @param monitor * @param operation */ private IStatus doRedo(IProgressMonitor monitor, IAdaptable info, IUndoableOperation operation) throws ExecutionException { IStatus status = getRedoApproval(operation, info); if (status.isOK()) { notifyAboutToRedo(operation); try { status = operation.redo(monitor, info); } catch (OperationCanceledException e) { status = Status.CANCEL_STATUS; } catch (ExecutionException e) { notifyNotOK(operation); if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { Tracing.printTrace( "OPERATIONHISTORY", //$NON-NLS-1$ "ExecutionException while redoing " + operation); // $NON-NLS-1$ } throw e; } catch (Exception e) { notifyNotOK(operation); if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { Tracing.printTrace( "OPERATIONHISTORY", //$NON-NLS-1$ "Exception while redoing " + operation); // $NON-NLS-1$ } throw new ExecutionException( "While redoing the operation, an exception occurred", e); // $NON-NLS-1$ } } // if successful, the operation is removed from the redo history and // placed back in the undo history. if (status.isOK()) { boolean addedToUndo = true; synchronized (undoRedoHistoryLock) { redoList.remove(operation); if (checkUndoLimit(operation)) { undoList.add(operation); } else { addedToUndo = false; } } // dispose the operation since we could not add it to the // stack and will no longer have a reference to it. if (!addedToUndo) { operation.dispose(); } // notify listeners must happen after history is updated notifyRedone(operation); } else { notifyNotOK(operation, status); } return status; }
/* * Flush the undo stack of all operations that have the given context. */ private void flushUndo(IUndoContext context) { if (DEBUG_OPERATION_HISTORY_DISPOSE) { Tracing.printTrace( "OPERATIONHISTORY", "Flushing undo history for " + context); // $NON-NLS-1$ //$NON-NLS-2$ } synchronized (undoRedoHistoryLock) { // Get all operations that have the context (or one that matches) Object[] filtered = filter(undoList, context); for (int i = 0; i < filtered.length; i++) { IUndoableOperation operation = (IUndoableOperation) filtered[i]; if (context == GLOBAL_UNDO_CONTEXT || operation.getContexts().length == 1) { // remove the operation if it only has the context or we are // flushing all undoList.remove(operation); internalRemove(operation); } else { // remove the reference to the context. // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=161786 // It is not enough to simply remove the context. There could // be one or more contexts that match the one we are trying to // dispose. IUndoContext[] contexts = operation.getContexts(); for (int j = 0; j < contexts.length; j++) { if (contexts[j].matches(context)) { operation.removeContext(contexts[j]); } } if (operation.getContexts().length == 0) { undoList.remove(operation); internalRemove(operation); } } } } /* * There may be an open composite. If it has this context, then the * context must be removed. If it has only this context or we are * flushing all operations, then null it out and notify that we are * ending it. We don't remove it since it was never added. */ ICompositeOperation endedComposite = null; synchronized (openCompositeLock) { if (openComposite != null) { if (openComposite.hasContext(context)) { if (context == GLOBAL_UNDO_CONTEXT || openComposite.getContexts().length == 1) { endedComposite = openComposite; openComposite = null; } else { openComposite.removeContext(context); } } } } // notify outside of the synchronized block. if (endedComposite != null) { notifyNotOK(endedComposite); } }
@Override public void closeOperation(boolean operationOK, boolean addToHistory, int mode) { ICompositeOperation endedComposite = null; synchronized (openCompositeLock) { if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { if (openComposite == null) { Tracing.printTrace( "OPERATIONHISTORY", "Attempted to close operation when none was open"); //$NON-NLS-1$//$NON-NLS-2$ return; } } // notifications will occur outside the synchonized block if (openComposite != null) { if (DEBUG_OPERATION_HISTORY_OPENOPERATION) { Tracing.printTrace( "OPERATIONHISTORY", "Closing operation " + openComposite); // $NON-NLS-1$ //$NON-NLS-2$ } endedComposite = openComposite; openComposite = null; } } // any mode other than EXECUTE was triggered by a request to undo or // redo something already in the history, so undo and redo // notification will occur at the end of that sequence. if (endedComposite != null) { if (operationOK) { if (mode == EXECUTE) { notifyDone(endedComposite); } if (addToHistory) { add(endedComposite); } } else { if (mode == EXECUTE) { notifyNotOK(endedComposite); } } } }
/* * Notify listeners that an operation did not succeed after an attempt to * execute, undo, or redo was made. */ private void notifyNotOK(IUndoableOperation operation) { notifyNotOK(operation, null); }
@Override public IStatus execute(IUndoableOperation operation, IProgressMonitor monitor, IAdaptable info) throws ExecutionException { Assert.isNotNull(operation); // error if operation is invalid if (!operation.canExecute()) { return IOperationHistory.OPERATION_INVALID_STATUS; } // check with the operation approvers IStatus status = getExecuteApproval(operation, info); if (!status.isOK()) { // not approved. No notifications are sent, just return the status. return status; } /* * If we are in the middle of an open composite, then we will add this * operation to the open operation rather than add the operation to the * history. We will still execute it. */ boolean merging = false; synchronized (openCompositeLock) { if (openComposite != null) { // the composite shouldn't be executed explicitly while it is // still // open if (openComposite == operation) { return IOperationHistory.OPERATION_INVALID_STATUS; } openComposite.add(operation); merging = true; } } /* * Execute the operation */ if (!merging) { notifyAboutToExecute(operation); } try { status = operation.execute(monitor, info); } catch (OperationCanceledException e) { status = Status.CANCEL_STATUS; } catch (ExecutionException e) { notifyNotOK(operation); throw e; } catch (Exception e) { notifyNotOK(operation); throw new ExecutionException( "While executing the operation, an exception occurred", e); // $NON-NLS-1$ } // if successful, the notify listeners are notified and the operation is // added to the history if (!merging) { if (status.isOK()) { notifyDone(operation); add(operation); } else { notifyNotOK(operation, status); // dispose the operation since we did not add it to the stack // and will no longer have a reference to it. operation.dispose(); } } // all other severities are not interpreted. Simply return the status. return status; }