2 using System
.Collections
.Generic
;
4 using System
.Windows
.Controls
;
5 using System
.Windows
.Media
;
6 using System
.Windows
.Ink
;
8 namespace InkCanvasEditingModes
10 sealed class CommandStack
15 /// <param name="strokes"></param>
16 public CommandStack(StrokeCollection strokes
)
20 throw new ArgumentNullException("strokes");
22 _strokeCollection
= strokes
;
23 _undoStack
= new Stack
<CommandItem
>();
24 _redoStack
= new Stack
<CommandItem
>();
25 _disableChangeTracking
= false;
29 /// StrokeCollection to track changes for
31 public StrokeCollection StrokeCollection
35 return _strokeCollection
;
40 /// Only undo if there are more items in the stack to step back into.
44 get { return (_undoStack.Count > 0); }
48 /// Only undo if one or more steps back in the stack.
52 get { return (_redoStack.Count > 0); }
56 /// Add an item to the top of the command stack
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;
72 _disableChangeTracking
= false;
75 //place this item on the redo stack
76 _redoStack
.Push(item
);
80 /// Take the top item off the command stack.
84 if (!CanRedo
) throw new InvalidOperationException();
86 CommandItem item
= _redoStack
.Pop();
88 // Invoke the redo operation, with change-tracking temporarily suspended.
89 _disableChangeTracking
= true;
96 _disableChangeTracking
= false;
99 //place this item on the undo stack
100 _undoStack
.Push(item
);
104 /// Add a command item to the stack.
106 /// <param name="item"></param>
107 public void Enqueue(CommandItem item
)
111 throw new ArgumentNullException("item");
114 // Ensure we don't enqueue new items if we're being changed programmatically.
115 if (_disableChangeTracking
)
120 // Check to see if this new item can be merged with previous.
122 if (_undoStack
.Count
> 0)
124 CommandItem prev
= _undoStack
.Peek();
125 merged
= prev
.Merge(item
);
128 // If not, append the new command item
131 _undoStack
.Push(item
);
134 //clear the redo stack
135 if (_redoStack
.Count
> 0)
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)
155 /// Derive from this class for every undoable/redoable operation you wish to support.
157 abstract class CommandItem
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
);
170 protected CommandStack _commandStack
;
172 protected CommandItem(CommandStack commandStack
)
174 _commandStack
= commandStack
;
179 /// This operation covers collecting new strokes, stroke-erase, and point-erase.
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
)
190 _editingMode
= editingMode
;
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
)
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
);
235 _removed
.Add(doomed
);
238 _added
.Add(newitemx
._added
);
245 /// This operation covers move and resize operations.
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
)
256 _selection
= selection
;
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
))
286 // Keep former oldrect, latter newrect.
287 _newrect
= newitemx
._newrect
;
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
);
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;