// // // // // $Revision: 2659 $ // using System; using System.Collections.Generic; using System.Drawing; namespace ICSharpCode.TextEditor.Undo { /// /// This class implements an undo stack /// public class UndoStack { Stack undostack = new Stack(); Stack redostack = new Stack(); public TextEditorControlBase TextEditorControl = null; /// /// public event EventHandler ActionUndone; /// /// public event EventHandler ActionRedone; /// /// Gets/Sets if changes to the document are protocolled by the undo stack. /// Used internally to disable the undo stack temporarily while undoing an action. /// internal bool AcceptChanges = true; /// /// Gets if there are actions on the undo stack. /// public bool CanUndo { get { return undostack.Count > 0; } } /// /// Gets if there are actions on the redo stack. /// public bool CanRedo { get { return redostack.Count > 0; } } /// /// Gets the number of actions on the undo stack. /// public int UndoItemCount { get { return undostack.Count; } } /// /// Gets the number of actions on the redo stack. /// public int RedoItemCount { get { return redostack.Count; } } // /// // /// You call this method to pool the last x operations from the undo stack // /// to make 1 operation from it. // /// // public void CombineLast(int actionCount) // { // undostack.Push(new UndoQueue(undostack, actionCount)); // } int undoGroupDepth; int actionCountInUndoGroup; public void StartUndoGroup() { if (undoGroupDepth == 0) { actionCountInUndoGroup = 0; } undoGroupDepth++; } public void EndUndoGroup() { if (undoGroupDepth == 0) return; //throw new InvalidOperationException("There are no open undo groups"); undoGroupDepth--; if (undoGroupDepth == 0 && actionCountInUndoGroup > 1) { undostack.Push(new UndoQueue(undostack, actionCountInUndoGroup)); } } /// /// Call this method to undo the last operation on the stack /// public void Undo() { while (undoGroupDepth != 0) // SSM 24.08.18 - грязный хак в попытке исправления ошибки с Ctrl-Z { EndUndoGroup(); } if (undoGroupDepth != 0) { throw new InvalidOperationException("cannot perform Undo/Redo while undo group is open"); } if (undostack.Count > 0) { IUndoableOperation uedit = (IUndoableOperation)undostack.Pop(); redostack.Push(uedit); uedit.Undo(); OnActionUndone(); } } /// /// Call this method to redo the last undone operation /// public void Redo() { if (undoGroupDepth != 0) { throw new InvalidOperationException("cannot perform Undo/Redo while undo group is open"); } if (redostack.Count > 0) { IUndoableOperation uedit = (IUndoableOperation)redostack.Pop(); undostack.Push(uedit); uedit.Redo(); OnActionRedone(); } } /// /// Call this method to push an UndoableOperation on the undostack, the redostack /// will be cleared, if you use this method. /// public void Push(IUndoableOperation operation) { if (operation == null) { throw new ArgumentNullException("UndoStack.Push(UndoableOperation operation) : operation can't be null"); } if (AcceptChanges) { StartUndoGroup(); undostack.Push(operation); actionCountInUndoGroup++; if (TextEditorControl != null) { undostack.Push(new UndoableSetCaretPosition(this, TextEditorControl.ActiveTextAreaControl.Caret.Position)); actionCountInUndoGroup++; } EndUndoGroup(); ClearRedoStack(); } } /// /// Call this method, if you want to clear the redo stack /// public void ClearRedoStack() { redostack.Clear(); } /// /// Clears both the undo and redo stack. /// public void ClearAll() { undostack.Clear(); redostack.Clear(); actionCountInUndoGroup = 0; } /// /// protected void OnActionUndone() { if (ActionUndone != null) { ActionUndone(null, null); } } /// /// protected void OnActionRedone() { if (ActionRedone != null) { ActionRedone(null, null); } } class UndoableSetCaretPosition : IUndoableOperation { UndoStack stack; TextLocation pos; TextLocation redoPos; public UndoableSetCaretPosition(UndoStack stack, TextLocation pos) { this.stack = stack; this.pos = pos; } public void Undo() { redoPos = stack.TextEditorControl.ActiveTextAreaControl.Caret.Position; stack.TextEditorControl.ActiveTextAreaControl.Caret.Position = pos; } public void Redo() { stack.TextEditorControl.ActiveTextAreaControl.Caret.Position = redoPos; } } } }