191 lines
4.6 KiB
C#
191 lines
4.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Studio
|
|
{
|
|
public interface IMemento<T>
|
|
{
|
|
IMemento<T> Restore(T target);
|
|
}
|
|
|
|
public class CompoundMemento<T> : IMemento<T>
|
|
{
|
|
private readonly List<IMemento<T>> _mementos = new List<IMemento<T>>();
|
|
|
|
public void Add(IMemento<T> m)
|
|
{
|
|
_mementos.Add(m);
|
|
}
|
|
|
|
public int Size => _mementos.Count;
|
|
|
|
#region IMemento Members
|
|
|
|
public CompoundMemento<T> Restore(T target)
|
|
{
|
|
CompoundMemento<T> inverse = new CompoundMemento<T>();
|
|
|
|
//starts from the last action
|
|
for (int i = _mementos.Count - 1; i >= 0; i--)
|
|
{
|
|
inverse.Add(_mementos[i].Restore(target));
|
|
}
|
|
|
|
return inverse;
|
|
}
|
|
|
|
IMemento<T> IMemento<T>.Restore(T target)
|
|
{
|
|
return Restore(target);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public class UndoRedo<T>
|
|
{
|
|
private const int DEFAULT_CAPACITY = 100;
|
|
|
|
private bool supportRedo = true;
|
|
private bool inUndoRedo = false;
|
|
|
|
private CompoundMemento<T> tempMemento = null;
|
|
|
|
protected T subject;
|
|
|
|
#if LIMITED_CAPACITY
|
|
protected RoundStack<IMemento<T>> undoStack;
|
|
|
|
protected RoundStack<IMemento<T>> redoStack;
|
|
|
|
public UndoRedoHistory(T subject)
|
|
: this(subject, DEFAULT_CAPACITY)
|
|
{
|
|
}
|
|
|
|
public UndoRedoHistory(T subject, int capacity)
|
|
{
|
|
this.subject = subject;
|
|
undoStack = new RoundStack<IMemento<T>>(capacity);
|
|
redoStack = new RoundStack<IMemento<T>>(capacity);
|
|
}
|
|
#else
|
|
protected Stack<IMemento<T>> undoStack = new Stack<IMemento<T>>(DEFAULT_CAPACITY);
|
|
|
|
protected Stack<IMemento<T>> redoStack = new Stack<IMemento<T>>(DEFAULT_CAPACITY);
|
|
|
|
public UndoRedo(T subject)
|
|
{
|
|
this.subject = subject;
|
|
}
|
|
#endif
|
|
|
|
public bool InUndoRedo => inUndoRedo;
|
|
|
|
public int UndoCount => undoStack.Count;
|
|
|
|
public int RedoCount => redoStack.Count;
|
|
|
|
public bool SupportRedo
|
|
{
|
|
get { return supportRedo; }
|
|
set { supportRedo = value; }
|
|
}
|
|
|
|
public void BeginCompoundDo()
|
|
{
|
|
if (tempMemento != null)
|
|
throw new InvalidOperationException("Previous complex memento wasn't commited.");
|
|
|
|
tempMemento = new CompoundMemento<T>();
|
|
}
|
|
|
|
public void EndCompoundDo()
|
|
{
|
|
if (tempMemento == null)
|
|
throw new InvalidOperationException("Ending a non-existing complex memento");
|
|
|
|
_Do(tempMemento);
|
|
tempMemento = null;
|
|
}
|
|
|
|
public void Do(IMemento<T> m)
|
|
{
|
|
if (inUndoRedo)
|
|
throw new InvalidOperationException("Involking do within an undo/redo action.");
|
|
|
|
if (tempMemento == null)
|
|
{
|
|
_Do(m);
|
|
}
|
|
else
|
|
{
|
|
tempMemento.Add(m);
|
|
}
|
|
}
|
|
|
|
private void _Do(IMemento<T> m)
|
|
{
|
|
Console.WriteLine(m.GetType().ToString());
|
|
|
|
redoStack.Clear();
|
|
undoStack.Push(m);
|
|
}
|
|
|
|
public void Undo()
|
|
{
|
|
if (tempMemento != null)
|
|
throw new InvalidOperationException("The complex memento wasn't commited.");
|
|
|
|
inUndoRedo = true;
|
|
if (CanUndo)
|
|
{
|
|
IMemento<T> top = undoStack.Pop();
|
|
if (top != null)
|
|
{
|
|
redoStack.Push(top.Restore(subject));
|
|
}
|
|
}
|
|
|
|
inUndoRedo = false;
|
|
}
|
|
|
|
public void Redo()
|
|
{
|
|
if (tempMemento != null)
|
|
throw new InvalidOperationException("The complex memento wasn't commited.");
|
|
|
|
inUndoRedo = true;
|
|
if (CanRedo)
|
|
{
|
|
IMemento<T> top = redoStack.Pop();
|
|
if (top != null)
|
|
{
|
|
undoStack.Push(top.Restore(subject));
|
|
}
|
|
}
|
|
|
|
inUndoRedo = false;
|
|
}
|
|
|
|
public bool CanUndo => (undoStack.Count != 0);
|
|
|
|
public bool CanRedo => (redoStack.Count != 0);
|
|
|
|
public void Clear()
|
|
{
|
|
undoStack.Clear();
|
|
redoStack.Clear();
|
|
}
|
|
|
|
public IMemento<T> PeekUndo()
|
|
{
|
|
return undoStack.Count > 0 ? undoStack.Peek() : null;
|
|
}
|
|
|
|
public IMemento<T> PeekRedo()
|
|
{
|
|
return redoStack.Count > 0 ? redoStack.Peek() : null;
|
|
}
|
|
}
|
|
}
|