Merge pull request #1466 from schani/stage-unified-card-table-scanning
[mono.git] / mcs / class / System.Design / System.ComponentModel.Design / UndoEngine.cs
1 //
2 // System.ComponentModel.Design.UndoEngine.cs
3 //
4 // Author:
5 //      Ivan N. Zlatev  <contact@i-nz.net>
6 //
7 // Copyright (C) 2007 Ivan N. Zlatev <contact@i-nz.net>
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.ComponentModel;
33 using System.ComponentModel.Design;
34 using System.Collections.Generic;
35 using System.ComponentModel.Design.Serialization;
36
37 namespace System.ComponentModel.Design
38 {
39         public abstract class UndoEngine : IDisposable
40         {
41                 private bool _undoing;
42                 private UndoUnit _currentUnit;
43                 private IServiceProvider _provider;
44                 private bool _enabled;
45
46                 protected UndoEngine (IServiceProvider provider)
47                 {
48                         if (provider == null)
49                                 throw new ArgumentNullException ("provider");
50
51                         _provider = provider;
52                         _currentUnit = null;
53                         Enable ();
54                 }
55
56                 private void Enable ()
57                 {
58                         if (!_enabled) {
59                                 IComponentChangeService changeService = GetRequiredService (typeof (IComponentChangeService)) as IComponentChangeService;
60                                 changeService.ComponentAdding += new ComponentEventHandler (OnComponentAdding);
61                                 changeService.ComponentAdded += new ComponentEventHandler (OnComponentAdded);
62                                 changeService.ComponentRemoving += new ComponentEventHandler (OnComponentRemoving);
63                                 changeService.ComponentRemoved += new ComponentEventHandler (OnComponentRemoved);
64                                 changeService.ComponentChanging += new ComponentChangingEventHandler (OnComponentChanging);
65                                 changeService.ComponentChanged += new ComponentChangedEventHandler (OnComponentChanged);
66                                 changeService.ComponentRename += new ComponentRenameEventHandler (OnComponentRename);
67
68                                 IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
69                                 host.TransactionClosed += new DesignerTransactionCloseEventHandler (OnTransactionClosed);
70                                 host.TransactionOpened += new EventHandler (OnTransactionOpened);
71
72                                 _enabled = true;
73                         }
74                 }
75
76                 private void Disable ()
77                 {
78                         if (_enabled) {
79                                 IComponentChangeService changeService = GetRequiredService (typeof (IComponentChangeService)) as IComponentChangeService;
80                                 changeService.ComponentAdding -= new ComponentEventHandler (OnComponentAdding);
81                                 changeService.ComponentAdded -= new ComponentEventHandler (OnComponentAdded);
82                                 changeService.ComponentRemoving -= new ComponentEventHandler (OnComponentRemoving);
83                                 changeService.ComponentRemoved -= new ComponentEventHandler (OnComponentRemoved);
84                                 changeService.ComponentChanging -= new ComponentChangingEventHandler (OnComponentChanging);
85                                 changeService.ComponentChanged -= new ComponentChangedEventHandler (OnComponentChanged);
86                                 changeService.ComponentRename -= new ComponentRenameEventHandler (OnComponentRename);
87
88                                 IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
89                                 host.TransactionClosed -= new DesignerTransactionCloseEventHandler (OnTransactionClosed);
90                                 host.TransactionOpened -= new EventHandler (OnTransactionOpened);
91
92                                 _enabled = false;
93                         }
94                 }
95
96                 // FIXME: there could be more transactions opened and closed (but not commited) after the first one!!!
97                 // This means that there should be multiple units. Only the top level transaction is commited though
98                 // 
99                 private void OnTransactionOpened (object sender, EventArgs args)
100                 {
101                         if (_currentUnit == null) {
102                                 IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
103                                 _currentUnit = CreateUndoUnit (host.TransactionDescription, true);
104                         }
105                 }
106
107
108                 private void OnTransactionClosed (object sender, DesignerTransactionCloseEventArgs args)
109                 {
110                         // Console.WriteLine ("TransactionClosed: Commited: " + args.TransactionCommitted.ToString ());
111                         IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
112                         if (!host.InTransaction) { // the "top-most"/last transaction was closed (currentUnit one)
113                                 _currentUnit.Close ();
114                                 if (args.TransactionCommitted) {
115                                         AddUndoUnit (_currentUnit);
116                                 } else {
117                                         _currentUnit.Undo ();
118                                         DiscardUndoUnit (_currentUnit);
119                                 }
120                                 _currentUnit = null;
121                         }
122                 }
123
124                 private void OnComponentAdding (object sender, ComponentEventArgs args)
125                 {
126                         if (_currentUnit == null)
127                                 _currentUnit = CreateUndoUnit ("Add " + args.Component.GetType ().Name, true);
128                         _currentUnit.ComponentAdding (args);
129                 }
130
131                 private void OnComponentAdded (object sender, ComponentEventArgs args)
132                 {
133                         if (_currentUnit == null)
134                                 _currentUnit = CreateUndoUnit ("Add " + args.Component.Site.Name, true);
135                         _currentUnit.ComponentAdded (args);
136
137                         IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
138                         if (!host.InTransaction) {
139                                 _currentUnit.Close ();
140                                 AddUndoUnit (_currentUnit);
141                                 _currentUnit = null;
142                         }
143                 }
144
145                 private void OnComponentRemoving (object sender, ComponentEventArgs args)
146                 {
147                         if (_currentUnit == null)
148                                 _currentUnit = CreateUndoUnit ("Remove " + args.Component.Site.Name, true);
149                         _currentUnit.ComponentRemoving (args);
150                 }
151
152                 private void OnComponentRemoved (object sender, ComponentEventArgs args)
153                 {
154                         if (_currentUnit == null)
155                                 _currentUnit = CreateUndoUnit ("Remove " + args.Component.GetType ().Name, true);
156                         _currentUnit.ComponentRemoved (args);
157
158                         IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
159                         if (!host.InTransaction) {
160                                 _currentUnit.Close ();
161                                 AddUndoUnit (_currentUnit);
162                                 _currentUnit = null;
163                         }
164                 }
165
166                 private void OnComponentChanging (object sender, ComponentChangingEventArgs args)
167                 {
168                         if (_currentUnit == null)
169                                 _currentUnit = CreateUndoUnit ("Modify " + ((IComponent)args.Component).Site.Name + 
170                                                                (args.Member != null ? "." + args.Member.Name : ""), 
171                                                                true);
172                         _currentUnit.ComponentChanging (args);
173                 }
174
175                 private void OnComponentChanged (object sender, ComponentChangedEventArgs args)
176                 {
177                         if (_currentUnit == null)
178                                 _currentUnit = CreateUndoUnit ("Modify " + ((IComponent)args.Component).Site.Name + "." + 
179                                                                (args.Member != null ? "." + args.Member.Name : ""), 
180                                                                true);
181                         _currentUnit.ComponentChanged (args);
182
183                         IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
184                         if (!host.InTransaction) {
185                                 _currentUnit.Close ();
186                                 AddUndoUnit (_currentUnit);
187                                 _currentUnit = null;
188                         }
189                 }
190
191                 private void OnComponentRename (object sender, ComponentRenameEventArgs args)
192                 {
193                         if (_currentUnit == null)
194                                 _currentUnit = CreateUndoUnit ("Rename " + ((IComponent)args.Component).Site.Name, true);
195                         _currentUnit.ComponentRename (args);
196
197                         IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
198                         if (!host.InTransaction) {
199                                 _currentUnit.Close ();
200                                 AddUndoUnit (_currentUnit);
201                                 _currentUnit = null;
202                         }
203                 }
204
205                 public event EventHandler Undoing;
206                 public event EventHandler Undone;
207
208                 public bool Enabled {
209                         get { return _enabled; }
210                         set {
211                                 if (value)
212                                         Enable ();
213                                 else
214                                         Disable ();
215                         }
216                 }
217
218                 public bool UndoInProgress {
219                         get { return _undoing; }
220                 }
221
222                 protected virtual UndoEngine.UndoUnit CreateUndoUnit (string name, bool primary)
223                 {
224                         // Console.WriteLine ("CreateUndoUnit: " + name);
225                         return new UndoUnit (this, name);
226                 }
227
228                 public void Dispose ()
229                 {
230                         Dispose (true);
231                 }
232
233                 protected virtual void Dispose (bool disposing)
234                 {
235                         if (disposing) {
236                                 if (_currentUnit != null) {
237                                         _currentUnit.Close ();
238                                         _currentUnit = null;
239                                 }
240                         }
241                 }
242
243                 protected object GetRequiredService (Type serviceType)
244                 {
245                         object service = this.GetService (serviceType);
246                         if (service == null)
247                                 throw new NotSupportedException ("Service '" + serviceType.Name + "' missing");
248                         return service;
249                 }
250
251                 protected object GetService (Type serviceType)
252                 {
253                         if (serviceType == null)
254                                 throw new ArgumentNullException ("serviceType");
255
256                         if (_provider != null)
257                                 return _provider.GetService (serviceType);
258                         return null;
259                 }
260
261                 protected virtual void OnUndoing (EventArgs e)
262                 {
263                         Disable ();
264                         _undoing = true;
265                         if (Undoing != null)
266                                 Undoing (this, e);
267                 }
268
269                 protected virtual void OnUndone (EventArgs e)
270                 {
271                         Enable ();
272                         _undoing = false;
273                         if (Undone != null)
274                                 Undone (this, e);
275                 }
276
277
278                 protected abstract void AddUndoUnit (UndoEngine.UndoUnit unit);
279
280                 protected virtual void DiscardUndoUnit (UndoEngine.UndoUnit unit)
281                 {
282                         // Console.WriteLine ("DiscardUndoUnit: " + unit.Name);
283                 }
284
285
286                 protected class UndoUnit
287                 {
288                         private class Action
289                         {
290                                 public virtual void Undo (UndoEngine engine)
291                                 {
292                                 }
293                         }
294
295                         private class ComponentRenameAction : Action
296                         {
297                                 private string _oldName;
298                                 private string _currentName;
299
300                                 public ComponentRenameAction (string currentName, string oldName)
301                                 {
302                                         // Console.WriteLine ("ComponentRenameAction (" + oldName + "): " + currentName);
303                                         _currentName = currentName;
304                                         _oldName = oldName;
305                                 }
306
307                                 public override void Undo (UndoEngine engine)
308                                 {
309                                         // Console.WriteLine ("ComponentRenameAction.Undo (" + _currentName + "): " + _oldName);
310                                         IDesignerHost host = engine.GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
311                                         IComponent component = host.Container.Components[_currentName];
312                                         component.Site.Name = _oldName;
313                                         string tmp = _currentName;
314                                         _currentName = _oldName;
315                                         _oldName = tmp;
316                                 }
317                         } // ComponentRenameAction
318
319                         private class ComponentAddRemoveAction : Action
320                         {
321                                 private string _componentName;
322                                 private SerializationStore _serializedComponent;
323                                 private bool _added;
324
325                                 public ComponentAddRemoveAction (UndoEngine engine, IComponent component, bool added)
326                                 {
327                                         if (component == null)
328                                                 throw new ArgumentNullException ("component");
329                                         // Console.WriteLine ((added ? "Component*Add*RemoveAction" : "ComponentAdd*Remove*Action") +
330                                         //                 " (" + component.Site.Name + ")");
331                                         ComponentSerializationService serializationService = engine.GetRequiredService (
332                                                 typeof (ComponentSerializationService)) as ComponentSerializationService;
333
334                                         _serializedComponent = serializationService.CreateStore ();
335                                         serializationService.Serialize (_serializedComponent, component);
336                                         _serializedComponent.Close ();
337
338                                         _added = added;
339                                         _componentName = component.Site.Name;
340                                 }
341
342                                 public override void Undo (UndoEngine engine)
343                                 {
344                                         IDesignerHost host = engine.GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
345                                         if (_added) {
346                                                 // Console.WriteLine ("Component*Add*RemoveAction.Undo (" + _componentName + ")");
347                                                 IComponent component = host.Container.Components[_componentName];
348                                                 if (component != null) // the component might have been destroyed already
349                                                         host.DestroyComponent (component);
350                                                 _added = false;
351                                         } else {
352                                                 // Console.WriteLine ("ComponentAdd*Remove*Action.Undo (" + _componentName + ")");
353                                                 ComponentSerializationService serializationService = engine.GetRequiredService (
354                                                         typeof (ComponentSerializationService)) as ComponentSerializationService;
355
356                                                 serializationService.DeserializeTo (_serializedComponent, host.Container);
357                                                 _added = true;
358                                         }
359                                 }
360                         } // ComponentAddRemoveAction
361
362
363                         private class ComponentChangeAction : Action
364                         {
365                                 private string _componentName;
366                                 private MemberDescriptor _member;
367                                 private IComponent _component;
368                                 private SerializationStore _afterChange;
369                                 private SerializationStore _beforeChange;
370
371                                 public ComponentChangeAction ()
372                                 {
373                                 }
374
375                                 public void SetOriginalState (UndoEngine engine, IComponent component, MemberDescriptor member)
376                                 {
377                                         _member = member;
378                                         _component = component;
379                                         _componentName = component.Site != null ? component.Site.Name : null;
380                                         // Console.WriteLine ("ComponentChangeAction.SetOriginalState (" + (_componentName != null ? (_componentName + ".") : "") +
381                                         //                 member.Name + "): " +
382                                         //                 (((PropertyDescriptor)member).GetValue (component) == null ? "null" :
383                                         //                 ((PropertyDescriptor)member).GetValue (component).ToString ()));
384                                         ComponentSerializationService serializationService = engine.GetRequiredService (
385                                                 typeof (ComponentSerializationService)) as ComponentSerializationService;
386                                         _beforeChange = serializationService.CreateStore ();
387                                         serializationService.SerializeMemberAbsolute (_beforeChange, component, member);
388                                         _beforeChange.Close ();
389                                 }
390
391
392                                 public void SetModifiedState (UndoEngine engine, IComponent component, MemberDescriptor member)
393                                 {
394                                         // Console.WriteLine ("ComponentChangeAction.SetModifiedState (" + (_componentName != null ? (_componentName + ".") : "") +
395                                         //                 member.Name + "): " +
396                                         //                 (((PropertyDescriptor)member).GetValue (component) == null ? "null" :
397                                         //                 ((PropertyDescriptor)member).GetValue (component).ToString ()));
398                                         ComponentSerializationService serializationService = engine.GetRequiredService (
399                                                 typeof (ComponentSerializationService)) as ComponentSerializationService;
400                                         _afterChange = serializationService.CreateStore ();
401                                         serializationService.SerializeMemberAbsolute (_afterChange, component, member);
402                                         _afterChange.Close ();
403                                 }
404
405                                 public bool IsComplete {
406                                         get { return (_beforeChange != null && _afterChange != null); }
407                                 }
408
409                                 public string ComponentName {
410                                         get { return _componentName; }
411                                 }
412
413                                 public IComponent Component {
414                                         get { return _component; }
415                                 }
416
417                                 public MemberDescriptor Member {
418                                         get { return _member; }
419                                 }
420
421                                 // Reminder: _component might no longer be a valid instance
422                                 // so one should request a new one.
423                                 // 
424                                 public override void Undo (UndoEngine engine)
425                                 {
426                                         if (_beforeChange == null) {
427                                                 // Console.WriteLine ("ComponentChangeAction.Undo: ERROR: UndoUnit is not complete.");
428                                                 return;
429                                         }
430
431                                         // Console.WriteLine ("ComponentChangeAction.Undo (" + _componentName + "." + _member.Name + ")");
432                                         IDesignerHost host = (IDesignerHost)engine.GetRequiredService (typeof(IDesignerHost));
433                                         _component = host.Container.Components[_componentName];
434
435                                         ComponentSerializationService serializationService = engine.GetRequiredService (
436                                                 typeof (ComponentSerializationService)) as ComponentSerializationService;
437                                         serializationService.DeserializeTo (_beforeChange, host.Container);
438
439                                         SerializationStore tmp = _beforeChange;
440                                         _beforeChange = _afterChange;
441                                         _afterChange = tmp;
442                                 }
443                         } // ComponentChangeAction
444
445                         private UndoEngine _engine;
446                         private string _name;
447                         private bool _closed;
448                         private List<Action> _actions;
449
450                         public UndoUnit (UndoEngine engine, string name)
451                         {
452                                 if (engine == null)
453                                         throw new ArgumentNullException ("engine");
454                                 if (name == null)
455                                         throw new ArgumentNullException ("name");
456
457                                 _engine = engine;
458                                 _name = name;
459                                 _actions = new List <Action> ();
460                         }
461
462                         public void Undo ()
463                         {
464                                 _engine.OnUndoing (EventArgs.Empty);
465                                 UndoCore ();
466                                 _engine.OnUndone (EventArgs.Empty);
467                         }
468
469                         protected virtual void UndoCore ()
470                         {
471                                 for (int i = _actions.Count - 1; i >= 0; i--) {
472                                         // Console.WriteLine ("Undoing action type: " + _actions[i].GetType ().Name);
473                                         _actions[i].Undo (_engine);
474                                 }
475                                 // Also reverses the stack of actions, so that
476                                 // if Undo is called twice it will Redo in the proper order
477                                 // 
478                                 _actions.Reverse ();
479                         }
480
481                         protected UndoEngine UndoEngine {
482                                 get { return _engine; }
483                         }
484
485                         public virtual bool IsEmpty {
486                                 get { return _actions.Count == 0; }
487                         }
488
489                         public virtual string Name {
490                                 get { return _name; }
491                         }
492
493                         public virtual void Close ()
494                         {
495                                 // Console.WriteLine ("UndoUnot.Close (" + _name + ")");
496                                 _closed = true;
497                         }
498
499                         public virtual void ComponentAdded (ComponentEventArgs e)
500                         {
501                                 if (!_closed) {
502                                         // Console.WriteLine ("New Action: Component*Add*RemoveAction (" + ((IComponent)e.Component).Site.Name + ")");
503                                         _actions.Add (new ComponentAddRemoveAction (_engine, (IComponent) e.Component, true));
504                                 }
505                         }
506
507                         public virtual void ComponentAdding (ComponentEventArgs e)
508                         {
509                         }
510
511                         public virtual void ComponentChanged (ComponentChangedEventArgs e)
512                         {
513                                 if (_closed)
514                                         return;
515
516                                 // A component starts getting removed. 
517                                 // ComponentRemoving -> remove component -> ComponentRemoved
518                                 // The problem is that someone can subscribe to the Removed event after us (the UndoEngine) - e.g
519                                 // ParentControlDesigner will explicitly request (by setting it to null between Removing and Removed 
520                                 // the serialization of the Parent property of the removed child.
521                                 // In the case where someone subscribes after and performs changes to the component, we might get 
522                                 // ComponentChanged events after we've already created the addremove action, but the componentchangeaction
523                                 // will be incomplete standing before the addremove one.
524                                 //
525                                 ComponentChangeAction changeAction = null;
526                                 for (int i=0; i < _actions.Count; i++) {
527                                         changeAction = _actions[i] as ComponentChangeAction;
528                                         if (changeAction != null && !changeAction.IsComplete &&
529                                             changeAction.Component == e.Component &&
530                                             changeAction.Member.Equals (e.Member)) {
531                                                 changeAction.SetModifiedState (_engine, (IComponent) e.Component, e.Member);
532                                                 break;
533                                         }
534                                 }
535                         }
536
537                         public virtual void ComponentChanging (ComponentChangingEventArgs e)
538                         {
539                                 if (_closed)
540                                         return;
541
542                                 // Console.WriteLine ("New Action: ComponentChangeAction (" + ((IComponent)e.Component).Site.Name + ")");
543                                 ComponentChangeAction action = new ComponentChangeAction ();
544                                 action.SetOriginalState (_engine, (IComponent) e.Component, e.Member);
545                                 _actions.Add (action);
546                         }
547
548                         public virtual void ComponentRemoved (ComponentEventArgs e)
549                         {
550                         }
551
552                         public virtual void ComponentRemoving (ComponentEventArgs e)
553                         {
554                                 if (!_closed) {
555                                         // Console.WriteLine ("New Action: ComponentAdd*Remove*Action (" + ((IComponent)e.Component).Site.Name + ")");
556                                         _actions.Add (new ComponentAddRemoveAction (_engine, e.Component, false));
557                                 }
558                         }
559
560                         public virtual void ComponentRename (ComponentRenameEventArgs e)
561                         {
562                                 if (!_closed) {
563                                         // Console.WriteLine ("New Action: ComponentRenameAction (" + ((IComponent)e.Component).Site.Name + ")");
564                                         _actions.Add (new ComponentRenameAction (e.NewName, e.OldName));
565                                 }
566                         }
567
568                         protected object GetService (Type serviceType)
569                         {
570                                 return _engine.GetService (serviceType);
571                         }
572
573                         public override string ToString ()
574                         {
575                                 return _name;
576                         }
577                 }
578         }
579 }