// 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 ()
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