added samples
[windows-sources.git] / sdk / samples / WPFSamples / InkCanvasEditingModes / csharp / undoredo.cs
blob98bd27f99981d5537ddf9d67d966f1eb33da7297
1 using System;
2 using System.Collections.Generic;
3 using System.Windows;
4 using System.Windows.Controls;
5 using System.Windows.Media;
6 using System.Windows.Ink;
8 namespace InkCanvasEditingModes
10 sealed class CommandStack
12 /// <summary>
13 /// Initialization.
14 /// </summary>
15 /// <param name="strokes"></param>
16 public CommandStack(StrokeCollection strokes)
18 if (strokes == null)
20 throw new ArgumentNullException("strokes");
22 _strokeCollection = strokes;
23 _undoStack = new Stack<CommandItem>();
24 _redoStack = new Stack<CommandItem>();
25 _disableChangeTracking = false;
28 /// <summary>
29 /// StrokeCollection to track changes for
30 /// </summary>
31 public StrokeCollection StrokeCollection
33 get
35 return _strokeCollection;
39 /// <summary>
40 /// Only undo if there are more items in the stack to step back into.
41 /// </summary>
42 public bool CanUndo
44 get { return (_undoStack.Count > 0); }
47 /// <summary>
48 /// Only undo if one or more steps back in the stack.
49 /// </summary>
50 public bool CanRedo
52 get { return (_redoStack.Count > 0); }
55 /// <summary>
56 /// Add an item to the top of the command stack
57 /// </summary>
58 public void Undo()
60 if (!CanUndo) throw new InvalidOperationException("No actions to undo");
62 CommandItem item = _undoStack.Pop();
64 // Invoke the undo operation, with change-tracking temporarily suspended.
65 _disableChangeTracking = true;
66 try
68 item.Undo();
70 finally
72 _disableChangeTracking = false;
75 //place this item on the redo stack
76 _redoStack.Push(item);
79 /// <summary>
80 /// Take the top item off the command stack.
81 /// </summary>
82 public void Redo()
84 if (!CanRedo) throw new InvalidOperationException();
86 CommandItem item = _redoStack.Pop();
88 // Invoke the redo operation, with change-tracking temporarily suspended.
89 _disableChangeTracking = true;
90 try
92 item.Redo();
94 finally
96 _disableChangeTracking = false;
99 //place this item on the undo stack
100 _undoStack.Push(item);
103 /// <summary>
104 /// Add a command item to the stack.
105 /// </summary>
106 /// <param name="item"></param>
107 public void Enqueue(CommandItem item)
109 if (item == null)
111 throw new ArgumentNullException("item");
114 // Ensure we don't enqueue new items if we're being changed programmatically.
115 if (_disableChangeTracking)
117 return;
120 // Check to see if this new item can be merged with previous.
121 bool merged = false;
122 if (_undoStack.Count > 0)
124 CommandItem prev = _undoStack.Peek();
125 merged = prev.Merge(item);
128 // If not, append the new command item
129 if (!merged)
131 _undoStack.Push(item);
134 //clear the redo stack
135 if (_redoStack.Count > 0)
137 _redoStack.Clear();
141 /// <summary>
142 /// Implementation
143 /// </summary>
144 private StrokeCollection _strokeCollection;
146 private Stack<CommandItem> _undoStack;
147 private Stack<CommandItem> _redoStack;
150 bool _disableChangeTracking; // reentrancy guard: disables tracking of programmatic changes
151 // (eg, in response to undo/redo ops)
154 /// <summary>
155 /// Derive from this class for every undoable/redoable operation you wish to support.
156 /// </summary>
157 abstract class CommandItem
160 // Interface
161 public abstract void Undo();
162 public abstract void Redo();
165 // Allows multiple subsequent commands of the same type to roll-up into one
166 // logical undoable/redoable command -- return false if newitem is incompatable.
167 public abstract bool Merge(CommandItem newitem);
169 // Implementation
170 protected CommandStack _commandStack;
172 protected CommandItem(CommandStack commandStack)
174 _commandStack = commandStack;
178 /// <summary>
179 /// This operation covers collecting new strokes, stroke-erase, and point-erase.
180 /// </summary>
181 class StrokesAddedOrRemovedCI : CommandItem
183 InkCanvasEditingMode _editingMode;
184 StrokeCollection _added, _removed;
185 int _editingOperationCount;
187 public StrokesAddedOrRemovedCI(CommandStack commandStack, InkCanvasEditingMode editingMode, StrokeCollection added, StrokeCollection removed, int editingOperationCount)
188 : base(commandStack)
190 _editingMode = editingMode;
192 _added = added;
193 _removed = removed;
195 _editingOperationCount = editingOperationCount;
198 public override void Undo()
200 _commandStack.StrokeCollection.Remove(_added);
201 _commandStack.StrokeCollection.Add(_removed);
204 public override void Redo()
206 _commandStack.StrokeCollection.Add(_added);
207 _commandStack.StrokeCollection.Remove(_removed);
210 public override bool Merge(CommandItem newitem)
212 StrokesAddedOrRemovedCI newitemx = newitem as StrokesAddedOrRemovedCI;
214 if (newitemx == null ||
215 newitemx._editingMode != _editingMode ||
216 newitemx._editingOperationCount != _editingOperationCount)
218 return false;
221 // We only implement merging for repeated point-erase operations.
222 if (_editingMode != InkCanvasEditingMode.EraseByPoint) return false;
223 if (newitemx._editingMode != InkCanvasEditingMode.EraseByPoint) return false;
225 // Note: possible for point-erase to have hit intersection of >1 strokes!
226 // For each newly hit stroke, merge results into this command item.
227 foreach (Stroke doomed in newitemx._removed)
229 if (_added.Contains(doomed))
231 _added.Remove(doomed);
233 else
235 _removed.Add(doomed);
238 _added.Add(newitemx._added);
240 return true;
244 /// <summary>
245 /// This operation covers move and resize operations.
246 /// </summary>
247 class SelectionMovedOrResizedCI : CommandItem
249 StrokeCollection _selection;
250 Rect _newrect, _oldrect;
251 int _editingOperationCount;
253 public SelectionMovedOrResizedCI(CommandStack commandStack, StrokeCollection selection, Rect newrect, Rect oldrect, int editingOperationCount)
254 : base(commandStack)
256 _selection = selection;
257 _newrect = newrect;
258 _oldrect = oldrect;
259 _editingOperationCount = editingOperationCount;
262 public override void Undo()
264 Matrix m = GetTransformFromRectToRect(_newrect, _oldrect);
265 _selection.Transform(m, false);
268 public override void Redo()
270 Matrix m = GetTransformFromRectToRect(_oldrect, _newrect);
271 _selection.Transform(m, false);
274 public override bool Merge(CommandItem newitem)
276 SelectionMovedOrResizedCI newitemx = newitem as SelectionMovedOrResizedCI;
278 // Ensure items are of the same type.
279 if (newitemx == null ||
280 newitemx._editingOperationCount != _editingOperationCount ||
281 !StrokeCollectionsAreEqual(newitemx._selection, _selection))
283 return false;
286 // Keep former oldrect, latter newrect.
287 _newrect = newitemx._newrect;
289 return true;
292 static Matrix GetTransformFromRectToRect(Rect src, Rect dst)
294 Matrix m = Matrix.Identity;
295 m.Translate(-src.X, -src.Y);
296 m.Scale(dst.Width / src.Width, dst.Height / src.Height);
297 m.Translate(+dst.X, +dst.Y);
298 return m;
301 static bool StrokeCollectionsAreEqual(StrokeCollection a, StrokeCollection b)
303 if (a == null && b == null) return true;
304 if (a == null || b == null) return false;
305 if (a.Count != b.Count) return false;
307 for (int i = 0; i < a.Count; ++i)
308 if (a[i] != b[i]) return false;
310 return true;