PropertyTracker.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // The Command & Conquer Map Editor and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // The Command & Conquer Map Editor and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. using System;
  15. using System.Collections.Generic;
  16. using System.ComponentModel;
  17. using System.Dynamic;
  18. using System.Linq;
  19. using System.Reflection;
  20. namespace MobiusEditor.Utility
  21. {
  22. public class TrackablePropertyDescriptor<T> : PropertyDescriptor
  23. {
  24. private readonly T obj;
  25. private readonly PropertyInfo propertyInfo;
  26. private readonly Dictionary<string, object> propertyValues;
  27. public override Type ComponentType => obj.GetType();
  28. public override bool IsReadOnly => false;
  29. public override Type PropertyType => propertyInfo.PropertyType;
  30. public TrackablePropertyDescriptor(string name, T obj, PropertyInfo propertyInfo, Dictionary<string, object> propertyValues)
  31. : base(name, null)
  32. {
  33. this.obj = obj;
  34. this.propertyInfo = propertyInfo;
  35. this.propertyValues = propertyValues;
  36. }
  37. public override bool CanResetValue(object component)
  38. {
  39. return propertyValues.ContainsKey(Name);
  40. }
  41. public override object GetValue(object component)
  42. {
  43. if (propertyValues.TryGetValue(Name, out object result))
  44. {
  45. return result;
  46. }
  47. return propertyInfo.GetValue(obj);
  48. }
  49. public override void ResetValue(object component)
  50. {
  51. propertyValues.Remove(Name);
  52. }
  53. public override void SetValue(object component, object value)
  54. {
  55. if (Equals(propertyInfo.GetValue(obj), value))
  56. {
  57. propertyValues.Remove(Name);
  58. }
  59. else
  60. {
  61. propertyValues[Name] = value;
  62. }
  63. }
  64. public override bool ShouldSerializeValue(object component)
  65. {
  66. return false;
  67. }
  68. }
  69. public class PropertyTracker<T> : DynamicObject, ICustomTypeDescriptor
  70. {
  71. private readonly Dictionary<string, PropertyInfo> trackableProperties;
  72. private readonly Dictionary<string, object> propertyValues = new Dictionary<string, object>();
  73. public T Object { get; private set; }
  74. public PropertyTracker(T obj)
  75. {
  76. Object = obj;
  77. trackableProperties = Object.GetType()
  78. .GetProperties(BindingFlags.Public | BindingFlags.Instance)
  79. .Where(p => (p.GetGetMethod() != null) && (p.GetSetMethod() != null))
  80. .ToDictionary(k => k.Name, v => v);
  81. }
  82. public void Revert() => propertyValues.Clear();
  83. public void Commit()
  84. {
  85. foreach (var propertyValue in propertyValues)
  86. {
  87. trackableProperties[propertyValue.Key].SetValue(Object, propertyValue.Value);
  88. }
  89. propertyValues.Clear();
  90. }
  91. public IDictionary<string, object> GetUndoValues() => propertyValues.ToDictionary(kv => kv.Key, kv => trackableProperties[kv.Key].GetValue(Object));
  92. public IDictionary<string, object> GetRedoValues() => new Dictionary<string, object>(propertyValues);
  93. public override bool TryGetMember(GetMemberBinder binder, out object result)
  94. {
  95. if (!trackableProperties.TryGetValue(binder.Name, out PropertyInfo property))
  96. {
  97. result = null;
  98. return false;
  99. }
  100. if (!propertyValues.TryGetValue(binder.Name, out result))
  101. {
  102. result = property.GetValue(Object);
  103. }
  104. return true;
  105. }
  106. public override bool TrySetMember(SetMemberBinder binder, object value)
  107. {
  108. if (!trackableProperties.TryGetValue(binder.Name, out PropertyInfo property))
  109. {
  110. return false;
  111. }
  112. if (Equals(property.GetValue(Object), value))
  113. {
  114. propertyValues.Remove(binder.Name);
  115. }
  116. else
  117. {
  118. propertyValues[binder.Name] = value;
  119. }
  120. return true;
  121. }
  122. public AttributeCollection GetAttributes()
  123. {
  124. return TypeDescriptor.GetAttributes(Object.GetType());
  125. }
  126. public string GetClassName()
  127. {
  128. return TypeDescriptor.GetClassName(Object.GetType());
  129. }
  130. public string GetComponentName()
  131. {
  132. return TypeDescriptor.GetComponentName(Object.GetType());
  133. }
  134. public TypeConverter GetConverter()
  135. {
  136. return TypeDescriptor.GetConverter(Object.GetType());
  137. }
  138. public EventDescriptor GetDefaultEvent()
  139. {
  140. return TypeDescriptor.GetDefaultEvent(Object.GetType());
  141. }
  142. public PropertyDescriptor GetDefaultProperty()
  143. {
  144. return TypeDescriptor.GetDefaultProperty(Object.GetType());
  145. }
  146. public object GetEditor(Type editorBaseType)
  147. {
  148. return TypeDescriptor.GetEditor(Object.GetType(), editorBaseType);
  149. }
  150. public EventDescriptorCollection GetEvents()
  151. {
  152. return TypeDescriptor.GetEvents(Object.GetType());
  153. }
  154. public EventDescriptorCollection GetEvents(Attribute[] attributes)
  155. {
  156. return TypeDescriptor.GetEvents(Object.GetType(), attributes);
  157. }
  158. public PropertyDescriptorCollection GetProperties()
  159. {
  160. var propertyDescriptors = trackableProperties.Select(kv =>
  161. {
  162. return new TrackablePropertyDescriptor<T>(kv.Key, Object, kv.Value, propertyValues);
  163. }).ToArray();
  164. return new PropertyDescriptorCollection(propertyDescriptors);
  165. }
  166. public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
  167. {
  168. return GetProperties();
  169. }
  170. public object GetPropertyOwner(PropertyDescriptor pd)
  171. {
  172. return Object;
  173. }
  174. }
  175. }