Remove ChangeLog files from the repo
[mono.git] / mcs / class / System.Design / System.ComponentModel.Design / UndoEngine.cs
index d243911573bb3a39382dcfe154818411df109427..5f12c09ee2447795424a26d83e27e89cac8bacd7 100644 (file)
@@ -2,9 +2,9 @@
 // System.ComponentModel.Design.UndoEngine.cs
 //
 // Author:
-//      Atsushi Enomoto  <atsushi@ximian.com>
+//     Ivan N. Zlatev  <contact@i-nz.net>
 //
-// Copyright (C) 2007 Novell, Inc
+// Copyright (C) 2007 Ivan N. Zlatev <contact@i-nz.net>
 //
 
 //
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-#if NET_2_0
 
 using System;
 using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Collections.Generic;
+using System.ComponentModel.Design.Serialization;
 
 namespace System.ComponentModel.Design
 {
        public abstract class UndoEngine : IDisposable
        {
-               [MonoTODO]
+               private bool _undoing;
+               private UndoUnit _currentUnit;
+               private IServiceProvider _provider;
+               private bool _enabled;
+
                protected UndoEngine (IServiceProvider provider)
                {
-                       throw new NotImplementedException ();
+                       if (provider == null)
+                               throw new ArgumentNullException ("provider");
+
+                       _provider = provider;
+                       _currentUnit = null;
+                       Enable ();
+               }
+
+               private void Enable ()
+               {
+                       if (!_enabled) {
+                               IComponentChangeService changeService = GetRequiredService (typeof (IComponentChangeService)) as IComponentChangeService;
+                               changeService.ComponentAdding += new ComponentEventHandler (OnComponentAdding);
+                               changeService.ComponentAdded += new ComponentEventHandler (OnComponentAdded);
+                               changeService.ComponentRemoving += new ComponentEventHandler (OnComponentRemoving);
+                               changeService.ComponentRemoved += new ComponentEventHandler (OnComponentRemoved);
+                               changeService.ComponentChanging += new ComponentChangingEventHandler (OnComponentChanging);
+                               changeService.ComponentChanged += new ComponentChangedEventHandler (OnComponentChanged);
+                               changeService.ComponentRename += new ComponentRenameEventHandler (OnComponentRename);
+
+                               IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
+                               host.TransactionClosed += new DesignerTransactionCloseEventHandler (OnTransactionClosed);
+                               host.TransactionOpened += new EventHandler (OnTransactionOpened);
+
+                               _enabled = true;
+                       }
+               }
+
+               private void Disable ()
+               {
+                       if (_enabled) {
+                               IComponentChangeService changeService = GetRequiredService (typeof (IComponentChangeService)) as IComponentChangeService;
+                               changeService.ComponentAdding -= new ComponentEventHandler (OnComponentAdding);
+                               changeService.ComponentAdded -= new ComponentEventHandler (OnComponentAdded);
+                               changeService.ComponentRemoving -= new ComponentEventHandler (OnComponentRemoving);
+                               changeService.ComponentRemoved -= new ComponentEventHandler (OnComponentRemoved);
+                               changeService.ComponentChanging -= new ComponentChangingEventHandler (OnComponentChanging);
+                               changeService.ComponentChanged -= new ComponentChangedEventHandler (OnComponentChanged);
+                               changeService.ComponentRename -= new ComponentRenameEventHandler (OnComponentRename);
+
+                               IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
+                               host.TransactionClosed -= new DesignerTransactionCloseEventHandler (OnTransactionClosed);
+                               host.TransactionOpened -= new EventHandler (OnTransactionOpened);
+
+                               _enabled = false;
+                       }
+               }
+
+               // FIXME: there could be more transactions opened and closed (but not commited) after the first one!!!
+               // This means that there should be multiple units. Only the top level transaction is commited though
+               // 
+               private void OnTransactionOpened (object sender, EventArgs args)
+               {
+                       if (_currentUnit == null) {
+                               IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
+                               _currentUnit = CreateUndoUnit (host.TransactionDescription, true);
+                       }
+               }
+
+
+               private void OnTransactionClosed (object sender, DesignerTransactionCloseEventArgs args)
+               {
+                       // Console.WriteLine ("TransactionClosed: Commited: " + args.TransactionCommitted.ToString ());
+                       IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
+                       if (!host.InTransaction) { // the "top-most"/last transaction was closed (currentUnit one)
+                               _currentUnit.Close ();
+                               if (args.TransactionCommitted) {
+                                       AddUndoUnit (_currentUnit);
+                               } else {
+                                       _currentUnit.Undo ();
+                                       DiscardUndoUnit (_currentUnit);
+                               }
+                               _currentUnit = null;
+                       }
+               }
+
+               private void OnComponentAdding (object sender, ComponentEventArgs args)
+               {
+                       if (_currentUnit == null)
+                               _currentUnit = CreateUndoUnit ("Add " + args.Component.GetType ().Name, true);
+                       _currentUnit.ComponentAdding (args);
+               }
+
+               private void OnComponentAdded (object sender, ComponentEventArgs args)
+               {
+                       if (_currentUnit == null)
+                               _currentUnit = CreateUndoUnit ("Add " + args.Component.Site.Name, true);
+                       _currentUnit.ComponentAdded (args);
+
+                       IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
+                       if (!host.InTransaction) {
+                               _currentUnit.Close ();
+                               AddUndoUnit (_currentUnit);
+                               _currentUnit = null;
+                       }
+               }
+
+               private void OnComponentRemoving (object sender, ComponentEventArgs args)
+               {
+                       if (_currentUnit == null)
+                               _currentUnit = CreateUndoUnit ("Remove " + args.Component.Site.Name, true);
+                       _currentUnit.ComponentRemoving (args);
+               }
+
+               private void OnComponentRemoved (object sender, ComponentEventArgs args)
+               {
+                       if (_currentUnit == null)
+                               _currentUnit = CreateUndoUnit ("Remove " + args.Component.GetType ().Name, true);
+                       _currentUnit.ComponentRemoved (args);
+
+                       IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
+                       if (!host.InTransaction) {
+                               _currentUnit.Close ();
+                               AddUndoUnit (_currentUnit);
+                               _currentUnit = null;
+                       }
+               }
+
+               private void OnComponentChanging (object sender, ComponentChangingEventArgs args)
+               {
+                       if (_currentUnit == null)
+                               _currentUnit = CreateUndoUnit ("Modify " + ((IComponent)args.Component).Site.Name + 
+                                                              (args.Member != null ? "." + args.Member.Name : ""), 
+                                                              true);
+                       _currentUnit.ComponentChanging (args);
+               }
+
+               private void OnComponentChanged (object sender, ComponentChangedEventArgs args)
+               {
+                       if (_currentUnit == null)
+                               _currentUnit = CreateUndoUnit ("Modify " + ((IComponent)args.Component).Site.Name + "." + 
+                                                              (args.Member != null ? "." + args.Member.Name : ""), 
+                                                              true);
+                       _currentUnit.ComponentChanged (args);
+
+                       IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
+                       if (!host.InTransaction) {
+                               _currentUnit.Close ();
+                               AddUndoUnit (_currentUnit);
+                               _currentUnit = null;
+                       }
+               }
+
+               private void OnComponentRename (object sender, ComponentRenameEventArgs args)
+               {
+                       if (_currentUnit == null)
+                               _currentUnit = CreateUndoUnit ("Rename " + ((IComponent)args.Component).Site.Name, true);
+                       _currentUnit.ComponentRename (args);
+
+                       IDesignerHost host = GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
+                       if (!host.InTransaction) {
+                               _currentUnit.Close ();
+                               AddUndoUnit (_currentUnit);
+                               _currentUnit = null;
+                       }
                }
 
                public event EventHandler Undoing;
                public event EventHandler Undone;
 
-               [MonoTODO]
                public bool Enabled {
-                       get { throw new NotImplementedException (); }
-                       set { throw new NotImplementedException (); }
+                       get { return _enabled; }
+                       set {
+                               if (value)
+                                       Enable ();
+                               else
+                                       Disable ();
+                       }
                }
 
-               [MonoTODO]
                public bool UndoInProgress {
-                       get { throw new NotImplementedException (); }
+                       get { return _undoing; }
                }
 
-               protected abstract void AddUndoUnit (UndoEngine.UndoUnit unit);
-
-               [MonoTODO]
                protected virtual UndoEngine.UndoUnit CreateUndoUnit (string name, bool primary)
                {
-                       throw new NotImplementedException ();
-               }
-
-               [MonoTODO]
-               protected virtual void DiscardUndoUnit (UndoEngine.UndoUnit unit)
-               {
-                       throw new NotImplementedException ();
+                       // Console.WriteLine ("CreateUndoUnit: " + name);
+                       return new UndoUnit (this, name);
                }
 
                public void Dispose ()
@@ -75,132 +230,350 @@ namespace System.ComponentModel.Design
                        Dispose (true);
                }
 
-               [MonoTODO]
                protected virtual void Dispose (bool disposing)
                {
+                       if (disposing) {
+                               if (_currentUnit != null) {
+                                       _currentUnit.Close ();
+                                       _currentUnit = null;
+                               }
+                       }
                }
 
-               [MonoTODO]
                protected object GetRequiredService (Type serviceType)
                {
-                       throw new NotImplementedException ();
+                       object service = this.GetService (serviceType);
+                       if (service == null)
+                               throw new NotSupportedException ("Service '" + serviceType.Name + "' missing");
+                       return service;
                }
 
-               [MonoTODO]
                protected object GetService (Type serviceType)
                {
-                       throw new NotImplementedException ();
+                       if (serviceType == null)
+                               throw new ArgumentNullException ("serviceType");
+
+                       if (_provider != null)
+                               return _provider.GetService (serviceType);
+                       return null;
                }
 
-               [MonoTODO]
                protected virtual void OnUndoing (EventArgs e)
                {
-                       throw new NotImplementedException ();
+                       Disable ();
+                       _undoing = true;
+                       if (Undoing != null)
+                               Undoing (this, e);
                }
 
-               [MonoTODO]
                protected virtual void OnUndone (EventArgs e)
                {
-                       throw new NotImplementedException ();
+                       Enable ();
+                       _undoing = false;
+                       if (Undone != null)
+                               Undone (this, e);
                }
 
+
+               protected abstract void AddUndoUnit (UndoEngine.UndoUnit unit);
+
+               protected virtual void DiscardUndoUnit (UndoEngine.UndoUnit unit)
+               {
+                       // Console.WriteLine ("DiscardUndoUnit: " + unit.Name);
+               }
+
+
                protected class UndoUnit
                {
-                       UndoEngine engine;
-                       string name;
+                       private class Action
+                       {
+                               public virtual void Undo (UndoEngine engine)
+                               {
+                               }
+                       }
+
+                       private class ComponentRenameAction : Action
+                       {
+                               private string _oldName;
+                               private string _currentName;
+
+                               public ComponentRenameAction (string currentName, string oldName)
+                               {
+                                       // Console.WriteLine ("ComponentRenameAction (" + oldName + "): " + currentName);
+                                       _currentName = currentName;
+                                       _oldName = oldName;
+                               }
+
+                               public override void Undo (UndoEngine engine)
+                               {
+                                       // Console.WriteLine ("ComponentRenameAction.Undo (" + _currentName + "): " + _oldName);
+                                       IDesignerHost host = engine.GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
+                                       IComponent component = host.Container.Components[_currentName];
+                                       component.Site.Name = _oldName;
+                                       string tmp = _currentName;
+                                       _currentName = _oldName;
+                                       _oldName = tmp;
+                               }
+                       } // ComponentRenameAction
+
+                       private class ComponentAddRemoveAction : Action
+                       {
+                               private string _componentName;
+                               private SerializationStore _serializedComponent;
+                               private bool _added;
+
+                               public ComponentAddRemoveAction (UndoEngine engine, IComponent component, bool added)
+                               {
+                                       if (component == null)
+                                               throw new ArgumentNullException ("component");
+                                       // Console.WriteLine ((added ? "Component*Add*RemoveAction" : "ComponentAdd*Remove*Action") +
+                                       //                 " (" + component.Site.Name + ")");
+                                       ComponentSerializationService serializationService = engine.GetRequiredService (
+                                               typeof (ComponentSerializationService)) as ComponentSerializationService;
+
+                                       _serializedComponent = serializationService.CreateStore ();
+                                       serializationService.Serialize (_serializedComponent, component);
+                                       _serializedComponent.Close ();
+
+                                       _added = added;
+                                       _componentName = component.Site.Name;
+                               }
+
+                               public override void Undo (UndoEngine engine)
+                               {
+                                       IDesignerHost host = engine.GetRequiredService (typeof (IDesignerHost)) as IDesignerHost;
+                                       if (_added) {
+                                               // Console.WriteLine ("Component*Add*RemoveAction.Undo (" + _componentName + ")");
+                                               IComponent component = host.Container.Components[_componentName];
+                                               if (component != null) // the component might have been destroyed already
+                                                       host.DestroyComponent (component);
+                                               _added = false;
+                                       } else {
+                                               // Console.WriteLine ("ComponentAdd*Remove*Action.Undo (" + _componentName + ")");
+                                               ComponentSerializationService serializationService = engine.GetRequiredService (
+                                                       typeof (ComponentSerializationService)) as ComponentSerializationService;
+
+                                               serializationService.DeserializeTo (_serializedComponent, host.Container);
+                                               _added = true;
+                                       }
+                               }
+                       } // ComponentAddRemoveAction
+
+
+                       private class ComponentChangeAction : Action
+                       {
+                               private string _componentName;
+                               private MemberDescriptor _member;
+                               private IComponent _component;
+                               private SerializationStore _afterChange;
+                               private SerializationStore _beforeChange;
+
+                               public ComponentChangeAction ()
+                               {
+                               }
+
+                               public void SetOriginalState (UndoEngine engine, IComponent component, MemberDescriptor member)
+                               {
+                                       _member = member;
+                                       _component = component;
+                                       _componentName = component.Site != null ? component.Site.Name : null;
+                                       // Console.WriteLine ("ComponentChangeAction.SetOriginalState (" + (_componentName != null ? (_componentName + ".") : "") +
+                                       //                 member.Name + "): " +
+                                       //                 (((PropertyDescriptor)member).GetValue (component) == null ? "null" :
+                                       //                 ((PropertyDescriptor)member).GetValue (component).ToString ()));
+                                       ComponentSerializationService serializationService = engine.GetRequiredService (
+                                               typeof (ComponentSerializationService)) as ComponentSerializationService;
+                                       _beforeChange = serializationService.CreateStore ();
+                                       serializationService.SerializeMemberAbsolute (_beforeChange, component, member);
+                                       _beforeChange.Close ();
+                               }
+
+
+                               public void SetModifiedState (UndoEngine engine, IComponent component, MemberDescriptor member)
+                               {
+                                       // Console.WriteLine ("ComponentChangeAction.SetModifiedState (" + (_componentName != null ? (_componentName + ".") : "") +
+                                       //                 member.Name + "): " +
+                                       //                 (((PropertyDescriptor)member).GetValue (component) == null ? "null" :
+                                       //                 ((PropertyDescriptor)member).GetValue (component).ToString ()));
+                                       ComponentSerializationService serializationService = engine.GetRequiredService (
+                                               typeof (ComponentSerializationService)) as ComponentSerializationService;
+                                       _afterChange = serializationService.CreateStore ();
+                                       serializationService.SerializeMemberAbsolute (_afterChange, component, member);
+                                       _afterChange.Close ();
+                               }
+
+                               public bool IsComplete {
+                                       get { return (_beforeChange != null && _afterChange != null); }
+                               }
+
+                               public string ComponentName {
+                                       get { return _componentName; }
+                               }
+
+                               public IComponent Component {
+                                       get { return _component; }
+                               }
+
+                               public MemberDescriptor Member {
+                                       get { return _member; }
+                               }
+
+                               // Reminder: _component might no longer be a valid instance
+                               // so one should request a new one.
+                               // 
+                               public override void Undo (UndoEngine engine)
+                               {
+                                       if (_beforeChange == null) {
+                                               // Console.WriteLine ("ComponentChangeAction.Undo: ERROR: UndoUnit is not complete.");
+                                               return;
+                                       }
+
+                                       // Console.WriteLine ("ComponentChangeAction.Undo (" + _componentName + "." + _member.Name + ")");
+                                       IDesignerHost host = (IDesignerHost)engine.GetRequiredService (typeof(IDesignerHost));
+                                       _component = host.Container.Components[_componentName];
+
+                                       ComponentSerializationService serializationService = engine.GetRequiredService (
+                                               typeof (ComponentSerializationService)) as ComponentSerializationService;
+                                       serializationService.DeserializeTo (_beforeChange, host.Container);
+
+                                       SerializationStore tmp = _beforeChange;
+                                       _beforeChange = _afterChange;
+                                       _afterChange = tmp;
+                               }
+                       } // ComponentChangeAction
+
+                       private UndoEngine _engine;
+                       private string _name;
+                       private bool _closed;
+                       private List<Action> _actions;
 
-                       [MonoTODO]
                        public UndoUnit (UndoEngine engine, string name)
                        {
-                               this.engine = engine;
-                               this.name = name;
+                               if (engine == null)
+                                       throw new ArgumentNullException ("engine");
+                               if (name == null)
+                                       throw new ArgumentNullException ("name");
+
+                               _engine = engine;
+                               _name = name;
+                               _actions = new List <Action> ();
+                       }
+
+                       public void Undo ()
+                       {
+                               _engine.OnUndoing (EventArgs.Empty);
+                               UndoCore ();
+                               _engine.OnUndone (EventArgs.Empty);
+                       }
+
+                       protected virtual void UndoCore ()
+                       {
+                               for (int i = _actions.Count - 1; i >= 0; i--) {
+                                       // Console.WriteLine ("Undoing action type: " + _actions[i].GetType ().Name);
+                                       _actions[i].Undo (_engine);
+                               }
+                               // Also reverses the stack of actions, so that
+                               // if Undo is called twice it will Redo in the proper order
+                               // 
+                               _actions.Reverse ();
                        }
 
                        protected UndoEngine UndoEngine {
-                               get { return engine; }
+                               get { return _engine; }
                        }
 
-                       [MonoTODO]
                        public virtual bool IsEmpty {
-                               get { throw new NotImplementedException (); }
+                               get { return _actions.Count == 0; }
                        }
 
                        public virtual string Name {
-                               get { return name; }
+                               get { return _name; }
                        }
 
-                       [MonoTODO]
                        public virtual void Close ()
                        {
-                               throw new NotImplementedException ();
+                               // Console.WriteLine ("UndoUnot.Close (" + _name + ")");
+                               _closed = true;
                        }
 
-                       [MonoTODO]
                        public virtual void ComponentAdded (ComponentEventArgs e)
                        {
-                               throw new NotImplementedException ();
+                               if (!_closed) {
+                                       // Console.WriteLine ("New Action: Component*Add*RemoveAction (" + ((IComponent)e.Component).Site.Name + ")");
+                                       _actions.Add (new ComponentAddRemoveAction (_engine, (IComponent) e.Component, true));
+                               }
                        }
 
-                       [MonoTODO]
                        public virtual void ComponentAdding (ComponentEventArgs e)
                        {
-                               throw new NotImplementedException ();
                        }
 
-                       [MonoTODO]
-                       public virtual void ComponentChanged (ComponentEventArgs e)
+                       public virtual void ComponentChanged (ComponentChangedEventArgs e)
                        {
-                               throw new NotImplementedException ();
+                               if (_closed)
+                                       return;
+
+                               // A component starts getting removed. 
+                               // ComponentRemoving -> remove component -> ComponentRemoved
+                               // The problem is that someone can subscribe to the Removed event after us (the UndoEngine) - e.g
+                               // ParentControlDesigner will explicitly request (by setting it to null between Removing and Removed 
+                               // the serialization of the Parent property of the removed child.
+                               // In the case where someone subscribes after and performs changes to the component, we might get 
+                               // ComponentChanged events after we've already created the addremove action, but the componentchangeaction
+                               // will be incomplete standing before the addremove one.
+                               //
+                               ComponentChangeAction changeAction = null;
+                               for (int i=0; i < _actions.Count; i++) {
+                                       changeAction = _actions[i] as ComponentChangeAction;
+                                       if (changeAction != null && !changeAction.IsComplete &&
+                                           changeAction.Component == e.Component &&
+                                           changeAction.Member.Equals (e.Member)) {
+                                               changeAction.SetModifiedState (_engine, (IComponent) e.Component, e.Member);
+                                               break;
+                                       }
+                               }
                        }
 
-                       [MonoTODO]
-                       public virtual void ComponentChanging (ComponentEventArgs e)
+                       public virtual void ComponentChanging (ComponentChangingEventArgs e)
                        {
-                               throw new NotImplementedException ();
+                               if (_closed)
+                                       return;
+
+                               // Console.WriteLine ("New Action: ComponentChangeAction (" + ((IComponent)e.Component).Site.Name + ")");
+                               ComponentChangeAction action = new ComponentChangeAction ();
+                               action.SetOriginalState (_engine, (IComponent) e.Component, e.Member);
+                               _actions.Add (action);
                        }
 
-                       [MonoTODO]
                        public virtual void ComponentRemoved (ComponentEventArgs e)
                        {
-                               throw new NotImplementedException ();
                        }
 
-                       [MonoTODO]
                        public virtual void ComponentRemoving (ComponentEventArgs e)
                        {
-                               throw new NotImplementedException ();
+                               if (!_closed) {
+                                       // Console.WriteLine ("New Action: ComponentAdd*Remove*Action (" + ((IComponent)e.Component).Site.Name + ")");
+                                       _actions.Add (new ComponentAddRemoveAction (_engine, e.Component, false));
+                               }
                        }
 
-                       [MonoTODO]
                        public virtual void ComponentRename (ComponentRenameEventArgs e)
                        {
-                               throw new NotImplementedException ();
+                               if (!_closed) {
+                                       // Console.WriteLine ("New Action: ComponentRenameAction (" + ((IComponent)e.Component).Site.Name + ")");
+                                       _actions.Add (new ComponentRenameAction (e.NewName, e.OldName));
+                               }
                        }
 
-                       [MonoTODO]
                        protected object GetService (Type serviceType)
                        {
-                               throw new NotImplementedException ();
+                               return _engine.GetService (serviceType);
                        }
 
-                       [MonoTODO]
                        public override string ToString ()
                        {
-                               return base.ToString ();
-                       }
-
-                       [MonoTODO]
-                       public void Undo ()
-                       {
-                               throw new NotImplementedException ();
-                       }
-
-                       [MonoTODO]
-                       protected virtual void UndoCore ()
-                       {
-                               throw new NotImplementedException ();
+                               return _name;
                        }
                }
        }
 }
-#endif