/* * 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); } }
/* * Flush the redo stack of all operations that have the given context. */ private void flushRedo(IUndoContext context) { if (DEBUG_OPERATION_HISTORY_DISPOSE) { Tracing.printTrace( "OPERATIONHISTORY", "Flushing redo history for " + context); // $NON-NLS-1$ //$NON-NLS-2$ } synchronized (undoRedoHistoryLock) { Object[] filtered = filter(redoList, 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 redoList.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) { redoList.remove(operation); internalRemove(operation); } } } } }
/** * Check the undo limit before adding an operation. Return a boolean indicating whether the undo * should proceed. */ private boolean checkUndoLimit(IUndoableOperation operation) { IUndoContext[] contexts = operation.getContexts(); for (int i = 0; i < contexts.length; i++) { int limit = getLimit(contexts[i]); if (limit > 0) { forceUndoLimit(contexts[i], limit - 1); } else { // this context has a 0 limit operation.removeContext(contexts[i]); } } return operation.getContexts().length > 0; }
/* * Force the undo history for the given context to contain max or less * items. */ private void forceUndoLimit(IUndoContext context, int max) { synchronized (undoRedoHistoryLock) { Object[] filtered = filter(undoList, context); int size = filtered.length; if (size > 0) { int index = 0; while (size > max) { IUndoableOperation removed = (IUndoableOperation) filtered[index]; if (context == GLOBAL_UNDO_CONTEXT || removed.getContexts().length == 1) { /* * remove the operation if we are enforcing a global limit * or if the operation only has the specified context */ undoList.remove(removed); internalRemove(removed); } else { /* * if the operation has multiple contexts and we've reached * the limit for only one of them, then just remove the * context, not the operation. */ removed.removeContext(context); } size--; index++; } } } }
@Override public void add(IUndoableOperation operation) { Assert.isNotNull(operation); /* * If we are in the middle of executing an open batching operation, and * this is not that operation, then we need only add the context of the * new operation to the batch. The operation itself is disposed since we * will never undo or redo it. We consider it to be triggered by the * batching operation and assume that its undo will be triggered by the * batching operation undo. */ synchronized (openCompositeLock) { if (openComposite != null && openComposite != operation) { openComposite.add(operation); return; } } if (checkUndoLimit(operation)) { synchronized (undoRedoHistoryLock) { undoList.add(operation); } notifyAdd(operation); // flush redo stack for related contexts IUndoContext[] contexts = operation.getContexts(); for (int i = 0; i < contexts.length; i++) { flushRedo(contexts[i]); } } else { // Dispose the operation since we will not have a reference to it. operation.dispose(); } }
/* * (non-Javadoc) * * @see org.eclipse.core.commands.operations.IOperationApprover#proceedRedoing(org.eclipse.core.commands.operations.IUndoableOperation, * org.eclipse.core.commands.operations.IOperationHistory, * org.eclipse.core.runtime.IAdaptable) */ public final IStatus proceedRedoing( IUndoableOperation operation, IOperationHistory history, IAdaptable info) { IUndoContext[] contexts = operation.getContexts(); for (int i = 0; i < contexts.length; i++) { IUndoContext context = contexts[i]; if (history.getRedoOperation(context) != operation) { IStatus status = allowLinearRedoViolation(operation, context, history, info); if (!status.isOK()) return status; } } return Status.OK_STATUS; }