Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/UndoRedo/UndoManager.cs

Last change on this file was 4068, checked in by swagner, 14 years ago

Sorted usings and removed unused usings in entire solution (#1094)

File size: 11.2 KB
Line 
1
2using System;
3using System.Diagnostics;
4namespace Netron.Diagramming.Core {
5  /// <summary>
6  /// UndoManager is a concrete class that maintains the undo list
7  /// and redo stack data structures. It also provides methods that
8  /// tell you whether there is something to undo or redo. The class
9  /// is designed to be used directly in undo/redo menu item handlers,
10  /// and undo/redo menu item state update functions.
11  /// </summary>
12  public class UndoManager : IUndoSupport {
13    /// <summary>
14    /// Occurs when the undo/redo history has changed.
15    /// </summary>
16    public event EventHandler OnHistoryChange;
17
18    #region Fields
19
20    /// <summary>
21    /// the max level to keep history
22    /// </summary>
23    private int undoLevel;
24    /// <summary>
25    /// the undo list
26    /// </summary>
27    private UndoCollection undoList;
28
29
30    /// <summary>
31    /// the internal stack of redoable operations
32    /// </summary>
33    private StackBase<CommandInfo> redoStack;
34
35    #endregion
36
37    #region Constructor
38    /// <summary>
39    /// Constructor which initializes the manager with up to 8 levels
40    /// of undo/redo.
41    /// </summary>
42    /// <param name="level">the undo level</param>
43    public UndoManager(int level) {
44      undoLevel = level;
45      undoList = new UndoCollection();
46      redoStack = new StackBase<CommandInfo>();
47      // The following events are linked in the sense that when an item
48      // is popped from the stack it'll be put in the undo collection
49      // again. So, strictly speaking, you need only two of the four
50      // events to make the surface aware of the history changes, but
51      // we'll keep it as is. It depends on your own perception of the
52      // undo/reo process.
53      redoStack.OnItemPopped +=
54          new EventHandler<CollectionEventArgs<CommandInfo>>(
55          HistoryChanged);
56
57      redoStack.OnItemPushed +=
58          new EventHandler<CollectionEventArgs<CommandInfo>>(
59          HistoryChanged);
60
61      undoList.OnItemAdded +=
62          new EventHandler<CollectionEventArgs<CommandInfo>>(
63          HistoryChanged);
64
65      undoList.OnItemRemoved +=
66          new EventHandler<CollectionEventArgs<CommandInfo>>(
67          HistoryChanged);
68
69    }
70
71
72
73    private void HistoryChanged(object sender, CollectionEventArgs<CommandInfo> e) {
74      RaiseHistoryChange();
75    }
76
77    #endregion
78
79    #region Properties
80    /// <summary>
81    /// Property for the maximum undo level.
82    /// </summary>
83    public int MaxUndoLevel {
84      get {
85        return undoLevel;
86      }
87      set {
88        Debug.Assert(value >= 0);
89
90        // To keep things simple, if you change the undo level,
91        // we clear all outstanding undo/redo commands.
92        if (value != undoLevel) {
93          ClearUndoRedo();
94          undoLevel = value;
95        }
96      }
97    }
98
99    /// <summary>
100    /// Gets the undo list.
101    /// </summary>
102    /// <value>The undo list.</value>
103    internal UndoCollection UndoList {
104      get { return undoList; }
105    }
106    #endregion
107
108    #region Methods
109    /// <summary>
110    /// Raises the OnHistoryChange event
111    /// </summary>
112    private void RaiseHistoryChange() {
113      if (OnHistoryChange != null)
114        OnHistoryChange(this, EventArgs.Empty);
115    }
116    /// <summary>
117    /// Register a new undo command. Use this method after your
118    /// application has performed an operation/command that is
119    /// undoable.
120    /// </summary>
121    /// <param name="cmd">New command to add to the manager.</param>
122    public void AddUndoCommand(ICommand cmd) {
123      Debug.Assert(cmd != null);
124      Debug.Assert(undoList.Count <= undoLevel);
125
126      if (undoLevel == 0)
127        return;
128
129      CommandInfo info = null;
130      if (undoList.Count == undoLevel) {
131        // Remove the oldest entry from the undo list to make room.
132        info = (CommandInfo)undoList[0];
133        undoList.RemoveAt(0);
134      }
135
136      // Insert the new undoable command into the undo list.
137      if (info == null)
138        info = new CommandInfo();
139      info.Command = cmd;
140      info.Handler = null;
141      undoList.Add(info);
142
143      // Clear the redo stack.
144      ClearRedo();
145    }
146
147    /// <summary>
148    /// Register a new undo command along with an undo handler. The
149    /// undo handler is used to perform the actual undo or redo
150    /// operation later when requested.
151    /// </summary>
152    /// <param name="cmd">New command to add to the manager.</param>
153    /// <param name="undoHandler">Undo handler to perform the actual undo/redo operation.</param>
154    public void AddUndoCommand(ICommand cmd, IUndoSupport undoHandler) {
155      AddUndoCommand(cmd);
156
157      if (undoList.Count > 0) {
158        CommandInfo info = (CommandInfo)undoList[undoList.Count - 1];
159        Debug.Assert(info != null);
160        info.Handler = undoHandler;
161      }
162    }
163
164    /// <summary>
165    /// Clear the internal undo/redo data structures. Use this method
166    /// when your application performs an operation that cannot be undone.
167    /// For example, when the user "saves" or "commits" all the changes in
168    /// the application.
169    /// </summary>
170    public void ClearUndoRedo() {
171      ClearUndo();
172      ClearRedo();
173    }
174
175    /// <summary>
176    /// Check if there is something to undo. Use this method to decide
177    /// whether your application's "Undo" menu item should be enabled
178    /// or disabled.
179    /// </summary>
180    /// <returns>Returns true if there is something to undo, false otherwise.</returns>
181    public bool CanUndo() {
182      return undoList.Count > 0;
183    }
184
185    /// <summary>
186    /// Check if there is something to redo. Use this method to decide
187    /// whether your application's "Redo" menu item should be enabled
188    /// or disabled.
189    /// </summary>
190    /// <returns>Returns true if there is something to redo, false otherwise.</returns>
191    public bool CanRedo() {
192      return redoStack.Count > 0;
193    }
194
195    /// <summary>
196    /// Perform the undo operation. If an undo handler is specified, it
197    /// will be used to perform the actual operation. Otherwise, the command
198    /// instance is asked to perform the undo.
199    /// </summary>
200    public void Undo() {
201      if (!CanUndo())
202        return;
203
204      // Remove newest entry from the undo list.
205      CommandInfo info = (CommandInfo)undoList[undoList.Count - 1];
206      undoList.RemoveAt(undoList.Count - 1);
207
208      // Perform the undo.
209      Debug.Assert(info.Command != null);
210
211      info.Command.Undo();
212
213      // Now the command is available for redo. Push it onto
214      // the redo stack.
215      redoStack.Push(info);
216    }
217
218    /// <summary>
219    /// Perform the redo operation. If an undo handler is specified, it
220    /// will be used to perform the actual operation. Otherwise, the command
221    /// instance is asked to perform the redo.
222    /// </summary>
223    public void Redo() {
224      if (!CanRedo())
225        return;
226
227      // Remove newest entry from the redo stack.
228      CommandInfo info = (CommandInfo)redoStack.Pop();
229
230      // Perform the redo.
231      Debug.Assert(info.Command != null);
232
233      info.Command.Redo();
234
235      // Now the command is available for undo again. Put it back
236      // into the undo list.
237      undoList.Add(info);
238    }
239
240    /// <summary>
241    /// Get the text value of the next undo command. Use this method
242    /// to update the Text property of your "Undo" menu item if
243    /// desired. For example, the text value for a command might be
244    /// "Draw Circle". This allows you to change your menu item Text
245    /// property to "Undo Draw Circle".
246    /// </summary>
247    /// <returns>Text value of the next undo command.</returns>
248    public string UndoText {
249      get {
250        ICommand cmd = NextUndoCommand;
251        if (cmd == null)
252          return "";
253        return cmd.Text;
254      }
255    }
256
257    /// <summary>
258    /// <para>
259    /// Get the text value of the next redo command. Use this method
260    /// to update the Text property of your "Redo" menu item if desired.
261    /// For example, the text value for a command might be "Draw Line".
262    /// This allows you to change your menu item text to "Redo Draw Line".
263    /// </para>
264    /// </summary>
265    /// <value>The redo text.</value>
266    /// <returns>Text value of the next redo command.</returns>
267    public string RedoText {
268      get {
269        ICommand cmd = NextRedoCommand;
270        if (cmd == null)
271          return "";
272        return cmd.Text;
273      }
274    }
275
276    /// <summary>
277    /// Get the next (or newest) undo command. This is like a "Peek"
278    /// method. It does not remove the command from the undo list.
279    /// </summary>
280    /// <returns>The next undo command.</returns>
281    public ICommand NextUndoCommand {
282      get {
283        if (undoList.Count == 0)
284          return null;
285        CommandInfo info = (CommandInfo)undoList[undoList.Count - 1];
286        return info.Command;
287      }
288    }
289
290    /// <summary>
291    /// Get the next redo command. This is like a "Peek"
292    /// method. It does not remove the command from the redo stack.
293    /// </summary>
294    /// <returns>The next redo command.</returns>
295    public ICommand NextRedoCommand {
296      get {
297        if (redoStack.Count == 0)
298          return null;
299        CommandInfo info = (CommandInfo)redoStack.Peek();
300        return info.Command;
301      }
302
303    }
304
305    /// <summary>
306    /// Retrieve all of the undo commands. Useful for debugging,
307    /// to analyze the contents of the undo list.
308    /// </summary>
309    /// <returns>Array of commands for undo.</returns>
310    public ICommand[] GetUndoCommands() {
311      if (undoList.Count == 0)
312        return null;
313
314      ICommand[] cmdList = new ICommand[undoList.Count];
315      object[] objList = undoList.ToArray();
316      for (int i = 0; i < objList.Length; i++) {
317        CommandInfo info = (CommandInfo)objList[i];
318        cmdList[i] = info.Command;
319      }
320
321      return cmdList;
322    }
323
324    /// <summary>
325    /// Retrieve all of the redo commands. Useful for debugging,
326    /// to analyze the contents of the redo stack.
327    /// </summary>
328    /// <returns>Array of commands for redo.</returns>
329    public ICommand[] GetRedoCommands() {
330      if (redoStack.Count == 0)
331        return null;
332
333      ICommand[] cmdList = new ICommand[redoStack.Count];
334      object[] objList = redoStack.ToArray();
335      for (int i = 0; i < objList.Length; i++) {
336        CommandInfo info = (CommandInfo)objList[i];
337        cmdList[i] = info.Command;
338      }
339
340      return cmdList;
341    }
342
343    /// <summary>
344    /// Clear the contents of the undo list.
345    /// </summary>
346    private void ClearUndo() {
347      while (undoList.Count > 0) {
348        CommandInfo info = (CommandInfo)undoList[undoList.Count - 1];
349        undoList.RemoveAt(undoList.Count - 1);
350        info.Command = null;
351        info.Handler = null;
352      }
353    }
354
355    /// <summary>
356    /// Clear the contents of the redo stack.
357    /// </summary>
358    private void ClearRedo() {
359      while (redoStack.Count > 0) {
360        CommandInfo info = (CommandInfo)redoStack.Pop();
361        info.Command = null;
362        info.Handler = null;
363      }
364    }
365    #endregion
366  }
367
368  class UndoCollection : CollectionBase<CommandInfo> {
369
370  }
371}
372
373
Note: See TracBrowser for help on using the repository browser.