Changeset 4068 for trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/UndoRedo/UndoManager.cs
- Timestamp:
- 07/22/10 00:44:01 (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/UndoRedo/UndoManager.cs
r2768 r4068 1 1 2 2 using System; 3 using System.Collections;4 3 using System.Diagnostics; 5 using System.Collections.Generic; 6 namespace 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 4 namespace 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; 74 95 } 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 } 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 } 409 371 } 410 372
Note: See TracChangeset
for help on using the changeset viewer.