first commit
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
namespace ICSharpCode.TextEditor.Undo
|
||||
{
|
||||
/// <summary>
|
||||
/// This Interface describes a the basic Undo/Redo operation
|
||||
/// all Undo Operations must implement this interface.
|
||||
/// </summary>
|
||||
public interface IUndoableOperation
|
||||
{
|
||||
/// <summary>
|
||||
/// Undo the last operation
|
||||
/// </summary>
|
||||
void Undo();
|
||||
|
||||
/// <summary>
|
||||
/// Redo the last operation
|
||||
/// </summary>
|
||||
void Redo();
|
||||
}
|
||||
}
|
||||
53
ICSharpCode.TextEditor/Project/Src/Undo/UndoQueue.cs
Normal file
53
ICSharpCode.TextEditor/Project/Src/Undo/UndoQueue.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Undo
|
||||
{
|
||||
/// <summary>
|
||||
/// This class stacks the last x operations from the undostack and makes
|
||||
/// one undo/redo operation from it.
|
||||
/// </summary>
|
||||
internal sealed class UndoQueue : IUndoableOperation
|
||||
{
|
||||
List<IUndoableOperation> undolist = new List<IUndoableOperation>();
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public UndoQueue(Stack<IUndoableOperation> stack, int numops)
|
||||
{
|
||||
if (stack == null) {
|
||||
throw new ArgumentNullException("stack");
|
||||
}
|
||||
|
||||
Debug.Assert(numops > 0 , "ICSharpCode.TextEditor.Undo.UndoQueue : numops should be > 0");
|
||||
if (numops > stack.Count) {
|
||||
numops = stack.Count;
|
||||
}
|
||||
|
||||
for (int i = 0; i < numops; ++i) {
|
||||
undolist.Add(stack.Pop());
|
||||
}
|
||||
}
|
||||
public void Undo()
|
||||
{
|
||||
for (int i = 0; i < undolist.Count; ++i) {
|
||||
undolist[i].Undo();
|
||||
}
|
||||
}
|
||||
|
||||
public void Redo()
|
||||
{
|
||||
for (int i = undolist.Count - 1 ; i >= 0 ; --i) {
|
||||
undolist[i].Redo();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
242
ICSharpCode.TextEditor/Project/Src/Undo/UndoStack.cs
Normal file
242
ICSharpCode.TextEditor/Project/Src/Undo/UndoStack.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Undo
|
||||
{
|
||||
/// <summary>
|
||||
/// This class implements an undo stack
|
||||
/// </summary>
|
||||
public class UndoStack
|
||||
{
|
||||
Stack<IUndoableOperation> undostack = new Stack<IUndoableOperation>();
|
||||
Stack<IUndoableOperation> redostack = new Stack<IUndoableOperation>();
|
||||
|
||||
public TextEditorControlBase TextEditorControl = null;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public event EventHandler ActionUndone;
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public event EventHandler ActionRedone;
|
||||
|
||||
public event OperationEventHandler OperationPushed;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
internal bool AcceptChanges = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if there are actions on the undo stack.
|
||||
/// </summary>
|
||||
public bool CanUndo {
|
||||
get {
|
||||
return undostack.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if there are actions on the redo stack.
|
||||
/// </summary>
|
||||
public bool CanRedo {
|
||||
get {
|
||||
return redostack.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of actions on the undo stack.
|
||||
/// </summary>
|
||||
public int UndoItemCount {
|
||||
get {
|
||||
return undostack.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of actions on the redo stack.
|
||||
/// </summary>
|
||||
public int RedoItemCount {
|
||||
get {
|
||||
return redostack.Count;
|
||||
}
|
||||
}
|
||||
|
||||
int undoGroupDepth;
|
||||
int actionCountInUndoGroup;
|
||||
|
||||
public void StartUndoGroup()
|
||||
{
|
||||
if (undoGroupDepth == 0) {
|
||||
actionCountInUndoGroup = 0;
|
||||
}
|
||||
undoGroupDepth++;
|
||||
//Util.LoggingService.Debug("Open undo group (new depth=" + undoGroupDepth + ")");
|
||||
}
|
||||
|
||||
public void EndUndoGroup()
|
||||
{
|
||||
if (undoGroupDepth == 0)
|
||||
throw new InvalidOperationException("There are no open undo groups");
|
||||
undoGroupDepth--;
|
||||
//Util.LoggingService.Debug("Close undo group (new depth=" + undoGroupDepth + ")");
|
||||
if (undoGroupDepth == 0 && actionCountInUndoGroup > 1) {
|
||||
UndoQueue op = new UndoQueue(undostack, actionCountInUndoGroup);
|
||||
undostack.Push(op);
|
||||
if (OperationPushed != null) {
|
||||
OperationPushed(this, new OperationEventArgs(op));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AssertNoUndoGroupOpen()
|
||||
{
|
||||
if (undoGroupDepth != 0) {
|
||||
undoGroupDepth = 0;
|
||||
throw new InvalidOperationException("No undo group should be open at this point");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this method to undo the last operation on the stack
|
||||
/// </summary>
|
||||
public void Undo()
|
||||
{
|
||||
AssertNoUndoGroupOpen();
|
||||
if (undostack.Count > 0) {
|
||||
IUndoableOperation uedit = (IUndoableOperation)undostack.Pop();
|
||||
redostack.Push(uedit);
|
||||
uedit.Undo();
|
||||
OnActionUndone();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this method to redo the last undone operation
|
||||
/// </summary>
|
||||
public void Redo()
|
||||
{
|
||||
AssertNoUndoGroupOpen();
|
||||
if (redostack.Count > 0) {
|
||||
IUndoableOperation uedit = (IUndoableOperation)redostack.Pop();
|
||||
undostack.Push(uedit);
|
||||
uedit.Redo();
|
||||
OnActionRedone();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this method to push an UndoableOperation on the undostack, the redostack
|
||||
/// will be cleared, if you use this method.
|
||||
/// </summary>
|
||||
public void Push(IUndoableOperation operation)
|
||||
{
|
||||
if (operation == null) {
|
||||
throw new ArgumentNullException("operation");
|
||||
}
|
||||
|
||||
if (AcceptChanges) {
|
||||
StartUndoGroup();
|
||||
undostack.Push(operation);
|
||||
actionCountInUndoGroup++;
|
||||
if (TextEditorControl != null) {
|
||||
undostack.Push(new UndoableSetCaretPosition(this, TextEditorControl.ActiveTextAreaControl.Caret.Position));
|
||||
actionCountInUndoGroup++;
|
||||
}
|
||||
EndUndoGroup();
|
||||
ClearRedoStack();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this method, if you want to clear the redo stack
|
||||
/// </summary>
|
||||
public void ClearRedoStack()
|
||||
{
|
||||
redostack.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears both the undo and redo stack.
|
||||
/// </summary>
|
||||
public void ClearAll()
|
||||
{
|
||||
AssertNoUndoGroupOpen();
|
||||
undostack.Clear();
|
||||
redostack.Clear();
|
||||
actionCountInUndoGroup = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
protected void OnActionUndone()
|
||||
{
|
||||
if (ActionUndone != null) {
|
||||
ActionUndone(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
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;
|
||||
stack.TextEditorControl.ActiveTextAreaControl.SelectionManager.ClearSelection();
|
||||
}
|
||||
|
||||
public void Redo()
|
||||
{
|
||||
stack.TextEditorControl.ActiveTextAreaControl.Caret.Position = redoPos;
|
||||
stack.TextEditorControl.ActiveTextAreaControl.SelectionManager.ClearSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class OperationEventArgs : EventArgs
|
||||
{
|
||||
public OperationEventArgs(IUndoableOperation op)
|
||||
{
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
IUndoableOperation op;
|
||||
|
||||
public IUndoableOperation Operation {
|
||||
get {
|
||||
return op;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void OperationEventHandler(object sender, OperationEventArgs e);
|
||||
}
|
||||
72
ICSharpCode.TextEditor/Project/Src/Undo/UndoableDelete.cs
Normal file
72
ICSharpCode.TextEditor/Project/Src/Undo/UndoableDelete.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Undo
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is for the undo of Document insert operations
|
||||
/// </summary>
|
||||
public class UndoableDelete : IUndoableOperation
|
||||
{
|
||||
IDocument document;
|
||||
// int oldCaretPos;
|
||||
int offset;
|
||||
string text;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="UndoableDelete"/>
|
||||
/// </summary>
|
||||
public UndoableDelete(IDocument document, int offset, string text)
|
||||
{
|
||||
if (document == null) {
|
||||
throw new ArgumentNullException("document");
|
||||
}
|
||||
if (offset < 0 || offset > document.TextLength) {
|
||||
throw new ArgumentOutOfRangeException("offset");
|
||||
}
|
||||
|
||||
Debug.Assert(text != null, "text can't be null");
|
||||
// oldCaretPos = document.Caret.Offset;
|
||||
this.document = document;
|
||||
this.offset = offset;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Undo last operation
|
||||
/// </remarks>
|
||||
public void Undo()
|
||||
{
|
||||
// we clear all selection direct, because the redraw
|
||||
// is done per refresh at the end of the action
|
||||
// textArea.SelectionManager.SelectionCollection.Clear();
|
||||
document.UndoStack.AcceptChanges = false;
|
||||
document.Insert(offset, text);
|
||||
// document.Caret.Offset = Math.Min(document.TextLength, Math.Max(0, oldCaretPos));
|
||||
document.UndoStack.AcceptChanges = true;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Redo last undone operation
|
||||
/// </remarks>
|
||||
public void Redo()
|
||||
{
|
||||
// we clear all selection direct, because the redraw
|
||||
// is done per refresh at the end of the action
|
||||
// textArea.SelectionManager.SelectionCollection.Clear();
|
||||
|
||||
document.UndoStack.AcceptChanges = false;
|
||||
document.Remove(offset, text.Length);
|
||||
// document.Caret.Offset = Math.Min(document.TextLength, Math.Max(0, document.Caret.Offset));
|
||||
document.UndoStack.AcceptChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
73
ICSharpCode.TextEditor/Project/Src/Undo/UndoableInsert.cs
Normal file
73
ICSharpCode.TextEditor/Project/Src/Undo/UndoableInsert.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Undo
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is for the undo of Document insert operations
|
||||
/// </summary>
|
||||
public class UndoableInsert : IUndoableOperation
|
||||
{
|
||||
IDocument document;
|
||||
// int oldCaretPos;
|
||||
int offset;
|
||||
string text;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="UndoableInsert"/>
|
||||
/// </summary>
|
||||
public UndoableInsert(IDocument document, int offset, string text)
|
||||
{
|
||||
if (document == null) {
|
||||
throw new ArgumentNullException("document");
|
||||
}
|
||||
if (offset < 0 || offset > document.TextLength) {
|
||||
throw new ArgumentOutOfRangeException("offset");
|
||||
}
|
||||
|
||||
Debug.Assert(text != null, "text can't be null");
|
||||
// oldCaretPos = document.Caret.Offset;
|
||||
this.document = document;
|
||||
this.offset = offset;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Undo last operation
|
||||
/// </remarks>
|
||||
public void Undo()
|
||||
{
|
||||
// we clear all selection direct, because the redraw
|
||||
// is done per refresh at the end of the action
|
||||
// document.SelectionCollection.Clear();
|
||||
|
||||
document.UndoStack.AcceptChanges = false;
|
||||
document.Remove(offset, text.Length);
|
||||
// document.Caret.Offset = Math.Min(document.TextLength, Math.Max(0, oldCaretPos));
|
||||
document.UndoStack.AcceptChanges = true;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Redo last undone operation
|
||||
/// </remarks>
|
||||
public void Redo()
|
||||
{
|
||||
// we clear all selection direct, because the redraw
|
||||
// is done per refresh at the end of the action
|
||||
// document.SelectionCollection.Clear();
|
||||
|
||||
document.UndoStack.AcceptChanges = false;
|
||||
document.Insert(offset, text);
|
||||
// document.Caret.Offset = Math.Min(document.TextLength, Math.Max(0, document.Caret.Offset));
|
||||
document.UndoStack.AcceptChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
ICSharpCode.TextEditor/Project/Src/Undo/UndoableReplace.cs
Normal file
75
ICSharpCode.TextEditor/Project/Src/Undo/UndoableReplace.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using ICSharpCode.TextEditor.Document;
|
||||
|
||||
namespace ICSharpCode.TextEditor.Undo
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is for the undo of Document insert operations
|
||||
/// </summary>
|
||||
public class UndoableReplace : IUndoableOperation
|
||||
{
|
||||
IDocument document;
|
||||
// int oldCaretPos;
|
||||
int offset;
|
||||
string text;
|
||||
string origText;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="UndoableReplace"/>
|
||||
/// </summary>
|
||||
public UndoableReplace(IDocument document, int offset, string origText, string text)
|
||||
{
|
||||
if (document == null) {
|
||||
throw new ArgumentNullException("document");
|
||||
}
|
||||
if (offset < 0 || offset > document.TextLength) {
|
||||
throw new ArgumentOutOfRangeException("offset");
|
||||
}
|
||||
|
||||
Debug.Assert(text != null, "text can't be null");
|
||||
// oldCaretPos = document.Caret.Offset;
|
||||
this.document = document;
|
||||
this.offset = offset;
|
||||
this.text = text;
|
||||
this.origText = origText;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Undo last operation
|
||||
/// </remarks>
|
||||
public void Undo()
|
||||
{
|
||||
// we clear all selection direct, because the redraw
|
||||
// is done per refresh at the end of the action
|
||||
// document.SelectionCollection.Clear();
|
||||
|
||||
document.UndoStack.AcceptChanges = false;
|
||||
document.Replace(offset, text.Length, origText);
|
||||
// document.Caret.Offset = Math.Min(document.TextLength, Math.Max(0, oldCaretPos));
|
||||
document.UndoStack.AcceptChanges = true;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Redo last undone operation
|
||||
/// </remarks>
|
||||
public void Redo()
|
||||
{
|
||||
// we clear all selection direct, because the redraw
|
||||
// is done per refresh at the end of the action
|
||||
// document.SelectionCollection.Clear();
|
||||
|
||||
document.UndoStack.AcceptChanges = false;
|
||||
document.Replace(offset, origText.Length, text);
|
||||
// document.Caret.Offset = Math.Min(document.TextLength, Math.Max(0, document.Caret.Offset));
|
||||
document.UndoStack.AcceptChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user