Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/UndoRedo/UndoManager.cs @ 2768

Last change on this file since 2768 was 2768, checked in by mkommend, 14 years ago

added solution folders and sources for the netron library (ticket #867)

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