diff options
| author | jpbruyere <jp.bruyere@hotmail.com> | 2017-01-05 15:47:33 +0100 |
|---|---|---|
| committer | jpbruyere <jp.bruyere@hotmail.com> | 2017-01-05 15:47:33 +0100 |
| commit | 49346012274739fd39745f9e035ee8853149bc73 (patch) | |
| tree | b8757165c6b31a6d08b7caad2118ed76f463c287 /src | |
| parent | 9c0f77e860a1852ee7de381a82118e6d8c7fb819 (diff) | |
| parent | 40b16003066ad42604fd82dc0eec9cfa0a4abeca (diff) | |
Merge branch 'master' into CrowIDE
Diffstat (limited to 'src')
45 files changed, 2879 insertions, 1681 deletions
diff --git a/src/Colors.cs b/src/Colors.cs index b76de623..f55c4187 100644 --- a/src/Colors.cs +++ b/src/Colors.cs @@ -15,6 +15,8 @@ namespace Crow /// </summary> public struct Color { + internal static Type TColor = typeof(Color); + #region CTOR public Color(double _R, double _G, double _B, double _A) { @@ -140,6 +142,9 @@ namespace Crow { return new Color (this.R, this.G, this.B, _A); } + public void ResetName(){ + Name = ""; + } #region Predefined colors public static readonly Color Transparent = new Color(0, 0, 0, 0, "Transparent"); @@ -1046,7 +1051,7 @@ namespace Crow } public override bool Equals (object obj) { - return (obj == null || obj.GetType() != typeof(Color)) ? + return (obj == null || obj.GetType() != TColor) ? false : this == (Color)obj; } @@ -1073,7 +1078,7 @@ namespace Crow } public static Color FromHSV(double _h, double _v = 1.0, double _s = 1.0){ Color c = Color.Black; - + c.ResetName (); if (_s == 0) {//HSV from 0 to 1 c.R = _v; c.G = _v; diff --git a/src/CompilerServices/Bindings.cs b/src/CompilerServices/Bindings.cs deleted file mode 100644 index 64f412f6..00000000 --- a/src/CompilerServices/Bindings.cs +++ /dev/null @@ -1,228 +0,0 @@ -// -// Bindings.cs -// -// Author: -// Jean-Philippe Bruyère <jp_bruyere@hotmail.com> -// -// Copyright (c) 2016 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -using System; -using System.Diagnostics; - -namespace Crow -{ - /// <summary> - /// Binding Class - /// </summary> - public class Binding - { - static int bindingCpt; - string dynMethodId = ""; - bool resolved; - - public bool TwoWayBinding; - public MemberReference Source; - public MemberReference Target; - - public string Expression; - - public string DynMethodId { - get { return dynMethodId; } - } - public Type SourceType { - get { return Source == null ? null - : Source.Instance == null ? null - : Source.Instance.GetType();} - } - - public bool Resolved { - get { return resolved; } - set { - if (value == resolved) - return; -#if DEBUG_BINDING - if (value == true) - Debug.WriteLine ("\tOk => " + this.ToString()); - else - Debug.WriteLine ("\tresolved state reseted => " + this.ToString()); -#endif - resolved = value; - } - } - - #region CTOR - public Binding () { } - public Binding (MemberReference _source, string _expression) - { - Source = _source; - Expression = _expression; - } - public Binding (object _source, string _member, string _expression) - { - Source = new MemberReference (_source, _source.GetType ().GetMember (_member) [0]); - Expression = _expression; - } - public Binding (object _source, string _sourceMember, object _target, string _targetMember) - { - Source = new MemberReference (_source, _source.GetType ().GetMember (_sourceMember) [0]); - Target = new MemberReference (_target, _target.GetType ().GetMember (_targetMember) [0]); - } - public Binding (MemberReference _source, MemberReference _target) - { - Source = _source; - Target = _target; - } - #endregion - - public string CreateNewDynMethodId () - { - if (!string.IsNullOrEmpty (dynMethodId)) - return dynMethodId; - dynMethodId = "dynHandle_" + bindingCpt; - bindingCpt++; - return dynMethodId; - } - /// <summary> - /// resolve target expression - /// </summary> - /// <returns><c>true</c>, if target was found, <c>false</c> otherwise.</returns> - public bool TryFindTarget () - { - if (Target != null) - return true; - - string memberName = null; - - //if binding exp = '{}' => binding is done on datasource - if (string.IsNullOrEmpty (Expression)) { - Object o = (Source.Instance as GraphicObject).DataSource; - if (o == null) - return false; - Target = new MemberReference (o); - return true; - } - - string expression = Expression; - - if (expression.StartsWith ("²")) { - expression = expression.Substring (1); - TwoWayBinding = true; - } - - string [] bindingExp = expression.Split ('/'); - - - if (bindingExp.Length == 1) { - //datasource binding - object dataSource = (Source.Instance as GraphicObject).DataSource; - if (dataSource == null) { - #if DEBUG_BINDING - Debug.WriteLine ("\tDataSource is null => " + this.ToString()); - #endif - return false; - } - - Target = new MemberReference (dataSource); - memberName = bindingExp [0]; - } else { - int ptr = 0; - ILayoutable tmpTarget = Source.Instance as ILayoutable; - //if exp start with '/' => Graphic tree parsing start at source - if (string.IsNullOrEmpty (bindingExp [0])) - ptr++; - else if (bindingExp[0] == "."){ //search template root - do { - tmpTarget = tmpTarget.Parent; - if (tmpTarget == null) - return false; - if (tmpTarget is Interface) - throw new Exception ("Not in Templated Control"); - } while (!(tmpTarget is TemplatedControl)); - ptr++; - } - while (ptr < bindingExp.Length - 1) { - if (tmpTarget == null) { -#if DEBUG_BINDING - Debug.WriteLine ("\tTarget not found => " + this.ToString()); -#endif - return false; - } - if (bindingExp [ptr] == "..") - tmpTarget = tmpTarget.LogicalParent; - else if (bindingExp [ptr] == ".") { - if (ptr > 0) - throw new Exception ("Syntax error in binding, './' may only appear in first position"); - tmpTarget = Source.Instance as ILayoutable; - } else - tmpTarget = (tmpTarget as GraphicObject).FindByName (bindingExp [ptr]); - ptr++; - } - - if (tmpTarget == null) { - #if DEBUG_BINDING - Debug.WriteLine ("\tBinding Target not found => " + this.ToString()); - #endif - return false; - } - - string [] bindTrg = bindingExp [ptr].Split ('.'); - - if (bindTrg.Length == 1) - memberName = bindTrg [0]; - else if (bindTrg.Length == 2) { - tmpTarget = (tmpTarget as GraphicObject).FindByName (bindTrg [0]); - memberName = bindTrg [1]; - } else - throw new Exception ("Syntax error in binding, expected 'go dot member'"); - - if (tmpTarget == null) { - #if DEBUG_BINDING - Debug.WriteLine ("\tBinding Target not found => " + this.ToString()); - #endif - return false; - } - - Target = new MemberReference (tmpTarget); - } - - if (Target.TryFindMember (memberName)) { - if (TwoWayBinding) { - IBindable source = Target.Instance as IBindable; - if (source == null) - throw new Exception (Source.Instance + " does not implement IBindable for 2 way bindings"); - source.Bindings.Add (new Binding (Target, Source)); - } - } - #if DEBUG_BINDING - else - Debug.WriteLine ("Property less binding: " + Target + expression); - #endif - - return true; - } - public void Reset () - { - Target = null; - dynMethodId = ""; - Resolved = false; - } - public override string ToString () - { - return string.Format ("[Binding: {0}.{1} <= {2}]", Source.Instance, Source.Member.Name, Expression); - } - } - -} - diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index 384e6765..e0d6c1e0 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -6,41 +6,101 @@ using System.Linq; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Xml; +using Crow.IML; namespace Crow { public static class CompilerServices { - static MethodInfo miAddBinding = typeof(GraphicObject).GetMethod ("BindMember"); - static FieldInfo miSetCurIface = typeof(GraphicObject).GetField ("currentInterface", - BindingFlags.NonPublic | BindingFlags.Instance); - static MethodInfo miFindByName = typeof (GraphicObject).GetMethod ("FindByName"); + internal static Type TObject = typeof(object); + internal static MethodInfo stringEquals = typeof (string).GetMethod("Equals", new Type [3] { typeof (string), typeof (string), typeof (StringComparison) }); + internal static MethodInfo miObjToString = typeof(object).GetMethod("ToString"); + internal static MethodInfo miGetType = typeof(object).GetMethod("GetType"); + internal static MethodInfo miParseEnum = typeof(Enum).GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, + Type.DefaultBinder, new Type [] {typeof (Type), typeof (string), typeof (bool)}, null); + internal static MethodInfo miGetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle"); + internal static MethodInfo miGetEvent = typeof(Type).GetMethod("GetEvent", new Type[] {typeof(string)}); - #region ValueChange Reflexion member info - static MethodInfo stringEquals = typeof (string).GetMethod - ("Equals", new Type [3] { typeof (string), typeof (string), typeof (StringComparison) }); - static EventInfo eiValueChange = typeof (IValueChange).GetEvent ("ValueChanged"); - static MethodInfo miInvokeValueChange = eiValueChange.EventHandlerType.GetMethod ("Invoke"); - static Type [] argsValueChange = { typeof (object), typeof (object), miInvokeValueChange.GetParameters () [1].ParameterType }; - static FieldInfo fiNewValue = typeof (ValueChangeEventArgs).GetField ("NewValue"); - static FieldInfo fiMbName = typeof (ValueChangeEventArgs).GetField ("MemberName"); - static MethodInfo miValueChangeAdd = eiValueChange.GetAddMethod (); + internal static MethodInfo miMIInvoke = typeof(MethodInfo).GetMethod ("Invoke", new Type[] { + typeof(object), + typeof(object[]) + }); + + internal static MethodInfo miCreateBoundDel = typeof(Delegate).GetMethod ("CreateDelegate", new Type[] { typeof(Type), typeof(object), typeof(MethodInfo) });//create bound delegate + internal static MethodInfo miGetColCount = typeof(System.Collections.ICollection).GetProperty("Count").GetGetMethod(); + internal static MethodInfo miGetDelegateListItem = typeof(List<Delegate>).GetMethod("get_Item", new Type[] { typeof(Int32) }); + + internal static MethodInfo miCompileDynEventHandler = typeof(CompilerServices).GetMethod ("compileDynEventHandler", BindingFlags.Static | BindingFlags.Public); + internal static MethodInfo miRemEvtHdlByName = typeof(CompilerServices).GetMethod("RemoveEventHandlerByName", BindingFlags.Static | BindingFlags.Public); + internal static MethodInfo miRemEvtHdlByTarget = typeof(CompilerServices).GetMethod("RemoveEventHandlerByTarget", BindingFlags.Static | BindingFlags.Public); + internal static MethodInfo miGetMethInfoWithRefx = typeof(CompilerServices).GetMethod ("getMethodInfoWithReflexion", BindingFlags.Static | BindingFlags.Public); + internal static MethodInfo miGetMembIinfoWithRefx = typeof(CompilerServices).GetMethod("getMemberInfoWithReflexion", BindingFlags.Static | BindingFlags.Public); + internal static MethodInfo miSetValWithRefx = typeof(CompilerServices).GetMethod("setValueWithReflexion", BindingFlags.Static | BindingFlags.Public); + internal static MethodInfo miGetValWithRefx = typeof(CompilerServices).GetMethod("getValueWithReflexion", BindingFlags.Static | BindingFlags.Public); + internal static MethodInfo miCreateDel = typeof(CompilerServices).GetMethod ("createDel", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miGetImplOp = typeof(CompilerServices).GetMethod ("getImplicitOp", BindingFlags.Static | BindingFlags.Public); + + internal static FieldInfo fiCachedDel = typeof(Instantiator).GetField("cachedDelegates", BindingFlags.Instance | BindingFlags.NonPublic); + internal static FieldInfo fiTemplateBinding = typeof(Instantiator).GetField("templateBinding", BindingFlags.Instance | BindingFlags.NonPublic); + internal static MethodInfo miDSChangeEmitHelper = typeof(Instantiator).GetMethod("dataSourceChangedEmitHelper", BindingFlags.Instance | BindingFlags.NonPublic); + internal static MethodInfo miDSReverseBinding = typeof(Instantiator).GetMethod("dataSourceReverseBinding", BindingFlags.Static | BindingFlags.NonPublic); + + internal static FieldInfo miSetCurIface = typeof(GraphicObject).GetField ("currentInterface", BindingFlags.NonPublic | BindingFlags.Instance); + internal static MethodInfo miFindByName = typeof (GraphicObject).GetMethod ("FindByName"); + internal static MethodInfo miGetGObjItem = typeof(List<GraphicObject>).GetMethod("get_Item", new Type[] { typeof(Int32) }); + internal static MethodInfo miLoadDefaultVals = typeof (GraphicObject).GetMethod ("loadDefaultValues"); + internal static PropertyInfo piStyle = typeof (GraphicObject).GetProperty ("Style"); + internal static MethodInfo miGetLogicalParent = typeof(GraphicObject).GetProperty("LogicalParent").GetGetMethod(); + internal static MethodInfo miGetDataSource = typeof(GraphicObject).GetProperty("DataSource").GetGetMethod (); + internal static EventInfo eiLogicalParentChanged = typeof(GraphicObject).GetEvent("LogicalParentChanged"); + + internal static MethodInfo miIFaceLoad = typeof(Interface).GetMethod ("Load", BindingFlags.Instance | BindingFlags.Public); + internal static MethodInfo miGetITemp = typeof(Interface).GetMethod ("GetItemTemplate"); + + internal static MethodInfo miAddITemp = typeof(Dictionary<string, ItemTemplate>).GetMethod ("set_Item", new Type[] { typeof(string), typeof(ItemTemplate) }); + internal static MethodInfo miGetITempFromDic = typeof(Dictionary<string, ItemTemplate>).GetMethod ("get_Item", new Type[] { typeof(string) }); + internal static FieldInfo fldItemTemplates = typeof(TemplatedGroup).GetField("ItemTemplates"); + internal static MethodInfo miCreateExpDel = typeof(ItemTemplate).GetMethod ("CreateExpandDelegate"); + + #region tree handling methods + internal static FieldInfo fiChild = typeof(PrivateContainer).GetField ("child", BindingFlags.Instance | BindingFlags.NonPublic); + internal static MethodInfo miSetChild = typeof (Container).GetMethod ("SetChild"); + internal static MethodInfo miAddChild = typeof (Group).GetMethod ("AddChild"); + internal static FieldInfo fiChildren = typeof(Group).GetField ("children", BindingFlags.Instance | BindingFlags.NonPublic); + internal static MethodInfo miLoadTmp = typeof (TemplatedControl).GetMethod ("loadTemplate", BindingFlags.Instance | BindingFlags.NonPublic); + internal static PropertyInfo piContent = typeof(TemplatedContainer).GetProperty ("Content"); + internal static MethodInfo miAddItem = typeof (TemplatedGroup).GetMethod ("AddItem", BindingFlags.Instance | BindingFlags.Public); + internal static MethodInfo miGetItems = typeof(TemplatedGroup).GetProperty ("Items").GetGetMethod (); #endregion + #region ValueChange & DSChange Reflexion member info + internal static EventInfo eiValueChange = typeof (IValueChange).GetEvent ("ValueChanged"); + internal static MethodInfo miInvokeValueChange = eiValueChange.EventHandlerType.GetMethod ("Invoke"); + internal static Type [] argsBoundValueChange = { typeof (object), typeof (object), miInvokeValueChange.GetParameters () [1].ParameterType }; + internal static Type [] argsValueChange = { typeof (object), miInvokeValueChange.GetParameters () [1].ParameterType }; + internal static FieldInfo fiVCNewValue = typeof (ValueChangeEventArgs).GetField ("NewValue"); + internal static FieldInfo fiVCMbName = typeof (ValueChangeEventArgs).GetField ("MemberName"); + internal static MethodInfo miValueChangeAdd = eiValueChange.GetAddMethod (); + + internal static EventInfo eiDSChange = typeof (GraphicObject).GetEvent ("DataSourceChanged"); + internal static MethodInfo miInvokeDSChange = eiDSChange.EventHandlerType.GetMethod ("Invoke"); + internal static Type [] argsBoundDSChange = {typeof (object), typeof (object), miInvokeDSChange.GetParameters () [1].ParameterType }; + internal static FieldInfo fiDSCNewDS = typeof (DataSourceChangeEventArgs).GetField ("NewDataSource"); + internal static FieldInfo fiDSCOldDS = typeof (DataSourceChangeEventArgs).GetField ("OldDataSource"); + internal static Type ehTypeDSChange = eiDSChange.EventHandlerType; + #endregion + /// <summary> + /// Loc0 is the current graphic object and arg2 of loader is the current interface + /// </summary> + /// <param name="il">Il.</param> public static void emitSetCurInterface(ILGenerator il){ il.Emit (OpCodes.Ldloc_0); il.Emit (OpCodes.Ldarg_1); il.Emit (OpCodes.Stfld, miSetCurIface); } - public static void emitBindingCreation(ILGenerator il, string memberName, string expression){ - il.Emit (OpCodes.Ldloc_0); - il.Emit (OpCodes.Ldstr, memberName); - il.Emit (OpCodes.Ldstr, expression); - il.Emit (OpCodes.Callvirt, miAddBinding); - } public static void EmitSetValue(ILGenerator il, PropertyInfo pi, object val){ il.Emit (OpCodes.Ldloc_0); @@ -119,25 +179,17 @@ namespace Crow if (pi.PropertyType == typeof(string)) il.Emit (OpCodes.Ldstr, Convert.ToString (val)); else if (pi.PropertyType.IsEnum) { - MethodInfo miParse = typeof(Enum).GetMethod - ("Parse", BindingFlags.Static | BindingFlags.Public, - Type.DefaultBinder, new Type [] {typeof (Type), typeof (string), typeof (bool)}, null); - - if (miParse == null) - throw new Exception ("Enum Parse method not found"); - //load type of enum il.Emit(OpCodes.Ldtoken, pi.PropertyType); - il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new - Type[1]{typeof(RuntimeTypeHandle)})); + il.Emit(OpCodes.Call, CompilerServices.miGetTypeFromHandle); //load enum value name il.Emit (OpCodes.Ldstr, Convert.ToString (val));//TODO:is this convert required? //load false il.Emit (OpCodes.Ldc_I4_0); - il.Emit (OpCodes.Callvirt, miParse); + il.Emit (OpCodes.Callvirt, CompilerServices.miParseEnum); - if (miParse.ReturnType != pi.PropertyType) - il.Emit (OpCodes.Unbox_Any, pi.PropertyType); + if (CompilerServices.miParseEnum.ReturnType != pi.PropertyType) + il.Emit (OpCodes.Unbox_Any, pi.PropertyType); } else { MethodInfo miParse = pi.PropertyType.GetMethod ("Parse", BindingFlags.Static | BindingFlags.Public, @@ -152,280 +204,397 @@ namespace Crow il.Emit (OpCodes.Unbox_Any, pi.PropertyType); } } - il.Emit (OpCodes.Callvirt, pi.GetSetMethod ()); + il.Emit (OpCodes.Callvirt, pi.GetSetMethod ()); } - public static void ResolveBindings (List<Binding> Bindings) - { - if (Bindings == null) - return; - if (Bindings.Count == 0) - return; - //#if DEBUG_BINDING - // Debug.WriteLine ("Resolve Bindings => " + this.ToString ()); - //#endif - //grouped bindings by Instance of Source - Dictionary<object, List<Binding>> resolved = new Dictionary<object, List<Binding>> (); - - foreach (Binding b in Bindings) { - if (b.Resolved) - continue; - if (b.Source.Member.MemberType == MemberTypes.Event) { - if (b.Expression.StartsWith ("{")) { - CompilerServices.CompileEventSource (b); - continue; - } - if (!b.TryFindTarget ()) - continue; - //register handler for event - if (b.Target.Method == null) { - //Debug.WriteLine ("\tError: Handler Method not found: " + b.ToString ()); - continue; - } - try { - MethodInfo addHandler = b.Source.Event.GetAddMethod (); - Delegate del = Delegate.CreateDelegate (b.Source.Event.EventHandlerType, b.Target.Instance, b.Target.Method); - addHandler.Invoke (b.Source.Instance, new object [] { del }); - -#if DEBUG_BINDING - Debug.WriteLine ("\tHandler binded => " + b.ToString()); -#endif - b.Resolved = true; - } catch (Exception ex) { - //Debug.WriteLine ("\tERROR: " + ex.ToString ()); - } - continue; - } - - if (!b.TryFindTarget ()) - continue; - - //group Bindings by target instanceq - List<Binding> bindings = null; - if (!resolved.TryGetValue (b.Target.Instance, out bindings)) { - bindings = new List<Binding> (); - resolved [b.Target.Instance] = bindings; - } - bindings.Add (b); - b.Resolved = true; - } - - Type target_Type = Bindings [0].Source.Instance.GetType (); - - //group;only one dynMethods by target (valuechanged event source) - //changed value name tested in switch - //IEnumerable<Binding[]> groupedByTarget = resolved.GroupBy (g => g.Target.Instance, g => g, (k, g) => g.ToArray ()); - foreach (List<Binding> grouped in resolved.Values) { - int i = 0; - Type source_Type = grouped [0].Target.Instance.GetType (); - - DynamicMethod dm = null; - ILGenerator il = null; - - System.Reflection.Emit.Label [] jumpTable = null; - System.Reflection.Emit.Label endMethod = new System.Reflection.Emit.Label (); - - #region Retrieve EventHandler parameter type - //EventInfo ei = targetType.GetEvent ("ValueChanged"); - //no dynamic update if ValueChanged interface is not implemented - if (source_Type.GetInterfaces ().Contains (typeof (IValueChange))) { - dm = new DynamicMethod (grouped [0].CreateNewDynMethodId (), - MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot, - CallingConventions.Standard, - typeof (void), - argsValueChange, - target_Type, true); - - il = dm.GetILGenerator (256); + #region conversions - endMethod = il.DefineLabel (); - jumpTable = new System.Reflection.Emit.Label [grouped.Count]; - for (i = 0; i < grouped.Count; i++) - jumpTable [i] = il.DefineLabel (); - il.DeclareLocal (typeof (string)); - il.DeclareLocal (typeof (object)); + internal static MethodInfo GetConvertMethod (Type targetType) + { + string name; - il.Emit (OpCodes.Nop); - il.Emit (OpCodes.Ldarg_0); - //il.Emit(OpCodes.Isinst, sourceType); - //push new value onto stack - il.Emit (OpCodes.Ldarg_2); - il.Emit (OpCodes.Ldfld, fiNewValue); - il.Emit (OpCodes.Stloc_1); - //push name - il.Emit (OpCodes.Ldarg_2); - il.Emit (OpCodes.Ldfld, fiMbName); - il.Emit (OpCodes.Stloc_0); - il.Emit (OpCodes.Ldloc_0); - il.Emit (OpCodes.Brfalse, endMethod); - } - #endregion + if (targetType == typeof (bool)) + name = "ToBoolean"; + else if (targetType == typeof (byte)) + name = "ToByte"; + else if (targetType == typeof (short)) + name = "ToInt16"; + else if (targetType == typeof (int)) + name = "ToInt32"; + else if (targetType == typeof (long)) + name = "ToInt64"; + else if (targetType == typeof (double)) + name = "ToDouble"; + else if (targetType == typeof (float)) + name = "ToSingle"; + else if (targetType == typeof (string)) + return typeof (object).GetMethod ("ToString", Type.EmptyTypes); + else //try to find implicit convertion + throw new NotImplementedException (string.Format ("Conversion to {0} is not implemented.", targetType.Name)); - i = 0; - foreach (Binding b in grouped) { - #region initialize target with actual value - object targetValue = null; - if (b.Target.Member != null) { - if (b.Target.Member.MemberType == MemberTypes.Property) - targetValue = b.Target.Property.GetGetMethod ().Invoke (b.Target.Instance, null); - else if (b.Target.Member.MemberType == MemberTypes.Field) - targetValue = b.Target.Field.GetValue (b.Target.Instance); - else if (b.Target.Member.MemberType == MemberTypes.Method) { - MethodInfo mthSrc = b.Target.Method; - if (mthSrc.IsDefined (typeof (ExtensionAttribute), false)) - targetValue = mthSrc.Invoke (null, new object [] { b.Target.Instance }); - else - targetValue = mthSrc.Invoke (b.Target.Instance, null); - } else - throw new Exception ("unandled source member type for binding"); - } else if (string.IsNullOrEmpty (b.Expression)) - targetValue = grouped [0].Target.Instance;//empty binding exp=> bound to target object by default - //TODO: handle other dest type conversions - if (b.Source.Property.PropertyType == typeof (string)) { - if (targetValue == null) { - //set default value + return typeof (Convert).GetMethod (name, BindingFlags.Static | BindingFlags.Public, null, new Type [] { typeof (object) }, null); + } + #endregion - } else - targetValue = targetValue.ToString (); - } - try { - if (targetValue != null) - b.Source.Property.GetSetMethod ().Invoke - (b.Source.Instance, new object [] { b.Source.Property.PropertyType.Cast (targetValue) }); - else - b.Source.Property.GetSetMethod ().Invoke - (b.Source.Instance, new object [] { targetValue }); - } catch (Exception ex) { - Debug.WriteLine (ex.ToString ()); - } - #endregion + /// <summary> + /// retrieve event handler in class or ancestors + /// </summary> + public static FieldInfo GetEventHandlerField (Type type, string eventName) + { + FieldInfo fi; + Type ty = type; + do { + fi = ty.GetField (eventName, + BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.GetField); + ty = ty.BaseType; + if (ty == null) + break; + } while (fi == null); + return fi; + } - //if no dyn update, skip jump table - if (il == null) - continue; + /// <summary> + /// Gets extension methods defined in assembley for extendedType + /// </summary> + /// <returns>Extension methods enumerable</returns> + /// <param name="assembly">Assembly</param> + /// <param name="extendedType">Extended type to search for</param> + public static IEnumerable<MethodInfo> GetExtensionMethods (Assembly assembly, + Type extendedType) + { + IEnumerable<MethodInfo> query = null; + Type curType = extendedType; - il.Emit (OpCodes.Ldloc_0); - if (b.Target.Member != null) - il.Emit (OpCodes.Ldstr, b.Target.Member.Name); - else - il.Emit (OpCodes.Ldstr, b.Expression.Split ('/').LastOrDefault ().Split('.').LastOrDefault()); - il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal - il.Emit (OpCodes.Callvirt, stringEquals); - il.Emit (OpCodes.Brtrue, jumpTable [i]); - i++; - } + do { + query = from type in assembly.GetTypes () + where type.IsSealed && !type.IsGenericType && !type.IsNested + from method in type.GetMethods (BindingFlags.Static + | BindingFlags.Public | BindingFlags.NonPublic) + where method.IsDefined (typeof (ExtensionAttribute), false) + where method.GetParameters () [0].ParameterType == curType + select method; - if (il == null) - continue; + if (query.Count () > 0) + break; - il.Emit (OpCodes.Br, endMethod); + curType = curType.BaseType; + } while (curType != null); - i = 0; - foreach (Binding b in grouped) { + return query; + } - il.MarkLabel (jumpTable [i]); + public static MethodInfo SearchExtMethod(Type t, string methodName){ + MethodInfo mi = null; + mi = GetExtensionMethods (Assembly.GetEntryAssembly(), t) + .Where (em => em.Name == methodName).FirstOrDefault (); + if (mi != null) + return mi; + return GetExtensionMethods (Assembly.GetExecutingAssembly(), t) + .Where (em => em.Name == methodName).FirstOrDefault (); + } - //load 2 times to check first for null - il.Emit (OpCodes.Ldloc_1); - il.Emit (OpCodes.Ldloc_1); + public static MemberInfo getMemberInfoWithReflexion(object instance, string member){ + return instance.GetType ().GetMember (member).FirstOrDefault(); + } + public static MethodInfo getMethodInfoWithReflexion(object instance, string method){ + return instance.GetType ().GetMethod (method, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + } + public static Type getEventHandlerType(object instance, string eventName){ + return instance.GetType ().GetEvent (eventName).EventHandlerType; + } + public static void setValueWithReflexion(object dest, object value, string destMember){ + Type destType = null; + Type origType = null; + object convertedVal = null; - System.Reflection.Emit.Label labSetValue = il.DefineLabel (); - il.Emit (OpCodes.Brtrue, labSetValue); - //if null - il.Emit (OpCodes.Unbox_Any, b.Source.Property.PropertyType); - il.Emit (OpCodes.Callvirt, b.Source.Property.GetSetMethod ()); - il.Emit (OpCodes.Br, endMethod); + MemberInfo miDest = getMemberInfoWithReflexion (dest, destMember); - il.MarkLabel (labSetValue); - //new value not null + if (miDest == null) { + Debug.WriteLine ("Reverse template binding error: " + destMember + " not found in " + dest); + return; + } - //by default, source value type is deducted from target member type to allow - //memberless binding, if targetMember exists, it will be used to determine target - //value type for conversion - Type sourceValueType = b.Source.Property.PropertyType; - if (b.Target.Member != null) { - if (b.Target.Member.MemberType == MemberTypes.Property) - sourceValueType = b.Target.Property.PropertyType; - else if (b.Target.Member.MemberType == MemberTypes.Field) - sourceValueType = b.Target.Field.FieldType; - else - throw new Exception ("unhandle target member type in binding"); - } + if (miDest.MemberType == MemberTypes.Property) + destType =(miDest as PropertyInfo).PropertyType; + else if (miDest.MemberType == MemberTypes.Field) + destType =(miDest as FieldInfo).FieldType; + if (value != null) { + origType = value.GetType (); + if (destType.IsAssignableFrom (origType)) + convertedVal = Convert.ChangeType (value, destType); + else if (origType.IsPrimitive & destType.IsPrimitive) + convertedVal = GetConvertMethod (destType).Invoke (null, new Object[] { value }); + else + convertedVal = getImplicitOp (origType, destType).Invoke (value, null); + } + if (miDest.MemberType == MemberTypes.Property) + (miDest as PropertyInfo).SetValue (dest, convertedVal); + else if (miDest.MemberType == MemberTypes.Field) + (miDest as FieldInfo).SetValue (dest, convertedVal); + } + public static object getValueWithReflexion(object instance, MemberInfo mi){ + object tmp = null; + Type dstType = null; + if (mi == null) + return null; + if (mi.MemberType == MemberTypes.Property) { + PropertyInfo pi = mi as PropertyInfo; + tmp = pi.GetValue (instance); + dstType = pi.PropertyType; + } + if (mi.MemberType == MemberTypes.Field) { + FieldInfo fi = mi as FieldInfo; + tmp = fi.GetValue (instance); + dstType = fi.FieldType; + } + if (tmp != null) + return tmp; + if (dstType == typeof(string) || dstType == CompilerServices.TObject)//TODO:object should be allowed to return null and not "" + return ""; + if (dstType.IsValueType) + return Activator.CreateInstance (dstType); - if (b.Source.Property.PropertyType == typeof (string)) { - MemberReference tostring = new MemberReference (b.Source.Instance); - if (!tostring.TryFindMember ("ToString")) - throw new Exception ("ToString method not found"); - il.Emit (OpCodes.Callvirt, tostring.Method); - } else if (!sourceValueType.IsValueType) - il.Emit (OpCodes.Castclass, sourceValueType); - else if (b.Source.Property.PropertyType != sourceValueType && b.Source.Property.PropertyType != typeof(object)) { - il.Emit (OpCodes.Callvirt, CompilerServices.GetConvertMethod (b.Source.Property.PropertyType)); - } else - il.Emit (OpCodes.Unbox_Any, b.Source.Property.PropertyType); + return null; + } + public static void emitGetInstance (ILGenerator il, NodeAddress orig, NodeAddress dest){ + int ptr = 0; + while (orig [ptr] == dest [ptr]) { + ptr++; + if (ptr == orig.Count || ptr == dest.Count) + break; + } + for (int i = 0; i < orig.Count - ptr; i++) + il.Emit (OpCodes.Callvirt, CompilerServices.miGetLogicalParent); + while (ptr < dest.Count) { + emitGetChild (il, dest [ptr-1].CrowType, dest [ptr].Index); + ptr++; + } + } + public static void emitGetInstance (ILGenerator il, NodeAddress dest){ + if (dest == null) + return; + for (int i = 0; i < dest.Count - 1; i++) + emitGetChild (il, dest [i].CrowType, dest [i + 1].Index); + } + public static void emitGetChild(ILGenerator il, Type parentType, int index){ + if (typeof (Group).IsAssignableFrom (parentType)) { + il.Emit (OpCodes.Ldfld, fiChildren); + il.Emit(OpCodes.Ldc_I4, index); + il.Emit (OpCodes.Callvirt, miGetGObjItem); + return; + } + if (typeof(Container).IsAssignableFrom (parentType) || index < 0) { + il.Emit (OpCodes.Ldfld, fiChild); + return; + } + if (typeof(TemplatedContainer).IsAssignableFrom (parentType)) { + il.Emit (OpCodes.Callvirt, piContent.GetGetMethod()); + return; + } + if (typeof(TemplatedGroup).IsAssignableFrom (parentType)) { + il.Emit (OpCodes.Callvirt, miGetItems); + il.Emit(OpCodes.Ldc_I4, index); + il.Emit (OpCodes.Callvirt, miGetGObjItem); + return; + } + } + /// <summary> + /// Emit conversion from orig type to dest type + /// </summary> + public static void emitConvert(ILGenerator il, Type origType, Type destType){ + if (destType == CompilerServices.TObject) + return; + if (destType == typeof(string)) { + System.Reflection.Emit.Label emitNullStr = il.DefineLabel (); + System.Reflection.Emit.Label endConvert = il.DefineLabel (); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, emitNullStr); + il.Emit (OpCodes.Callvirt, CompilerServices.miObjToString); + il.Emit (OpCodes.Br, endConvert); + il.MarkLabel (emitNullStr); + il.Emit (OpCodes.Pop);//remove null string from stack + il.Emit (OpCodes.Ldstr, "");//replace with empty string + il.MarkLabel (endConvert); + }else if (origType.IsValueType) { + if (destType != origType) { + il.Emit (OpCodes.Callvirt, CompilerServices.GetConvertMethod (destType)); + }else + il.Emit (OpCodes.Unbox_Any, destType);//TODO:double check this + } else { + if (origType.IsAssignableFrom(destType)) + il.Emit (OpCodes.Castclass, destType); + else { + MethodInfo miIO = getImplicitOp (origType, destType); + if (miIO != null) + il.Emit (OpCodes.Callvirt, miIO); + } + } + } + /// <summary> + /// check type of current object on the stack and convert to dest type, + /// use loc_0 so store it as object!!! + /// </summary> + public static void emitConvert(ILGenerator il, Type dstType){ + System.Reflection.Emit.Label endConvert = il.DefineLabel (); + System.Reflection.Emit.Label convert = il.DefineLabel (); - il.Emit (OpCodes.Callvirt, b.Source.Property.GetSetMethod ()); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Isinst, dstType); + il.Emit (OpCodes.Brfalse, convert); - //il.BeginCatchBlock (typeof (Exception)); - //il.Emit (OpCodes.Pop); - //il.EndExceptionBlock (); + if (dstType.IsValueType) + il.Emit (OpCodes.Unbox_Any, dstType); + else + il.Emit (OpCodes.Isinst, dstType); + il.Emit (OpCodes.Br, endConvert); - il.Emit (OpCodes.Br, endMethod); - i++; + il.MarkLabel (convert); - } - il.MarkLabel (endMethod); + if (dstType == typeof(string)) { + System.Reflection.Emit.Label emitNullStr = il.DefineLabel (); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, emitNullStr); + il.Emit (OpCodes.Callvirt, CompilerServices.miObjToString); + il.Emit (OpCodes.Br, endConvert); + il.MarkLabel (emitNullStr); + il.Emit (OpCodes.Pop);//remove null string from stack + il.Emit (OpCodes.Ldstr, "");//replace with empty string + } else if (dstType.IsPrimitive) { + //il.Emit (OpCodes.Unbox_Any, dstType); + il.Emit (OpCodes.Callvirt, CompilerServices.GetConvertMethod (dstType)); + } else if (dstType.IsValueType) { + il.Emit (OpCodes.Unbox_Any, dstType); + } else{ + il.Emit (OpCodes.Stloc_0); //save orig value in loc0 + //first check if not null + il.Emit (OpCodes.Ldloc_0); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, endConvert); + il.Emit (OpCodes.Callvirt, miGetType); + il.Emit (OpCodes.Ldtoken, dstType);//push destination property type for testing + il.Emit (OpCodes.Call, CompilerServices.miGetTypeFromHandle); + il.Emit (OpCodes.Call, miGetImplOp); + il.Emit (OpCodes.Dup); + convert = il.DefineLabel (); + il.Emit (OpCodes.Brtrue, convert); il.Emit (OpCodes.Pop); - il.Emit (OpCodes.Ret); + il.Emit (OpCodes.Ldloc_0); + il.Emit (OpCodes.Isinst, dstType); + il.Emit (OpCodes.Br, endConvert); - try { - Delegate del = dm.CreateDelegate (eiValueChange.EventHandlerType, Bindings [0].Source.Instance); - miValueChangeAdd.Invoke (grouped [0].Target.Instance, new object [] { del }); + il.MarkLabel (convert); + il.Emit (OpCodes.Ldnull);//null instance for invoke + il.Emit (OpCodes.Ldc_I4_1); + il.Emit(OpCodes.Newarr, CompilerServices.TObject); + il.Emit (OpCodes.Dup);//duplicate the array ref + il.Emit (OpCodes.Ldc_I4_0);//push the index 0 + il.Emit (OpCodes.Ldloc_0);//push the orig value to convert + il.Emit (OpCodes.Stelem, CompilerServices.TObject);//set the array element at index 0 + il.Emit (OpCodes.Callvirt, miMIInvoke); + } - } catch (Exception ex) { - Debug.WriteLine ("Binding Delegate error for {0}: \n{1}", Bindings [0].Source.Instance, ex.ToString ()); + il.MarkLabel (endConvert); + } + /// <summary> + /// search for an implicit conversion method in origine or destination classes + /// </summary> + public static MethodInfo getImplicitOp(Type origType, Type dstType){ + foreach(MethodInfo mi in origType.GetMethods(BindingFlags.Public|BindingFlags.Static)){ + if (mi.Name == "op_Implicit") { + if (mi.ReturnType == dstType && mi.GetParameters ().FirstOrDefault ().ParameterType == origType) + return mi; } } + foreach(MethodInfo mi in dstType.GetMethods(BindingFlags.Public|BindingFlags.Static)){ + if (mi.Name == "op_Implicit") { + if (mi.ReturnType == dstType && mi.GetParameters ().FirstOrDefault ().ParameterType == origType) + return mi; + } + } + return null; } - /// <summary> - /// Compile events expression in GOML attributes + /// Removes delegate from event handler by name /// </summary> - /// <param name="binding">Event binding details</param> - public static void CompileEventSource (Binding binding) - { -#if DEBUG_BINDING - Debug.WriteLine ("\tCompile Event Source => " + binding.ToString()); -#endif + public static void RemoveEventHandlerByName(object instance, string eventName, string delegateName){ + Type t = instance.GetType (); + FieldInfo fiEvt = CompilerServices.GetEventHandlerField (t, eventName); + if (fiEvt == null) { + Debug.WriteLine ("RemoveHandlerByName: Event '" + eventName + "' not found in " + instance); + return; + } + EventInfo eiEvt = t.GetEvent (eventName); + MulticastDelegate multiDel = fiEvt.GetValue (instance) as MulticastDelegate; + if (multiDel != null) { + foreach (Delegate d in multiDel.GetInvocationList()) { + if (d.Method.Name == delegateName) { + eiEvt.RemoveEventHandler (instance, d); + #if DEBUG_BINDING + Debug.WriteLine ("\t{0} handler removed in {1} for: {2}", d.Method.Name,instance, eventName); + #endif + } + } + } + } + /// <summary> + /// Removes delegate from event handler by searching for the object they are bond to + /// </summary> + public static void RemoveEventHandlerByTarget(object instance, string eventName, object target){ + Type t = instance.GetType (); + FieldInfo fiEvt = CompilerServices.GetEventHandlerField (t, eventName); + EventInfo eiEvt = t.GetEvent (eventName); + MulticastDelegate multiDel = fiEvt.GetValue (instance) as MulticastDelegate; + if (multiDel != null) { + foreach (Delegate d in multiDel.GetInvocationList()) { + if (d.Target == target) { + eiEvt.RemoveEventHandler (instance, d); + #if DEBUG_BINDING + Debug.WriteLine ("\t{0} handler removed in {1} for: {2}", d.Method.Name,instance, eventName); + #endif + } + } + } + } + internal static Delegate createDel(Type eventType, object instance, string method){ + Type t = instance.GetType (); + MethodInfo mi = t.GetMethod (method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (mi == null) { + Debug.WriteLine ("Handler Method '{0}' not found in '{1}'", method, t); + return null; + } + return Delegate.CreateDelegate (eventType, instance, mi); + } + public static Delegate compileDynEventHandler(EventInfo sourceEvent, string expression, NodeAddress currentNode = null){ + #if DEBUG_BINDING + Debug.WriteLine ("\tCompile Event {0}: {1}", sourceEvent.Name, expression); + #endif - Type target_type = binding.Source.Instance.GetType (); + Type lopType = null; + + if (currentNode == null) + lopType = sourceEvent.DeclaringType; + else + lopType = currentNode.NodeType; #region Retrieve EventHandler parameter type - MethodInfo evtInvoke = binding.Source.Event.EventHandlerType.GetMethod ("Invoke"); + MethodInfo evtInvoke = sourceEvent.EventHandlerType.GetMethod ("Invoke"); ParameterInfo [] evtParams = evtInvoke.GetParameters (); Type handlerArgsType = evtParams [1].ParameterType; #endregion - Type [] args = { typeof (object), typeof (object), handlerArgsType }; - DynamicMethod dm = new DynamicMethod (binding.CreateNewDynMethodId (), - typeof (void), - args, - target_type); - - - #region IL generation + Type [] args = { CompilerServices.TObject, handlerArgsType }; + DynamicMethod dm = new DynamicMethod ("dyn_eventHandler", + typeof(void), + args, true); ILGenerator il = dm.GetILGenerator (256); + il.Emit (OpCodes.Nop); - string src = binding.Expression.Trim (); - - if (!(src.StartsWith ("{") || src.EndsWith ("}"))) - throw new Exception (string.Format ("GOML:Malformed {0} Event handler: {1}", binding.Source.Member.Name, binding.Expression)); - - src = src.Substring (1, src.Length - 2); - string [] srcLines = src.Split (new char [] { ';' }); + string [] srcLines = expression.Trim ().Split (new char [] { ';' }); foreach (string srcLine in srcLines) { string statement = srcLine.Trim (); @@ -433,51 +602,59 @@ namespace Crow string [] operandes = statement.Split (new char [] { '=' }); if (operandes.Length < 2) //not an affectation { + //maybe we could handle here handler function name continue; } - string lop = operandes [0].Trim (); + string rop = operandes [operandes.Length - 1].Trim (); #region LEFT OPERANDES - GraphicObject lopObj = binding.Source.Instance as GraphicObject; //default left operand base object is - //the first arg (object sender) of the event handler + string [] lopParts = operandes [0].Trim ().Split ('/'); + MemberInfo lopMI = null; il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack - string [] lopParts = lop.Split (new char [] { '.' }); - if (lopParts.Length > 1) {//should search also for member of es.Source - for (int j = 0; j < lopParts.Length - 1; j++) { - il.Emit (OpCodes.Ldstr, lopParts [j]); - il.Emit (OpCodes.Callvirt, miFindByName); - } + if (lopParts.Length > 1) { + NodeAddress lopNA = getNodeAdressFromBindingExp (currentNode, lopParts); + CompilerServices.emitGetInstance (il, currentNode, lopNA); + lopType = lopNA.NodeType; } - int i = lopParts.Length - 1; + string [] bindTrg = lopParts.Last().Split ('.'); + + if (bindTrg.Length == 1) + lopMI = lopType.GetMember (bindTrg [0]).FirstOrDefault(); + else if (bindTrg.Length == 2) { + //named target + //TODO: + il.Emit(OpCodes.Ldstr, bindTrg[0]); + il.Emit(OpCodes.Callvirt, miFindByName); + lopMI = lopType.GetMember (bindTrg [1]).FirstOrDefault(); + } else + throw new Exception ("Syntax error in binding, expected 'go dot member'"); - MemberInfo [] lopMbis = lopObj.GetType ().GetMember (lopParts [i]); - if (lopMbis.Length < 1) - throw new Exception (string.Format ("CROW BINDING: Member not found '{0}'", lop)); + if (lopMI == null) + throw new Exception (string.Format ("IML BINDING: Member not found")); - OpCode lopSetOC; - dynamic lopSetMbi; + OpCode lopSetOpCode; + dynamic lopSetMI; Type lopT = null; - switch (lopMbis [0].MemberType) { + switch (lopMI.MemberType) { case MemberTypes.Property: - PropertyInfo lopPi = target_type.GetProperty (lopParts [i]); - MethodInfo dstMi = lopPi.GetSetMethod (); + lopSetOpCode = OpCodes.Callvirt; + PropertyInfo lopPi = lopMI as PropertyInfo; lopT = lopPi.PropertyType; - lopSetMbi = dstMi; - lopSetOC = OpCodes.Callvirt; + lopSetMI = lopPi.GetSetMethod (); break; case MemberTypes.Field: - FieldInfo dstFi = target_type.GetField (lopParts [i]); + lopSetOpCode = OpCodes.Stfld; + FieldInfo dstFi = lopMI as FieldInfo; lopT = dstFi.FieldType; - lopSetMbi = dstFi; - lopSetOC = OpCodes.Stfld; + lopSetMI = dstFi; break; default: - throw new Exception (string.Format ("GOML:member type not handle: {0}", lopParts [i])); + throw new Exception (string.Format ("GOML:member type not handle")); } #endregion @@ -506,108 +683,67 @@ namespace Crow #endregion //emit left operand assignment - il.Emit (lopSetOC, lopSetMbi); + il.Emit (lopSetOpCode, lopSetMI); } il.Emit (OpCodes.Ret); - #endregion - - Delegate del = dm.CreateDelegate (binding.Source.Event.EventHandlerType, binding.Source.Instance); - MethodInfo addHandler = binding.Source.Event.GetAddMethod (); - addHandler.Invoke (binding.Source.Instance, new object [] { del }); - - binding.Resolved = true; + return dm.CreateDelegate (sourceEvent.EventHandlerType); } - - #region conversions - - internal static MethodInfo GetConvertMethod (Type targetType) - { - string name; - - if (targetType == typeof (bool)) - name = "ToBoolean"; - else if (targetType == typeof (byte)) - name = "ToByte"; - else if (targetType == typeof (short)) - name = "ToInt16"; - else if (targetType == typeof (int)) - name = "ToInt32"; - else if (targetType == typeof (long)) - name = "ToInt64"; - else if (targetType == typeof (double)) - name = "ToDouble"; - else if (targetType == typeof (float)) - name = "ToSingle"; - else if (targetType == typeof (string)) - return typeof (object).GetMethod ("ToString", Type.EmptyTypes); - else //try to find implicit convertion - throw new NotImplementedException (string.Format ("Conversion to {0} is not implemented.", targetType.Name)); - - return typeof (Convert).GetMethod (name, BindingFlags.Static | BindingFlags.Public, null, new Type [] { typeof (object) }, null); - } - #endregion - - /// <summary> - /// retrieve event handler in class or ancestors - /// </summary> - public static FieldInfo GetEventHandlerField (Type type, string eventName) - { - FieldInfo fi; - Type ty = type; - do { - fi = ty.GetField (eventName, - BindingFlags.NonPublic | - BindingFlags.Instance | - BindingFlags.GetField); - ty = ty.BaseType; - if (ty == null) + public static string[] splitOnSemiColumnOutsideAccolades (string expression){ + List<String> exps = new List<string>(); + int accCount = 0; + int expPtr = 0; + for (int c = 0; c < expression.Length; c++) { + switch (expression[c]){ + case '{': + accCount++; break; - } while (fi == null); - return fi; + case '}': + accCount--; + break; + case ';': + if (accCount > 0) + break; + exps.Add(expression.Substring(expPtr, c - expPtr - 1)); + expPtr = c + 1; + break; + } + } + if (exps.Count == 0) + exps.Add(expression); + return exps.ToArray (); } - /// <summary> - /// Gets extension methods defined in assembley for extendedType + /// Gets the node adress from binding expression splitted with '/' starting at a given node /// </summary> - /// <returns>Extension methods enumerable</returns> - /// <param name="assembly">Assembly</param> - /// <param name="extendedType">Extended type to search for</param> - public static IEnumerable<MethodInfo> GetExtensionMethods (Assembly assembly, - Type extendedType) - { - IEnumerable<MethodInfo> query = null; - Type curType = extendedType; - - do { - query = from type in assembly.GetTypes () - where type.IsSealed && !type.IsGenericType && !type.IsNested - from method in type.GetMethods (BindingFlags.Static - | BindingFlags.Public | BindingFlags.NonPublic) - where method.IsDefined (typeof (ExtensionAttribute), false) - where method.GetParameters () [0].ParameterType == curType - select method; + public static NodeAddress getNodeAdressFromBindingExp(NodeAddress sourceAddr, string[] bindingExp){ + int ptr = sourceAddr.Count - 1; - if (query.Count () > 0) - break; - - curType = curType.BaseType; - } while (curType != null); - - return query; + //if exp start with '/' => Graphic tree parsing start at source + if (string.IsNullOrEmpty (bindingExp [0])) { + //TODO: + } else if (bindingExp [0] == ".") { //search template root + ptr--; + while (ptr >= 0) { + if (typeof(TemplatedControl).IsAssignableFrom (sourceAddr [ptr].CrowType)) + break; + ptr--; + } + } else if (bindingExp [0] == "..") { //search starting at current node + int levelUp = bindingExp.Length - 1; + if (levelUp > ptr + 1) + throw new Exception ("Binding error: try to bind outside IML source"); + ptr -= levelUp; + } + //TODO:change Template special address identified with Nodecount = 0 to something not using array count to 0, + //here linq is working without limits checking in compile option + //but defining a 0 capacity array with limits cheking enabled, cause 'out of memory' error + return new NodeAddress (sourceAddr.Take(ptr+1).ToArray());//[ptr+1]; + //Array.Copy (sourceAddr.ToArray (), targetNode, ptr + 1); + //return new NodeAddress (targetNode); } - public static MethodInfo SearchExtMethod(Type t, string methodName){ - MethodInfo mi = null; - mi = GetExtensionMethods (Assembly.GetEntryAssembly(), t) - .Where (em => em.Name == methodName).FirstOrDefault (); - if (mi != null) - return mi; - - return GetExtensionMethods (Assembly.GetExecutingAssembly(), t) - .Where (em => em.Name == methodName).FirstOrDefault (); - } } } diff --git a/src/Configuration.cs b/src/Configuration.cs index af52eed4..5e035f14 100644 --- a/src/Configuration.cs +++ b/src/Configuration.cs @@ -53,7 +53,7 @@ namespace Crow if (!items.ContainsKey (key)) return default(T); Type type = typeof(T); - MethodInfo miParse = type.GetMethod ("Parse", BindingFlags.Static); + MethodInfo miParse = type.GetMethod ("Parse", new Type[] {typeof(string)}); if (miParse == null) return (T)Convert.ChangeType (items [key], typeof(T)); diff --git a/src/Enums.cs b/src/Enums.cs index 46f8cdab..07ae1a4d 100644 --- a/src/Enums.cs +++ b/src/Enums.cs @@ -35,12 +35,4 @@ namespace Crow Bottom, Center, } - - public enum RectanglesRelations - { - NoRelation, //nothing to do - Intersect, //clipped clear & repaint, no test - Contains, //clipped clear & repaint, test children - Equal //repaint, no test - } } diff --git a/src/ExtensionsMethods.cs b/src/ExtensionsMethods.cs index 2bfc36ea..5e58cdd5 100644 --- a/src/ExtensionsMethods.cs +++ b/src/ExtensionsMethods.cs @@ -114,18 +114,7 @@ namespace Crow handler(sender, e); } } - /// <summary> - /// Type Extension method for Casting - /// </summary> - public static object Cast(this Type Type, object data) - { - var DataParam = Expression.Parameter(typeof(object), "data"); - var Body = Expression.Block(Expression.Convert(Expression.Convert(DataParam, data.GetType()), Type)); - var Run = Expression.Lambda(Body, DataParam).Compile(); - var ret = Run.DynamicInvoke(data); - return ret; - } public static bool IsWhiteSpaceOrNewLine (this char c) { return c == '\t' || c == '\r' || c == '\n' || char.IsWhiteSpace (c); diff --git a/src/GraphicObjects/Button.cs b/src/GraphicObjects/Button.cs index 59762182..1cfbe85c 100644 --- a/src/GraphicObjects/Button.cs +++ b/src/GraphicObjects/Button.cs @@ -47,12 +47,6 @@ namespace Crow #endregion #region GraphicObject Overrides - public override void ResolveBindings () - { - base.ResolveBindings (); - if (Content != null) - Content.ResolveBindings (); - } public override void onMouseDown (object sender, MouseButtonEventArgs e) { IsPressed = true; diff --git a/src/GraphicObjects/ColorPicker.cs b/src/GraphicObjects/ColorPicker.cs index cc8dfac3..57f9610d 100644 --- a/src/GraphicObjects/ColorPicker.cs +++ b/src/GraphicObjects/ColorPicker.cs @@ -131,9 +131,21 @@ namespace Crow hsvFromRGB (); } } - + [XmlAttributeAttribute] + public virtual Color SelectedRawColor { + get { return curColor; } + set { + if (curColor == value) + return; + curColor = value; + notifyCurColorHasChanged (); + notifyRGBAHasChanged (); + hsvFromRGB (); + } + } void notifyCurColorHasChanged(){ NotifyValueChanged ("SelectedColor", SelectedColor); + NotifyValueChanged ("SelectedRawColor", curColor); string n = curColor.ToString (); if (char.IsLetter(n[0])) NotifyValueChanged ("SelectedColorName", n); @@ -154,6 +166,7 @@ namespace Crow } void hsvFromRGB(){ Color c = curColor; + c.ResetName (); double min = Math.Min (c.R, Math.Min (c.G, c.B)); //Min. value of RGB double max = Math.Max (c.R, Math.Max (c.G, c.B)); //Max. value of RGB double diff = max - min; //Delta RGB value @@ -188,7 +201,7 @@ namespace Crow } void rgbFromHSV(){ Color c = Color.Black; - + c.ResetName (); if (s == 0) {//HSV from 0 to 1 c.R = v; c.G = v; diff --git a/src/GraphicObjects/DataSourceChangeEventArgs.cs b/src/GraphicObjects/DataSourceChangeEventArgs.cs new file mode 100644 index 00000000..56d44428 --- /dev/null +++ b/src/GraphicObjects/DataSourceChangeEventArgs.cs @@ -0,0 +1,41 @@ +// +// DataSourceChangeEventArg.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2016 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +using System; + +namespace Crow +{ + public class DataSourceChangeEventArgs : EventArgs + { + public object OldDataSource; + public object NewDataSource; + + public DataSourceChangeEventArgs (object oldDataSource, object newDataSource) : base() + { + OldDataSource = oldDataSource; + NewDataSource = newDataSource; + } + public override string ToString () + { + return string.Format ("DSChangeEA: {0} => {1}", OldDataSource, NewDataSource); + } + } +} + diff --git a/src/GraphicObjects/DirectoryView.cs b/src/GraphicObjects/DirectoryView.cs index 4e29621e..5ed46f3e 100644 --- a/src/GraphicObjects/DirectoryView.cs +++ b/src/GraphicObjects/DirectoryView.cs @@ -52,7 +52,7 @@ namespace Crow NotifyValueChanged ("SelectedItem", _selectedItem); } } - [XmlAttributeAttribute()][DefaultValue(false)] + [XmlAttributeAttribute()][DefaultValue(true)] public virtual bool ShowFiles { get { return showFiles; } set { @@ -76,8 +76,10 @@ namespace Crow } [XmlIgnore]public FileSystemInfo[] FileSystemEntries { get { - return showFiles ? new DirectoryInfo (CurrentDirectory).GetFileSystemInfos (): - new DirectoryInfo(currentDirectory).GetDirectories(); + return string.IsNullOrEmpty(CurrentDirectory) ? null : + showFiles ? + new DirectoryInfo (CurrentDirectory).GetFileSystemInfos () : + new DirectoryInfo(CurrentDirectory).GetDirectories(); } } public void onSelectedItemChanged (object sender, SelectionChangeEventArgs e){ diff --git a/src/GraphicObjects/Expandable.cs b/src/GraphicObjects/Expandable.cs index 70451f41..38506de8 100644 --- a/src/GraphicObjects/Expandable.cs +++ b/src/GraphicObjects/Expandable.cs @@ -45,14 +45,12 @@ namespace Crow public event EventHandler Collapse; #endregion - #region GraphicObject overrides - public override void onMouseClick (object sender, MouseButtonEventArgs e) + public BooleanTestOnInstance GetIsExpandable; + + public void onClickForExpand (object sender, MouseButtonEventArgs e) { - if (this.HasFocus) - IsExpanded = !IsExpanded; - base.onMouseClick (sender, e); + IsExpanded = !IsExpanded; } - #endregion public override GraphicObject Content { get { @@ -70,15 +68,9 @@ namespace Crow _contentContainer = this.child.FindByName ("Content") as Container; } - public override void ResolveBindings () - { - base.ResolveBindings (); - if (Content != null) - Content.ResolveBindings (); - } #region Public properties - [XmlAttributeAttribute()][DefaultValue("Expandable")] + [XmlAttributeAttribute][DefaultValue("Expandable")] public string Caption { get { return caption; } set { @@ -88,7 +80,7 @@ namespace Crow NotifyValueChanged ("Caption", caption); } } - [XmlAttributeAttribute()][DefaultValue("#Crow.Images.Icons.expandable.svg")] + [XmlAttributeAttribute][DefaultValue("#Crow.Images.Icons.expandable.svg")] public string Image { get { return image; } set { @@ -98,7 +90,7 @@ namespace Crow NotifyValueChanged ("Image", image); } } - [XmlAttributeAttribute()][DefaultValue(false)] + [XmlAttributeAttribute][DefaultValue(false)] public bool IsExpanded { get { return _isExpanded; } @@ -109,7 +101,9 @@ namespace Crow _isExpanded = value; - if (!HasContent) + bool isExp = IsExpandable; + NotifyValueChanged ("IsExpandable", isExp); + if (!(HasContent & isExp)) _isExpanded = false; NotifyValueChanged ("IsExpanded", _isExpanded); @@ -123,7 +117,16 @@ namespace Crow [XmlIgnore]public bool HasContent { get { return _contentContainer == null ? false : _contentContainer.Child != null; } } - + [XmlIgnore]public bool IsExpandable { + get { + try { + return GetIsExpandable == null ? true : GetIsExpandable (this); + } catch (Exception ex) { + System.Diagnostics.Debug.WriteLine ("Not Expandable error: " + ex.ToString ()); + return false; + } + } + } #endregion public virtual void onExpand(object sender, EventArgs e) diff --git a/src/GraphicObjects/GenericStack.cs b/src/GraphicObjects/GenericStack.cs index 840e883f..a2d78d88 100644 --- a/src/GraphicObjects/GenericStack.cs +++ b/src/GraphicObjects/GenericStack.cs @@ -84,7 +84,7 @@ namespace Crow d += c.Slot.Height + Spacing; } } - bmp = null; + IsDirty = true; } GraphicObject stretchedGO = null; public override bool UpdateLayout (LayoutingType layoutType) @@ -99,7 +99,7 @@ namespace Crow ComputeChildrenPositions (); //if no layouting remains in queue for item, registre for redraw - if (RegisteredLayoutings == LayoutingType.None && bmp == null) + if (RegisteredLayoutings == LayoutingType.None && IsDirty) CurrentInterface.EnqueueForRepaint (this); return true; @@ -134,7 +134,7 @@ namespace Crow newW = Math.Min (newW, stretchedGO.MaximumSize.Width); if (newW != stretchedGO.Slot.Width) { stretchedGO.Slot.Width = newW; - stretchedGO.bmp = null; + stretchedGO.IsDirty = true; #if DEBUG_LAYOUTING Debug.WriteLine ("\tAdjusting Width of " + stretchedGO.ToString()); #endif @@ -173,7 +173,7 @@ namespace Crow newH = Math.Min (newH, stretchedGO.MaximumSize.Height); if (newH != stretchedGO.Slot.Height) { stretchedGO.Slot.Height = newH; - stretchedGO.bmp = null; + stretchedGO.IsDirty = true; #if DEBUG_LAYOUTING Debug.WriteLine ("\tAdjusting Height of " + stretchedGO.ToString()); #endif diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index 277e3d48..25332832 100644 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -12,16 +12,8 @@ using System.IO; namespace Crow { - public class GraphicObject : IXmlSerializable, ILayoutable, IValueChange, ICloneable, IBindable + public class GraphicObject : IXmlSerializable, ILayoutable, IValueChange, ICloneable { - #region IBindable implementation - List<Binding> bindings = new List<Binding> (); - public List<Binding> Bindings { - get { return bindings; } - } - - #endregion - internal static ulong currentUid = 0; internal ulong uid = 0; @@ -88,7 +80,7 @@ namespace Crow Size minimumSize = "0,0"; bool cacheEnabled = false; bool clipToClientRect = true; - object dataSource; + protected object dataSource; string style; #endregion @@ -115,6 +107,7 @@ namespace Crow public object Tag; /// <summary>drawing Cache, if null, a redraw is done, cached or not</summary> public byte[] bmp; + public bool IsDirty = true; /// <summary> /// This size is computed on each child' layout changes. /// In stacking widget, it is used to compute the remaining space for the stretched @@ -133,11 +126,29 @@ namespace Crow /// </summary> [XmlIgnore]public virtual ILayoutable Parent { get { return parent; } - set { parent = value; } + set { + if (parent == value) + return; + DataSourceChangeEventArgs e = new DataSourceChangeEventArgs (parent, value); + parent = value; + + onParentChanged (this, e); + } } [XmlIgnore]public ILayoutable LogicalParent { get { return logicalParent == null ? Parent : logicalParent; } - set { logicalParent = value; } + set { + if (logicalParent == value) + return; + if (logicalParent != null) + (logicalParent as GraphicObject).DataSourceChanged -= onLogicalParentDataSourceChanged; + DataSourceChangeEventArgs dsce = new DataSourceChangeEventArgs (LogicalParent, null); + logicalParent = value; + dsce.NewDataSource = LogicalParent; + if (logicalParent != null) + (logicalParent as GraphicObject).DataSourceChanged += onLogicalParentDataSourceChanged; + onLogicalParentChanged (this, dsce); + } } [XmlIgnore]public virtual Rectangle ClientRectangle { get { @@ -178,6 +189,9 @@ namespace Crow public event EventHandler Enabled; public event EventHandler Disabled; public event EventHandler<LayoutingEventArgs> LayoutChanged; + public event EventHandler<DataSourceChangeEventArgs> DataSourceChanged; + public event EventHandler<DataSourceChangeEventArgs> ParentChanged; + public event EventHandler<DataSourceChangeEventArgs> LogicalParentChanged; #endregion #region public properties @@ -376,6 +390,10 @@ namespace Crow return; hasFocus = value; + if (hasFocus) + onFocused (this, null); + else + onUnfocused (this, null); NotifyValueChanged ("HasFocus", hasFocus); } } @@ -399,17 +417,23 @@ namespace Crow NotifyValueChanged ("MouseRepeat", mouseRepeat); } } + bool clearBackground = false; [XmlAttributeAttribute()][DefaultValue("Transparent")] public virtual Fill Background { get { return background; } set { if (background == value) return; + clearBackground = false; if (value == null) return; background = value; NotifyValueChanged ("Background", background); RegisterForRedraw (); + if (background is SolidColor) { + if ((Background as SolidColor).Equals (Color.Clear)) + clearBackground = true; + } } } [XmlAttributeAttribute()][DefaultValue("White")] @@ -528,32 +552,45 @@ namespace Crow RegisterForLayouting (LayoutingType.Sizing); } } + /// <summary> + /// Seek first logical tree upward if logicalParent is set, or seek graphic tree for + /// a not null dataSource that will be active for all descendants having dataSource=null + /// </summary> [XmlAttributeAttribute][DefaultValue(null)] public virtual object DataSource { set { - if (dataSource == value) + if (DataSource == value) return; - #if DEBUG_BINDING - Debug.WriteLine("******************************"); - Debug.WriteLine("New DataSource for => " + this.ToString()); - Debug.WriteLine("\t- " + DataSource); - Debug.WriteLine("\t+ " + value); - #endif - - this.ClearBinding (); + DataSourceChangeEventArgs dse = new DataSourceChangeEventArgs (DataSource, null); dataSource = value; + dse.NewDataSource = DataSource; - this.ResolveBindings(); + OnDataSourceChanged (this, dse); - NotifyValueChanged ("DataSource", dataSource); + NotifyValueChanged ("DataSource", DataSource); } get { - return dataSource == null ? LogicalParent == null ? null : - LogicalParent is GraphicObject ? - (LogicalParent as GraphicObject).DataSource : null : dataSource; + return dataSource == null ? + LogicalParent == null ? null : + LogicalParent is GraphicObject ? (LogicalParent as GraphicObject).DataSource : null : + dataSource; } } + protected virtual void onLogicalParentDataSourceChanged(object sender, DataSourceChangeEventArgs e){ + if (localDataSourceIsNull) + OnDataSourceChanged (this, e); + } + internal bool localDataSourceIsNull { get { return dataSource == null; } } + internal bool localLogicalParentIsNull { get { return logicalParent == null; } } + + public virtual void OnDataSourceChanged(object sender, DataSourceChangeEventArgs e){ + DataSourceChanged.Raise (this, e); + #if DEBUG_BINDING + Debug.WriteLine("New DataSource for => {0} \n\t{1}=>{2}", this.ToString(),e.OldDataSource,e.NewDataSource); + #endif + } + [XmlAttributeAttribute] public virtual string Style { get { return style; } @@ -635,10 +672,10 @@ namespace Crow dm = new DynamicMethod("dyn_loadDefValues", MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot, CallingConventions.Standard, - typeof(void),new Type[] {typeof(object)},thisType,true); + typeof(void),new Type[] {CompilerServices.TObject},thisType,true); il = dm.GetILGenerator(256); - il.DeclareLocal(typeof(GraphicObject)); + il.DeclareLocal(CompilerServices.TObject); il.Emit(OpCodes.Nop); //set local GraphicObject to root object passed as 1st argument il.Emit (OpCodes.Ldarg_0); @@ -648,7 +685,26 @@ namespace Crow string expression; if (!getDefaultEvent(ei, styling, out expression)) continue; - CompilerServices.emitBindingCreation (il, ei.Name, expression); + //TODO:dynEventHandler could be cached somewhere, maybe a style instanciato class holding the styling delegate and bound to it. + foreach (string exp in CompilerServices.splitOnSemiColumnOutsideAccolades(expression)) { + string trimed = exp.Trim(); + if (trimed.StartsWith ("{", StringComparison.OrdinalIgnoreCase)){ + il.Emit (OpCodes.Ldloc_0);//load this as 1st arg of event Add + + //push eventInfo as 1st arg of compile + il.Emit (OpCodes.Ldloc_0); + il.Emit (OpCodes.Call, CompilerServices.miGetType); + il.Emit (OpCodes.Ldstr, ei.Name);//push event name + il.Emit (OpCodes.Call, CompilerServices.miGetEvent); + //push expression as 2nd arg of compile + il.Emit (OpCodes.Ldstr, trimed.Substring (1, trimed.Length - 2)); + il.Emit (OpCodes.Ldnull); + il.Emit (OpCodes.Callvirt, CompilerServices.miCompileDynEventHandler); + il.Emit (OpCodes.Castclass, ei.EventHandlerType); + il.Emit (OpCodes.Callvirt, ei.AddMethod); + }else + Debug.WriteLine("error in styling, event not handled : " + trimed); + } } foreach (PropertyInfo pi in thisType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { @@ -732,7 +788,7 @@ namespace Crow #region Queuing public virtual void RegisterClip(Rectangle clip){ - if (CacheEnabled && bmp != null) + if (CacheEnabled && !IsDirty) Clipping.AddRectangle (clip + ClientRectangle.Position); if (Parent != null) Parent.RegisterClip (clip + Slot.Position + ClientRectangle.Position); @@ -741,7 +797,7 @@ namespace Crow [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RegisterForGraphicUpdate () { - bmp = null; + IsDirty = true; if (Width.IsFit || Height.IsFit) RegisterForLayouting (LayoutingType.Sizing); else if (RegisteredLayoutings == LayoutingType.None) @@ -751,7 +807,7 @@ namespace Crow [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RegisterForRedraw () { - bmp = null; + IsDirty = true; if (RegisteredLayoutings == LayoutingType.None) CurrentInterface.EnqueueForRepaint (this); } @@ -864,7 +920,7 @@ namespace Crow if (LastSlots.X == Slot.X) break; - bmp = null; + IsDirty = true; OnLayoutChanges (layoutType); @@ -894,7 +950,7 @@ namespace Crow if (LastSlots.Y == Slot.Y) break; - bmp = null; + IsDirty = true; OnLayoutChanges (layoutType); @@ -930,7 +986,7 @@ namespace Crow if (LastSlots.Width == Slot.Width) break; - bmp = null; + IsDirty = true; OnLayoutChanges (layoutType); @@ -966,7 +1022,7 @@ namespace Crow if (LastSlots.Height == Slot.Height) break; - bmp = null; + IsDirty = true; OnLayoutChanges (layoutType); @@ -975,7 +1031,7 @@ namespace Crow } //if no layouting remains in queue for item, registre for redraw - if (this.registeredLayoutings == LayoutingType.None && bmp == null) + if (this.registeredLayoutings == LayoutingType.None && IsDirty) CurrentInterface.EnqueueForRepaint (this); return true; @@ -1002,7 +1058,7 @@ namespace Crow int bmpSize = Math.Abs (stride) * Slot.Height; bmp = new byte[bmpSize]; - + IsDirty = false; using (ImageSurface draw = new ImageSurface(bmp, Format.Argb32, Slot.Width, Slot.Height, stride)) { using (Context gr = new Context (draw)) { @@ -1014,16 +1070,13 @@ namespace Crow } protected virtual void UpdateCache(Context ctx){ Rectangle rb = Slot + Parent.ClientRectangle.Position; - using (ImageSurface cache = new ImageSurface (bmp, Format.Argb32, Slot.Width, Slot.Height, 4 * Slot.Width)) { - //TODO:improve equality test for basic color and Fill - if (this.Background is SolidColor) { - if ((this.Background as SolidColor).Equals (Color.Clear)) { + using (ImageSurface cache = new ImageSurface (bmp, Format.Argb32, Slot.Width, Slot.Height, 4 * Slot.Width)) { + if (clearBackground) { ctx.Save (); ctx.Operator = Operator.Clear; ctx.Rectangle (rb); ctx.Fill (); ctx.Restore (); - } } ctx.SetSourceSurface (cache, rb.X, rb.Y); ctx.Paint (); @@ -1035,9 +1088,6 @@ namespace Crow /// of the widget </summary> public virtual void Paint (ref Context ctx) { - if (!Visible) - return; - //TODO:this test should not be necessary if (Slot.Height < 0 || Slot.Width < 0) return; @@ -1050,7 +1100,7 @@ namespace Crow } if (cacheEnabled) { - if (bmp == null) + if (IsDirty) RecreateCache (); UpdateCache (ctx); @@ -1113,7 +1163,7 @@ namespace Crow onMouseEnter (this, e); } - this.onMouseMove (this, e); + this.onMouseMove (this, e);//without this, window border doesn't work, should be removed } public virtual void onMouseMove(object sender, MouseMoveEventArgs e) { @@ -1125,6 +1175,12 @@ namespace Crow MouseMove.Raise (sender, e); } public virtual void onMouseDown(object sender, MouseButtonEventArgs e){ + if (CurrentInterface.eligibleForDoubleClick == this && CurrentInterface.clickTimer.ElapsedMilliseconds < Interface.DoubleClick) + onMouseDoubleClick (this, e); + else + currentInterface.clickTimer.Restart(); + CurrentInterface.eligibleForDoubleClick = null; + if (CurrentInterface.activeWidget == null) CurrentInterface.activeWidget = this; if (this.Focusable && !Interface.FocusOnHover) { @@ -1149,19 +1205,13 @@ namespace Crow MouseUp.Raise (this, e); - if (MouseIsIn (e.Position) && IsActive) + if (MouseIsIn (e.Position) && IsActive) { + if (CurrentInterface.clickTimer.ElapsedMilliseconds < Interface.DoubleClick) + CurrentInterface.eligibleForDoubleClick = this; onMouseClick (this, e); + } } public virtual void onMouseClick(object sender, MouseButtonEventArgs e){ - - if (Interface.clickTimer.ElapsedMilliseconds > 0 && - Interface.clickTimer.ElapsedMilliseconds < Interface.DoubleClick) { - Interface.clickTimer.Reset (); - onMouseDoubleClick (this, e); - return; - } else - Interface.clickTimer.Restart (); - GraphicObject p = Parent as GraphicObject; if (p != null) p.onMouseClick(sender,e); @@ -1196,20 +1246,17 @@ namespace Crow } #endregion - public virtual void onFocused(object sender, EventArgs e){ + protected virtual void onFocused(object sender, EventArgs e){ #if DEBUG_FOCUS Debug.WriteLine("Focused => " + this.ToString()); #endif Focused.Raise (this, e); - this.HasFocus = true; } - public virtual void onUnfocused(object sender, EventArgs e){ + protected virtual void onUnfocused(object sender, EventArgs e){ #if DEBUG_FOCUS Debug.WriteLine("UnFocused => " + this.ToString()); #endif - Unfocused.Raise (this, e); - this.HasFocus = false; } public virtual void onEnable(object sender, EventArgs e){ Enabled.Raise (this, e); @@ -1217,147 +1264,73 @@ namespace Crow public virtual void onDisable(object sender, EventArgs e){ Disabled.Raise (this, e); } - - #region Binding - public void BindMember(string _member, string _expression){ - Bindings.Add(new Binding (this, _member, _expression)); + protected virtual void onParentChanged(object sender, DataSourceChangeEventArgs e) { + ParentChanged.Raise (this, e); + if (logicalParent == null) + LogicalParentChanged.Raise (this, e); } - public virtual void ResolveBindings() - { - if (Bindings.Count == 0) - return; - #if DEBUG_BINDING - Debug.WriteLine ("Resolve Bindings => " + this.ToString ()); - #endif - - CompilerServices.ResolveBindings (Bindings); + protected virtual void onLogicalParentChanged(object sender, DataSourceChangeEventArgs e) { + LogicalParentChanged.Raise (this, e); } - - /// <summary> - /// Remove dynamic delegates by ids from dataSource - /// and delete ref of this in Shared interface refs - /// </summary> - public virtual void ClearBinding(){ - //dont clear binding if dataSource is not null, - foreach (Binding b in Bindings) { - try { - if (!b.Resolved) - continue; - //cancel compiled events - if (b.Target == null){ - continue; - #if DEBUG_BINDING - Debug.WriteLine("Clear binding canceled for => " + b.ToString()); - #endif - } - if (b.Target.Instance != DataSource){ - #if DEBUG_BINDING - Debug.WriteLine("Clear binding canceled for => " + b.ToString()); - #endif - continue; - } - #if DEBUG_BINDING - Debug.WriteLine("ClearBinding => " + b.ToString()); - #endif - if (string.IsNullOrEmpty (b.DynMethodId)) { - b.Resolved = false; - if (b.Source.Member.MemberType == MemberTypes.Event) - removeEventHandler (b); - //TODO:check if full reset is necessary - continue; - } - MemberReference mr = null; - if (b.Target == null) - mr = b.Source; - else - mr = b.Target; - Type dataSourceType = mr.Instance.GetType(); - EventInfo evtInfo = dataSourceType.GetEvent ("ValueChanged"); - FieldInfo evtFi = CompilerServices.GetEventHandlerField (dataSourceType, "ValueChanged"); - MulticastDelegate multicastDelegate = evtFi.GetValue (mr.Instance) as MulticastDelegate; - if (multicastDelegate != null) { - foreach (Delegate d in multicastDelegate.GetInvocationList()) { - if (d.Method.Name == b.DynMethodId) - evtInfo.RemoveEventHandler (mr.Instance, d); - } - } - b.Reset (); - } catch (Exception ex) { - Debug.WriteLine("\t Error: " + ex.ToString()); - } - } - } - void removeEventHandler(Binding b){ - FieldInfo fiEvt = CompilerServices.GetEventHandlerField (b.Source.Instance.GetType(), b.Source.Member.Name); - MulticastDelegate multiDel = fiEvt.GetValue (b.Source.Instance) as MulticastDelegate; - if (multiDel != null) { - foreach (Delegate d in multiDel.GetInvocationList()) { - if (d.Method.Name == b.Target.Member.Name) - b.Source.Event.RemoveEventHandler (b.Source.Instance, d); - } - } - } - #endregion - #region IXmlSerializable public virtual System.Xml.Schema.XmlSchema GetSchema () { return null; } - void affectMember(string name, string value){ - Type thisType = this.GetType (); - - if (string.IsNullOrEmpty (value)) - return; - - MemberInfo mi = thisType.GetMember (name).FirstOrDefault(); - if (mi == null) { - Debug.WriteLine ("XML: Unknown attribute in " + thisType.ToString() + " : " + name); - return; - } - if (mi.MemberType == MemberTypes.Event) { - this.Bindings.Add (new Binding (new MemberReference(this, mi), value)); - return; - } - if (mi.MemberType == MemberTypes.Property) { - PropertyInfo pi = mi as PropertyInfo; - - if (pi.GetSetMethod () == null) { - Debug.WriteLine ("XML: Read only property in " + thisType.ToString() + " : " + name); - return; - } - - XmlAttributeAttribute xaa = (XmlAttributeAttribute)pi.GetCustomAttribute (typeof(XmlAttributeAttribute)); - if (xaa != null) { - if (!string.IsNullOrEmpty (xaa.AttributeName)) - name = xaa.AttributeName; - } - if (value.StartsWith("{",StringComparison.Ordinal)) { - //binding - if (!value.EndsWith("}", StringComparison.Ordinal)) - throw new Exception (string.Format("XML:Malformed binding: {0}", value)); - - this.Bindings.Add (new Binding (new MemberReference(this, pi), value.Substring (1, value.Length - 2))); - return; - } - if (pi.GetCustomAttribute (typeof(XmlIgnoreAttribute)) != null) - return; - if (xaa == null)//not define as xmlAttribute - return; - - if (pi.PropertyType == typeof(string)) { - pi.SetValue (this, value, null); - return; - } - - if (pi.PropertyType.IsEnum) { - pi.SetValue (this, Enum.Parse (pi.PropertyType, value), null); - } else { - MethodInfo me = pi.PropertyType.GetMethod ("Parse", new Type[] { typeof(string) }); - pi.SetValue (this, me.Invoke (null, new string[] { value }), null); - } - } - } +// void affectMember(string name, string value){ +// Type thisType = this.GetType (); +// +// if (string.IsNullOrEmpty (value)) +// return; +// +// MemberInfo mi = thisType.GetMember (name).FirstOrDefault(); +// if (mi == null) { +// Debug.WriteLine ("XML: Unknown attribute in " + thisType.ToString() + " : " + name); +// return; +// } +// if (mi.MemberType == MemberTypes.Event) { +// this.Bindings.Add (new Binding (new MemberReference(this, mi), value)); +// return; +// } +// if (mi.MemberType == MemberTypes.Property) { +// PropertyInfo pi = mi as PropertyInfo; +// +// if (pi.GetSetMethod () == null) { +// Debug.WriteLine ("XML: Read only property in " + thisType.ToString() + " : " + name); +// return; +// } +// +// XmlAttributeAttribute xaa = (XmlAttributeAttribute)pi.GetCustomAttribute (typeof(XmlAttributeAttribute)); +// if (xaa != null) { +// if (!string.IsNullOrEmpty (xaa.AttributeName)) +// name = xaa.AttributeName; +// } +// if (value.StartsWith("{",StringComparison.Ordinal)) { +// //binding +// if (!value.EndsWith("}", StringComparison.Ordinal)) +// throw new Exception (string.Format("XML:Malformed binding: {0}", value)); +// +// this.Bindings.Add (new Binding (new MemberReference(this, pi), value.Substring (1, value.Length - 2))); +// return; +// } +// if (pi.GetCustomAttribute (typeof(XmlIgnoreAttribute)) != null) +// return; +// if (xaa == null)//not define as xmlAttribute +// return; +// +// if (pi.PropertyType == typeof(string)) { +// pi.SetValue (this, value, null); +// return; +// } +// +// if (pi.PropertyType.IsEnum) { +// pi.SetValue (this, Enum.Parse (pi.PropertyType, value), null); +// } else { +// MethodInfo me = pi.PropertyType.GetMethod ("Parse", new Type[] { typeof(string) }); +// pi.SetValue (this, me.Invoke (null, new string[] { value }), null); +// } +// } +// } public virtual void ReadXml (System.Xml.XmlReader reader) { if (reader.HasAttributes) { @@ -1370,7 +1343,7 @@ namespace Crow if (reader.Name == "Style") continue; - affectMember (reader.Name, reader.Value); + //affectMember (reader.Name, reader.Value); } reader.MoveToElement (); }else @@ -1488,12 +1461,6 @@ namespace Crow /// <summary> /// full GraphicTree clone with binding definition /// </summary> - public virtual GraphicObject DeepClone(){ - GraphicObject tmp = Clone () as GraphicObject; - foreach (Binding b in this.bindings) - tmp.Bindings.Add (new Binding (new MemberReference (tmp, b.Source.Member), b.Expression)); - return tmp; - } public override string ToString () { diff --git a/src/GraphicObjects/Grid.cs b/src/GraphicObjects/Grid.cs index a0f0cb69..57ceda5e 100644 --- a/src/GraphicObjects/Grid.cs +++ b/src/GraphicObjects/Grid.cs @@ -134,7 +134,7 @@ namespace Crow ComputeChildrenPositions (); //if no layouting remains in queue for item, registre for redraw - if (RegisteredLayoutings == LayoutingType.None && bmp == null) + if (RegisteredLayoutings == LayoutingType.None && IsDirty) CurrentInterface.EnqueueForRepaint (this); return true; diff --git a/src/GraphicObjects/Group.cs b/src/GraphicObjects/Group.cs index efe02d95..a285e282 100644 --- a/src/GraphicObjects/Group.cs +++ b/src/GraphicObjects/Group.cs @@ -40,7 +40,6 @@ namespace Crow lock (children) Children.Add(g); g.Parent = this; - g.ResolveBindings (); g.RegisteredLayoutings = LayoutingType.None; g.RegisterForLayouting (LayoutingType.Sizing | LayoutingType.ArrangeChildren); g.LayoutChanged += OnChildLayoutChanges; @@ -48,7 +47,6 @@ namespace Crow public virtual void RemoveChild(GraphicObject child) { child.LayoutChanged -= OnChildLayoutChanges; - child.ClearBinding (); //child.Parent = null; lock (children) Children.Remove(child); @@ -65,7 +63,6 @@ namespace Crow while(Children.Count > 0){ GraphicObject g = Children[Children.Count-1]; g.LayoutChanged -= OnChildLayoutChanges; - g.ClearBinding (); g.Parent = null; Children.RemoveAt(Children.Count-1); } @@ -94,11 +91,12 @@ namespace Crow } #region GraphicObject overrides - public override void ResolveBindings () + public override void OnDataSourceChanged (object sender, DataSourceChangeEventArgs e) { - base.ResolveBindings (); - foreach (GraphicObject w in Children) - w.ResolveBindings (); + base.OnDataSourceChanged (this, e); + foreach (GraphicObject g in children) + if (g.localDataSourceIsNull & g.localLogicalParentIsNull) + g.OnDataSourceChanged (sender, e); } public override GraphicObject FindByName (string nameToFind) { @@ -365,17 +363,5 @@ namespace Crow #endregion - public override void ClearBinding(){ - foreach (GraphicObject c in Children) - c.ClearBinding (); - base.ClearBinding (); - } - public override GraphicObject DeepClone () - { - Group tmp = base.DeepClone () as Group; - foreach (GraphicObject c in Children) - tmp.AddChild (c.DeepClone ()); - return tmp; - } } } diff --git a/src/GraphicObjects/Image.cs b/src/GraphicObjects/Image.cs index 61b6e059..fa036b97 100644 --- a/src/GraphicObjects/Image.cs +++ b/src/GraphicObjects/Image.cs @@ -50,9 +50,11 @@ namespace Crow _pic = null; return; } - LoadImage (value); - _pic.Scaled = scaled; - _pic.KeepProportions = keepProps; + lock(CurrentInterface.LayoutMutex){ + LoadImage (value); + _pic.Scaled = scaled; + _pic.KeepProportions = keepProps; + } } catch (Exception ex) { Debug.WriteLine (ex.Message); _pic = null; @@ -86,7 +88,6 @@ namespace Crow _pic.LoadImage (path); RegisterForGraphicUpdate (); - RegisterForLayouting (LayoutingType.Sizing); } #endregion diff --git a/src/GraphicObjects/Label.cs b/src/GraphicObjects/Label.cs index 666f00a2..2f35c223 100644 --- a/src/GraphicObjects/Label.cs +++ b/src/GraphicObjects/Label.cs @@ -670,7 +670,7 @@ namespace Crow base.onMouseLeave (sender, e); CurrentInterface.MouseCursor = XCursor.Default; } - public override void onFocused (object sender, EventArgs e) + protected override void onFocused (object sender, EventArgs e) { base.onFocused (sender, e); @@ -680,7 +680,7 @@ namespace Crow SelRelease = new Point (lines.LastOrDefault ().Length, lines.Count-1); RegisterForRedraw (); } - public override void onUnfocused (object sender, EventArgs e) + protected override void onUnfocused (object sender, EventArgs e) { base.onUnfocused (sender, e); diff --git a/src/GraphicObjects/Menu.cs b/src/GraphicObjects/Menu.cs index 765925e9..1a7a3f95 100644 --- a/src/GraphicObjects/Menu.cs +++ b/src/GraphicObjects/Menu.cs @@ -26,10 +26,14 @@ namespace Crow { public class Menu : TemplatedGroup { + #region CTOR + public Menu () : base() {} + #endregion + Orientation orientation; [XmlAttributeAttribute()][DefaultValue(Orientation.Horizontal)] - public virtual Orientation Orientation { + public Orientation Orientation { get { return orientation; } set { if (orientation == value) @@ -38,23 +42,15 @@ namespace Crow NotifyValueChanged ("Orientation", orientation); } } - bool isOpened = false; - public bool IsOpened - { - get { return isOpened; } - set - { - isOpened = value; - System.Diagnostics.Debug.WriteLine ("is opened = " + isOpened); - } - } - - public Menu () : base() + bool autoOpen = false; + [XmlIgnore]public bool AutomaticOpenning { + get { return autoOpen; } + set { autoOpen = value; } } public override void AddItem (GraphicObject g) - { + { base.AddItem (g); if (orientation == Orientation.Horizontal) @@ -65,7 +61,7 @@ namespace Crow public override void onMouseLeave (object sender, MouseMoveEventArgs e) { base.onMouseLeave (sender, e); - IsOpened = false; + AutomaticOpenning = false; } } } diff --git a/src/GraphicObjects/MenuItem.cs b/src/GraphicObjects/MenuItem.cs index 78162fbb..80b5759d 100644 --- a/src/GraphicObjects/MenuItem.cs +++ b/src/GraphicObjects/MenuItem.cs @@ -24,7 +24,7 @@ using System.ComponentModel; namespace Crow { - public class MenuItem : TemplatedGroup + public class MenuItem : Menu { #region CTOR public MenuItem () : base() {} @@ -40,7 +40,6 @@ namespace Crow [XmlAttributeAttribute][DefaultValue(false)] public bool IsOpened { - //get { return MenuRoot == null ? false : MenuRoot.IsOpened; } get { return isOpened; } set { if (isOpened == value) @@ -50,8 +49,7 @@ namespace Crow if (isOpened) { onOpen (this, null); - if (MenuRoot != null) - MenuRoot.IsOpened = true; + (LogicalParent as Menu).AutomaticOpenning = true; }else onClose (this, null); } @@ -78,19 +76,7 @@ namespace Crow NotifyValueChanged ("Caption", caption); } } - - [XmlIgnore]Menu MenuRoot { - get { - ILayoutable tmp = LogicalParent; - while (tmp != null) { - if (tmp is Menu) - return tmp as Menu; - tmp = tmp.LogicalParent; - } - return null; - } - } - + public override void AddItem (GraphicObject g) { base.AddItem (g); @@ -100,21 +86,30 @@ namespace Crow void onMI_Click (object sender, MouseButtonEventArgs e) { Execute.Raise (this, null); + if(!IsOpened) + (LogicalParent as Menu).AutomaticOpenning = false; } protected virtual void onOpen (object sender, EventArgs e){ - MenuRoot.IsOpened = true; Open.Raise (this, null); } protected virtual void onClose (object sender, EventArgs e){ - //MenuRoot.IsOpened = true; Close.Raise (this, null); } + public override bool MouseIsIn (Point m) + { + return base.MouseIsIn (m) || child.MouseIsIn (m); + } public override void onMouseEnter (object sender, MouseMoveEventArgs e) { base.onMouseEnter (sender, e); - if (MenuRoot == null || Items.Count == 0) - return; - IsOpened = MenuRoot.IsOpened; + if ((LogicalParent as Menu).AutomaticOpenning && items.Children.Count>0) + IsOpened = true; + } + public override void onMouseLeave (object sender, MouseMoveEventArgs e) + { + if (IsOpened) + IsOpened = false; + base.onMouseLeave (this, e); } } } diff --git a/src/GraphicObjects/Popper.cs b/src/GraphicObjects/Popper.cs index 669b8d7f..38621337 100644 --- a/src/GraphicObjects/Popper.cs +++ b/src/GraphicObjects/Popper.cs @@ -175,42 +175,42 @@ namespace Crow _content.MinimumSize = new Size (this.Slot.Width, _content.MinimumSize.Height); } - public override void ClearBinding () - { - //ensure popped window is cleared - if (Content != null) { - if (Content.Parent != null) - CurrentInterface.DeleteWidget (Content); - } - base.ClearBinding (); - } - public override void ResolveBindings () - { - base.ResolveBindings (); - if (Content != null) - Content.ResolveBindings (); - } public override void onMouseClick (object sender, MouseButtonEventArgs e) { if (_canPop) IsPopped = !IsPopped; - base.onMouseClick (sender, e); + base.onMouseClick (this, e); } public override void onMouseLeave (object sender, MouseMoveEventArgs e) { - if (!_isPopped || _content == null) { - base.onMouseLeave (sender, e); - System.Diagnostics.Debug.WriteLine ("NotPopped***popper mouse leave:"+ this.ToString()); - return; + base.onMouseLeave (this, e); + IsPopped = false; + } + public override bool MouseIsIn (Point m) + { + bool isInContent = false; + if (Content != null) { + if (Content.Parent != null) + isInContent = Content.MouseIsIn (m); } - - if (!_content.MouseIsIn (e.Position)) { - base.onMouseLeave (sender, e); - System.Diagnostics.Debug.WriteLine ("***popper mouse leave:"+ this.ToString()); - IsPopped = false; - return; + return base.MouseIsIn (m) || isInContent; + } + public override void checkHoverWidget (MouseMoveEventArgs e) + { + if (CurrentInterface.HoverWidget != this) { + CurrentInterface.HoverWidget = this; + onMouseEnter (this, e); + } + if (Content != null){ + if (Content.Parent != null) { + if (Content.MouseIsIn (e.Position)) { + Content.checkHoverWidget (e); + return; + } + } } + base.checkHoverWidget (e); } #endregion diff --git a/src/GraphicObjects/PrivateContainer.cs b/src/GraphicObjects/PrivateContainer.cs index 2a5deeaa..9c65e935 100644 --- a/src/GraphicObjects/PrivateContainer.cs +++ b/src/GraphicObjects/PrivateContainer.cs @@ -47,7 +47,6 @@ namespace Crow { if (child != null) { - child.ClearBinding (); contentSize = new Size (0, 0); child.LayoutChanged -= OnChildLayoutChanges; child.Parent = null; @@ -66,16 +65,7 @@ namespace Crow } #region GraphicObject Overrides - public override void ResolveBindings () - { - base.ResolveBindings (); - if (child != null) - child.ResolveBindings (); - } - protected void ResolveBindingsWithNoRecurse () - { - base.ResolveBindings (); - } + public override GraphicObject FindByName (string nameToFind) { if (Name == nameToFind) @@ -88,6 +78,13 @@ namespace Crow return child == goToFind ? true : child == null ? false : child.Contains(goToFind); } + public override void OnDataSourceChanged (object sender, DataSourceChangeEventArgs e) + { + base.OnDataSourceChanged (this, e); + if (child != null) + if (child.localDataSourceIsNull & child.localLogicalParentIsNull) + child.OnDataSourceChanged (sender, e); + } public override bool UpdateLayout (LayoutingType layoutType) { if (child != null) { @@ -161,8 +158,10 @@ namespace Crow gr.Clip (); } - if (child != null) - child.Paint (ref gr); + if (child != null) { + if (child.Visible) + child.Paint (ref gr); + } gr.Restore (); } protected override void UpdateCache (Context ctx) @@ -197,19 +196,6 @@ namespace Crow } #endregion - public override void ClearBinding () - { - if (child != null) - child.ClearBinding (); - base.ClearBinding (); - } - public override GraphicObject DeepClone () - { - PrivateContainer tmp = base.DeepClone () as PrivateContainer; - if (child != null) - tmp.SetChild (child.DeepClone ()); - return tmp; - } } } diff --git a/src/GraphicObjects/TabItem.cs b/src/GraphicObjects/TabItem.cs index e14a7815..aca033d9 100644 --- a/src/GraphicObjects/TabItem.cs +++ b/src/GraphicObjects/TabItem.cs @@ -27,17 +27,17 @@ namespace Crow { public class TabItem : TemplatedContainer { + #region CTOR + public TabItem () : base() {} + #endregion + #region Private fields string caption; Container _contentContainer; GraphicObject _tabTitle; int tabOffset; - #endregion - - #region CTOR - public TabItem () : base() - { - } + bool isSelected; + Measure tabThickness; #endregion #region TemplatedControl overrides @@ -46,15 +46,15 @@ namespace Crow return _contentContainer == null ? null : _contentContainer.Child; } set { + if (Content != null) { + Content.LogicalParent = null; + _contentContainer.SetChild (null); + } _contentContainer.SetChild(value); + if (value != null) + value.LogicalParent = this; } } - public override void ResolveBindings () - { - base.ResolveBindings (); - if (_contentContainer != null) - _contentContainer.ResolveBindings (); - } protected override void loadTemplate(GraphicObject template = null) { base.loadTemplate (template); @@ -65,6 +65,17 @@ namespace Crow internal GraphicObject TabTitle { get { return _tabTitle; }} #endregion + [XmlAttributeAttribute][DefaultValue("18")] + public virtual Measure TabThickness { + get { return tabThickness; } + set { + if (tabThickness == value) + return; + tabThickness = value; + NotifyValueChanged ("TabThickness", tabThickness); + RegisterForGraphicUpdate (); + } + } [XmlAttributeAttribute][DefaultValue(0)] public virtual int TabOffset { get { return tabOffset; } @@ -88,7 +99,6 @@ namespace Crow NotifyValueChanged ("Caption", caption); } } - bool isSelected; [XmlAttributeAttribute][DefaultValue(false)] public virtual bool IsSelected { get { return isSelected; } @@ -101,9 +111,11 @@ namespace Crow } protected override void onDraw (Cairo.Context gr) { + gr.Save (); + int spacing = (Parent as TabView).Spacing; - gr.MoveTo (0, TabTitle.Slot.Bottom-0.5); + gr.MoveTo (0.5, TabTitle.Slot.Bottom-0.5); gr.LineTo (TabTitle.Slot.Left - spacing, TabTitle.Slot.Bottom-0.5); gr.CurveTo ( TabTitle.Slot.Left - spacing / 2, TabTitle.Slot.Bottom-0.5, @@ -114,16 +126,16 @@ namespace Crow TabTitle.Slot.Right + spacing / 2, 0.5, TabTitle.Slot.Right + spacing / 2, TabTitle.Slot.Bottom-0.5, TabTitle.Slot.Right + spacing, TabTitle.Slot.Bottom-0.5); - gr.LineTo (Slot.Width, TabTitle.Slot.Bottom-0.5); + gr.LineTo (Slot.Width-0.5, TabTitle.Slot.Bottom-0.5); + - gr.LineWidth = 1; + gr.LineTo (Slot.Width-0.5, Slot.Height-0.5); + gr.LineTo (0.5, Slot.Height-0.5); + gr.ClosePath (); + gr.LineWidth = 2; Foreground.SetAsSource (gr); gr.StrokePreserve (); - gr.LineTo (Slot.Width, Slot.Height); - gr.LineTo (0, Slot.Height); - gr.ClosePath (); - gr.Save (); gr.Clip (); base.onDraw (gr); gr.Restore (); diff --git a/src/GraphicObjects/TabView.cs b/src/GraphicObjects/TabView.cs index 42bb39c0..1debfedc 100644 --- a/src/GraphicObjects/TabView.cs +++ b/src/GraphicObjects/TabView.cs @@ -25,10 +25,13 @@ using Cairo; using System.Diagnostics; namespace Crow -{ - //TODO:use TemplatedGroup +{ public class TabView : Group { + #region CTOR + public TabView () : base() {} + #endregion + #region Private fields int _spacing; Measure tabThickness; @@ -36,9 +39,6 @@ namespace Crow int selectedTab = 0; #endregion - public TabView () : base() - { - } #region public properties [XmlAttributeAttribute()][DefaultValue(Orientation.Horizontal)] @@ -56,7 +56,7 @@ namespace Crow NotifyValueChanged ("TabOrientation", Orientation.Horizontal); } } - [XmlAttributeAttribute()][DefaultValue(20)] + [XmlAttributeAttribute()][DefaultValue(16)] public int Spacing { get { return _spacing; } @@ -83,17 +83,6 @@ namespace Crow RegisterForRedraw (); } } - [XmlAttributeAttribute()][DefaultValue("18")] - public virtual Measure TabThickness { - get { return tabThickness; } - set { - if (tabThickness == value) - return; - tabThickness = value; - NotifyValueChanged ("TabThickness", tabThickness); - RegisterForGraphicUpdate (); - } - } #endregion public override void AddChild (GraphicObject child) @@ -143,7 +132,7 @@ namespace Crow } //if no layouting remains in queue for item, registre for redraw - if (RegisteredLayoutings == LayoutingType.None && bmp == null) + if (RegisteredLayoutings == LayoutingType.None && IsDirty) CurrentInterface.EnqueueForRepaint (this); return true; diff --git a/src/GraphicObjects/TemplatedContainer.cs b/src/GraphicObjects/TemplatedContainer.cs index 7a4649a4..44c2ad08 100644 --- a/src/GraphicObjects/TemplatedContainer.cs +++ b/src/GraphicObjects/TemplatedContainer.cs @@ -34,19 +34,6 @@ namespace Crow [XmlIgnore]public abstract GraphicObject Content{ get; set;} #region GraphicObject overrides - public override void ClearBinding () - { - if (Content != null) - Content.ClearBinding (); - - base.ClearBinding (); - } -// public override void ResolveBindings () -// { -// base.ResolveBindings (); -// if (Content != null) -// Content.ResolveBindings (); -// } public override GraphicObject FindByName (string nameToFind) { if (Name == nameToFind) diff --git a/src/GraphicObjects/TemplatedControl.cs b/src/GraphicObjects/TemplatedControl.cs index 7f2e1b87..ba49f44f 100644 --- a/src/GraphicObjects/TemplatedControl.cs +++ b/src/GraphicObjects/TemplatedControl.cs @@ -94,8 +94,6 @@ namespace Crow this.SetChild (CurrentInterface.Load (Interface.DefaultTemplates[this.GetType ().FullName])); }else this.SetChild (template); - - this.ResolveBindings (); } //TODO:IXmlSerializable is not used anymore diff --git a/src/GraphicObjects/TemplatedGroup.cs b/src/GraphicObjects/TemplatedGroup.cs index 7fb3ceff..f636786d 100644 --- a/src/GraphicObjects/TemplatedGroup.cs +++ b/src/GraphicObjects/TemplatedGroup.cs @@ -42,7 +42,6 @@ namespace Crow public event EventHandler<SelectionChangeEventArgs> SelectedItemChanged; #endregion - Group _list; IList data; int _selectedIndex; Color selBackground, selForeground; @@ -121,7 +120,9 @@ namespace Crow [XmlIgnore]public virtual object SelectedItem{ get { return data == null ? null : _selectedIndex < 0 ? null : data[_selectedIndex]; } } - + [XmlIgnore]public bool HasItems { + get { return Items.Count > 0; } + } [XmlAttributeAttribute]public IList Data { get { return data; } set { @@ -148,6 +149,7 @@ namespace Crow NotifyValueChanged ("SelectedIndex", _selectedIndex); NotifyValueChanged ("SelectedItem", SelectedItem); SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem)); + NotifyValueChanged ("HasItems", HasItems); } } @@ -181,10 +183,12 @@ namespace Crow public virtual void AddItem(GraphicObject g){ items.AddChild (g); + g.LogicalParent = this; NotifyValueChanged ("HasChildren", true); } public virtual void RemoveItem(GraphicObject g) { + g.LogicalParent = null; items.RemoveChild (g); if (items.Children.Count == 0) NotifyValueChanged ("HasChildren", false); @@ -302,6 +306,7 @@ namespace Crow if (cancelLoading) return; loadPage (i); + Thread.Sleep (1); } } void cancelLoadingThread(){ @@ -320,7 +325,23 @@ namespace Crow loadingTime.Start (); #endif - Group page = items.Clone () as Group; + Group page; + if (typeof(Wrapper).IsAssignableFrom (items.GetType ())){ + page = items; + itemPerPage = int.MaxValue; + }else if (typeof(GenericStack).IsAssignableFrom (items.GetType ())) { + GenericStack gs = new GenericStack (); + gs.CurrentInterface = items.CurrentInterface; + gs.initialize (); + gs.Orientation = (items as GenericStack).Orientation; + gs.Width = items.Width; + gs.Height = items.Height; + gs.VerticalAlignment = items.VerticalAlignment; + gs.HorizontalAlignment = items.HorizontalAlignment; + page = gs; + + }else + page = Activator.CreateInstance (items.GetType ()) as Group; page.Name = "page" + pageNum; @@ -331,10 +352,10 @@ namespace Crow return; loadItem (i, page); - - //g.LogicalParent = this; } + if (page == items) + return; lock (CurrentInterface.LayoutMutex) items.AddChild (page); @@ -369,16 +390,16 @@ namespace Crow lock (CurrentInterface.LayoutMutex) { g = iTemp.CreateInstance(CurrentInterface); page.AddChild (g); - g.DataSource = data [i]; - if (typeof(IBindable).IsAssignableFrom (dataType)) - CompilerServices.ResolveBindings ((data [i] as IBindable).Bindings); + //g.LogicalParent = this; + registerItemClick (g); } - registerItemClick (g); - if (iTemp.Expand != null && g is Expandable) { (g as Expandable).Expand += iTemp.Expand; - } + (g as Expandable).GetIsExpandable = iTemp.HasSubItems; + } + + g.DataSource = data [i]; } protected virtual void registerItemClick(GraphicObject g){ g.MouseClick += itemClick; diff --git a/src/GraphicObjects/Window.cs b/src/GraphicObjects/Window.cs index 9ef9e642..86f05d86 100644 --- a/src/GraphicObjects/Window.cs +++ b/src/GraphicObjects/Window.cs @@ -134,12 +134,6 @@ namespace Crow #endregion #region GraphicObject Overrides - public override void ResolveBindings () - { - base.ResolveBindings (); - if (Content != null) - Content.ResolveBindings (); - } public override void onMouseMove (object sender, MouseMoveEventArgs e) { base.onMouseMove (sender, e); diff --git a/src/GraphicObjects/Wrapper.cs b/src/GraphicObjects/Wrapper.cs index 1bfcd92a..fcf88583 100644 --- a/src/GraphicObjects/Wrapper.cs +++ b/src/GraphicObjects/Wrapper.cs @@ -76,7 +76,7 @@ namespace Crow dy += c.Slot.Height + Spacing; } } - bmp = null; + IsDirty = true; } public override void OnChildLayoutChanges (object sender, LayoutingEventArgs arg) { @@ -180,7 +180,7 @@ namespace Crow ComputeChildrenPositions (); //if no layouting remains in queue for item, registre for redraw - if (RegisteredLayoutings == LayoutingType.None && bmp == null) + if (RegisteredLayoutings == LayoutingType.None && IsDirty) CurrentInterface.EnqueueForRepaint (this); return true; diff --git a/src/IML/BindingDefinition.cs b/src/IML/BindingDefinition.cs new file mode 100644 index 00000000..eadbc9df --- /dev/null +++ b/src/IML/BindingDefinition.cs @@ -0,0 +1,84 @@ +// +// BindingDefinition.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2016 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +using System; + +namespace Crow.IML +{ + /// <summary> + /// store binding source and target addresses and member names + /// </summary> + public class BindingDefinition + { + public NodeAddress SourceNA = null; + public string SourceMember = ""; + public NodeAddress TargetNA = null; + public string TargetMember = ""; + public string TargetName = ""; + public bool TwoWay = false; + + #region CTOR + public BindingDefinition (NodeAddress _sourceNA, string _sourceMember){ + SourceNA = _sourceNA; + SourceMember = _sourceMember; + } + public BindingDefinition (NodeAddress _sourceNA, string _sourceMember, NodeAddress _targetNA, string _targetMember, string _targetName = "", bool _twoWay = false) + { + SourceNA = _sourceNA; + SourceMember = _sourceMember; + TargetNA = _targetNA; + TargetMember = _targetMember; + TargetName = _targetName; + TwoWay = _twoWay; + } + #endregion + + public bool IsDataSourceBinding { get { return TargetNA == null; }} + public bool IsTemplateBinding { get { return IsDataSourceBinding ? false : TargetNA.Count == 0; }} + public bool HasUnresolvedTargetName { get { return !string.IsNullOrEmpty(TargetName); }} + public MemberAddress SourceMemberAddress { get { return new MemberAddress (SourceNA, SourceMember);}} + public MemberAddress TargetMemberAddress { get { return new MemberAddress (TargetNA, TargetMember);}} + + /// <summary> + /// replace the target node address with corresponding named node address, and clear the target name once resolved + /// </summary> + /// <param name="newTargetNA">Named Node</param> + public void ResolveTargetName(NodeAddress newTargetNA){ + TargetNA = newTargetNA; + TargetName = ""; + } + + public override string ToString () + { + string tmp = string.Format ("Source:{0}.{1}", SourceNA, SourceMember); + if (TwoWay) + tmp += " <=> "; + else + tmp += " <= "; + + if (string.IsNullOrEmpty (TargetName)) + tmp += string.Format ("Target:{0}.{1}]", TargetNA, TargetMember); + else + tmp += string.Format ("Target:{0}.{1}.{2}]", TargetNA, TargetName, TargetMember); + return tmp; + } + } +} + diff --git a/src/IML/Context.cs b/src/IML/Context.cs new file mode 100644 index 00000000..7f739c37 --- /dev/null +++ b/src/IML/Context.cs @@ -0,0 +1,193 @@ +// +// Context.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2016 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.Xml; + +namespace Crow.IML +{ + public class DataSourceBinding { + public bool TwoWay; + public MemberAddress Source; + public string DataSourceMember; + } + /// <summary> + /// Context while parsing IML, this will store what's needed only while parsing and not during instancing + /// </summary> + public class Context + { + public XmlTextReader reader = null; + public Type RootType = null; + + public DynamicMethod dm = null; + public ILGenerator il = null; + //public SubNodeType curSubNodeType; + public NodeStack nodesStack = new NodeStack (); + + /// <summary> store addresses of named node for name resolution at end of parsing </summary> + public Dictionary<string, List<NodeAddress>> Names = new Dictionary<string, List<NodeAddress>>(); + /// <summary> Store non datasource binding (in tree and template) by origine and orig member </summary> + public Dictionary<NodeAddress, Dictionary<string, List<MemberAddress>>> Bindings = + new Dictionary<NodeAddress, Dictionary<string, List<MemberAddress>>>(); + /// <summary> Store binding with name in target, will be resolved at end of parsing </summary> + public List<BindingDefinition> UnresolvedTargets = new List<BindingDefinition>(); + + + public Context (Type rootType) + { + RootType = rootType; + dm = new DynamicMethod ("dyn_instantiator", + CompilerServices.TObject, new Type [] { typeof (Instantiator), typeof (Interface) }, true); + il = dm.GetILGenerator (256); + + il.DeclareLocal (typeof (GraphicObject)); + il.Emit (OpCodes.Nop); + //set local GraphicObject to root object + il.Emit (OpCodes.Newobj, rootType.GetConstructors () [0]); + il.Emit (OpCodes.Stloc_0); + CompilerServices.emitSetCurInterface (il); + } + + public NodeAddress CurrentNodeAddress { + get { + Node[] n = nodesStack.ToArray (); + Array.Reverse (n); + return new NodeAddress(n); + } + } + public Type CurrentNodeType { + get { return nodesStack.Peek().CrowType; } + } + public void StorePropertyBinding(NodeAddress origNA, string origMember, NodeAddress destNA, string destMember){ + Dictionary<string, List<MemberAddress>> nodeBindings = null; + if (Bindings.ContainsKey (origNA)) + nodeBindings = Bindings [origNA]; + else { + nodeBindings = new Dictionary<string, List<MemberAddress>> (); + Bindings [origNA] = nodeBindings; + } + + if (!nodeBindings.ContainsKey (origMember)) + nodeBindings [origMember] = new List<MemberAddress> (); + nodeBindings [origMember].Add (new MemberAddress (destNA, destMember)); + } + public void StorePropertyBinding(BindingDefinition bindDef){ + if (bindDef.HasUnresolvedTargetName) { + UnresolvedTargets.Add (bindDef); + return; + } + StorePropertyBinding (bindDef.TargetNA, bindDef.TargetMember, bindDef.SourceNA, bindDef.SourceMember); + if (bindDef.TwoWay) + StorePropertyBinding (bindDef.SourceNA, bindDef.SourceMember, bindDef.TargetNA, bindDef.TargetMember); + } + public void StoreCurrentName(string name){ + if (!Names.ContainsKey(name)) + Names[name] = new List<NodeAddress>(); + Names[name].Add(CurrentNodeAddress); + } + public void ResolveNamedTargets(){//TODO:methodinfo fetching is redundant with early parsing + foreach (BindingDefinition bd in UnresolvedTargets) { + if (bd.HasUnresolvedTargetName) { + try { + ResolveName (bd); + } catch (Exception ex) { + System.Diagnostics.Debug.WriteLine (ex.ToString ()); + continue; + } + } + + if (bd is EventBinding) { + emitHandlerMethodAddition (bd as EventBinding); + continue; + } + + MemberInfo miSource = bd.SourceMemberAddress.member; + if (miSource == null) + throw new Exception ("Source member '" + bd.SourceMember + "' not found"); + StorePropertyBinding (bd); + } + } + public void ResolveName (BindingDefinition bd){ + + if (!Names.ContainsKey (bd.TargetName)) + throw new Exception ("Target Name '" + bd.TargetName + "' not found"); + + NodeAddress resolvedNA = null; + foreach (NodeAddress na in Names[bd.TargetName]) { + bool naMatch = true; + for (int i = 0; i < bd.TargetNA.Count; i++) { + if (bd.TargetNA [i] != na [i]) { + naMatch = false; + break; + } + } + if (naMatch) { + resolvedNA = na; + break; + } + } + + if (resolvedNA == null) + throw new Exception ("Target Name '" + bd.TargetName + "' not found"); + + bd.ResolveTargetName (resolvedNA); + } + + /// <summary> + /// Emits cached delegate handler addition in the context of instantiator (ctx) + /// </summary> + public void emitCachedDelegateHandlerAddition(int index, EventInfo evt, NodeAddress address = null){ + il.Emit(OpCodes.Ldloc_0);//load ref to current graphic object + CompilerServices.emitGetInstance (il, address); + il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + il.Emit(OpCodes.Ldfld, CompilerServices.fiCachedDel); + il.Emit(OpCodes.Ldc_I4, index);//load delegate index + il.Emit(OpCodes.Callvirt, CompilerServices.miGetDelegateListItem); + il.Emit(OpCodes.Callvirt, evt.AddMethod);//call add event + } + /// <summary> + /// Emits the handler method addition, done at end of parsing, Loc_0 is root node instance + /// </summary> + /// <param name="bd">Bd.</param> + /// <param name="evt">passed as arg to prevent refetching it for the 3rd time</param> + public void emitHandlerMethodAddition(EventBinding bd){ + //fetch source instance with address for handler addition (as 1st arg of handler.add) + il.Emit (OpCodes.Ldloc_0);//push root + CompilerServices.emitGetInstance (il, bd.SourceNA); + + //load handlerType of sourceEvent to create handler delegate (1st arg) + il.Emit (OpCodes.Ldtoken, bd.SourceEvent.EventHandlerType); + il.Emit (OpCodes.Call, CompilerServices.miGetTypeFromHandle); + //load target the where the method is defined (2nd arg) + il.Emit (OpCodes.Ldloc_0); + CompilerServices.emitGetInstance (il, bd.TargetNA); + //load methodInfo (3rd arg) + il.Emit (OpCodes.Ldstr, bd.TargetMember); + + il.Emit (OpCodes.Callvirt, CompilerServices.miCreateDel); + + il.Emit (OpCodes.Callvirt, bd.SourceEvent.AddMethod);//call add event + } + + } +}
\ No newline at end of file diff --git a/src/GraphicObjects/IBindable.cs b/src/IML/EventBinding.cs index 45ff5864..90a797e0 100644 --- a/src/GraphicObjects/IBindable.cs +++ b/src/IML/EventBinding.cs @@ -1,10 +1,10 @@ // -// IValueChange.cs +// EventBinding.cs // // Author: // Jean-Philippe Bruyère <jp.bruyere@hotmail.com> // -// Copyright (c) 2015 jp +// Copyright (c) 2016 jp // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -19,14 +19,21 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. using System; -using System.Collections.Generic; +using System.Reflection; -namespace Crow +namespace Crow.IML { - public interface IBindable + public class EventBinding : BindingDefinition { - List<Binding> Bindings { get; } - object DataSource { get; set; } + public EventInfo SourceEvent; + + #region CTOR + public EventBinding (NodeAddress _sourceNA, EventInfo _sourceEvt, NodeAddress _targetNA, string _targetMember, string _targetName = "") + : base (_sourceNA, _sourceEvt.Name, _targetNA, _targetMember, _targetName) + { + SourceEvent = _sourceEvt; + } + #endregion } } diff --git a/src/IML/MemberAddress.cs b/src/IML/MemberAddress.cs new file mode 100644 index 00000000..a131927e --- /dev/null +++ b/src/IML/MemberAddress.cs @@ -0,0 +1,110 @@ +// +// MemberAddress.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2016 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +using System; +using System.Linq; +using System.Reflection; + +namespace Crow.IML +{ + /// <summary> + /// Address member of a node + /// </summary> + public struct MemberAddress + { + public string memberName; + public MemberInfo member; + public NodeAddress Address; + + public PropertyInfo Property { get { return member as PropertyInfo; }} + public bool IsTemplateBinding { get { return Address == null ? false : Address.Count == 0; }} +// public string Name { +// get { return memberName; } +// set { memberName = value; } +// } + + public MemberAddress (NodeAddress _address, string _member, bool findMember = true) + { + Address = _address; + memberName = _member; + member = null; + + if (Address == null) + return; + if (Address.Count == 0) + return; + + if (!findMember) + return; + if (!tryFindMember ()) + throw new Exception ("Member Not Found: " + memberName); + } + public MemberAddress (NodeAddress _address, MemberInfo _member) + { + Address = _address; + member = _member; + memberName = ""; + + if (member != null) + memberName = member.Name; + } + + #region Equality Compare + public override bool Equals (object obj) + { + return obj is MemberAddress && this == (MemberAddress)obj; + } + public override int GetHashCode () + { + return Address.GetHashCode () ^ member.GetHashCode (); + } + public static bool operator == (MemberAddress x, MemberAddress y) + { + return x.Address == y.Address && x.memberName == y.memberName; + } + public static bool operator != (MemberAddress x, MemberAddress y) + { + return !(x == y); + } + #endregion + + bool tryFindMember () + { + if (member != null) + throw new Exception ("member already found"); + if (Address == null) + return false; + if (Address.Count == 0) + return false; + Type t = Address.LastOrDefault ().CrowType; + member = t.GetMember (memberName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).FirstOrDefault (); + + #region search for extensions methods if member not found in type + if (member == null && !string.IsNullOrEmpty (memberName)) { + Assembly a = Assembly.GetExecutingAssembly (); + string mn = memberName; + member = CompilerServices.GetExtensionMethods (a, t).Where (em => em.Name == mn).FirstOrDefault (); + } + #endregion + + return member != null; + } + } +} diff --git a/src/IML/Node.cs b/src/IML/Node.cs new file mode 100644 index 00000000..4ba61c03 --- /dev/null +++ b/src/IML/Node.cs @@ -0,0 +1,85 @@ +// +// Node.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2016 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Collections.Generic; + +namespace Crow.IML +{ + /// <summary> + /// IML Node are defined with a type and the index in the parent, + /// </summary> + public struct Node + { + /// <summary> Current node type</summary> + public Type CrowType; + /// <summary> Index in parent, -1 for template</summary> + public int Index; + + public Node (Type crowType, int _index = 0) + { + CrowType = crowType; + Index = _index; + } + + public MethodInfo GetAddMethod(int childIdx){ + if (typeof (Group).IsAssignableFrom (CrowType)) + return CompilerServices.miAddChild; + if (typeof (Container).IsAssignableFrom (CrowType)) + return CompilerServices.miSetChild; + if (typeof (TemplatedContainer).IsAssignableFrom (CrowType)) + return childIdx < 0 ? CompilerServices.miLoadTmp : CompilerServices.piContent.GetSetMethod(); + if (typeof (TemplatedGroup).IsAssignableFrom (CrowType)) + return childIdx < 0 ? CompilerServices.miLoadTmp : CompilerServices.miAddItem; + if (typeof (TemplatedControl).IsAssignableFrom (CrowType)) + return CompilerServices.miLoadTmp; + return null; + } + + #region Equality Compare + public override bool Equals (object obj) + { + if (obj == null) + return false; + Node n = (Node)obj; + return CrowType == n.CrowType && Index == n.Index; + } + public override int GetHashCode () + { + return CrowType.GetHashCode () ^ Index.GetHashCode (); + } + #endregion + + public bool HasTemplate { + get { return typeof (TemplatedControl).IsAssignableFrom (CrowType);} + } + + public static implicit operator string (Node sn) + { + return sn.ToString (); + } + public override string ToString () + { + return string.Format ("{0}.{1}", CrowType.FullName, Index); + } + } +} diff --git a/src/IML/NodeAddress.cs b/src/IML/NodeAddress.cs new file mode 100644 index 00000000..460998da --- /dev/null +++ b/src/IML/NodeAddress.cs @@ -0,0 +1,69 @@ +// +// NodeAddress.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2016 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace Crow.IML +{ + public class NodeAddress : List<Node> + { + public NodeAddress (Node[] nodes) : base(nodes) { + } + + public Type NodeType { get { return Count == 0 ? null : this[this.Count -1].CrowType; }} + + public override bool Equals (object obj) + { + if (obj == null) + return false; + + NodeAddress na = (NodeAddress)obj; + return this.SequenceEqual (na); + } + public override int GetHashCode () + { + unchecked { + int hash = 19; + foreach (Node n in this) + hash = hash * 31 + (n == null ? 0 : n.GetHashCode ()); + return hash; + } + } + + public override string ToString () + { + string tmp = ""; + foreach (Node n in this) + tmp += string.Format ("{0};", n.Index); + return tmp; + } + } + + public class NamedNodeAddress : NodeAddress { + public string Name; + public NamedNodeAddress(string name, Node[] nodes) : base(nodes){ + Name = name; + } + } +} diff --git a/src/IML/NodeStack.cs b/src/IML/NodeStack.cs new file mode 100644 index 00000000..bec45e26 --- /dev/null +++ b/src/IML/NodeStack.cs @@ -0,0 +1,44 @@ +// +// NodeStack.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2016 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +using System; +using System.Collections.Generic; + +namespace Crow.IML +{ + public class NodeStack : Stack<Node> + { + public NodeStack () : base() + { + } + public void IncrementCurrentNodeIndex(){ + Node n = this.Pop(); + this.Push (new Node (n.CrowType, n.Index + 1)); + } + public void DecrementCurrentNodeIndex(){ + Node n = this.Pop(); + this.Push (new Node (n.CrowType, n.Index - 1)); + } + public void ResetCurrentNodeIndex(){ + this.Push (new Node (this.Pop().CrowType)); + } + } +} + diff --git a/src/IML/Reader.cs b/src/IML/Reader.cs new file mode 100644 index 00000000..7a0c8405 --- /dev/null +++ b/src/IML/Reader.cs @@ -0,0 +1,223 @@ +// +// IMLReader.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2016 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +using System; +using System.Xml; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using System.Linq; +using System.Collections.Generic; + +namespace Crow.IML2 +{ + public class Reader : XmlTextReader + { + static List<Type> CrowTypes = new List<Type> (); + + public enum SubNodeType{ + None, + Child, + Children, + Template, + Content, + Items, + ItemTemplate + } + + //public static Node Parse(string str){ + // string[] tmp = str.Trim ().Split ('.'); + // switch (tmp.Length) { + // case 1: + // return new Node ((SubNodeType)int.Parse (tmp [0])); + // case 2: + // return new Node ((SubNodeType)int.Parse (tmp [0]), int.Parse (tmp[1])); + // case 0: + // default: + // return new Node (); + // } + //} + //public static string AddressToString(Node[] address){ + // string tmp = ""; + // foreach (Node n in address) { + // tmp += n.ToString () + ";"; + // } + // return string.IsNullOrEmpty(tmp) ? tmp : tmp.Substring (0, tmp.Length - 1); + //} + //public static Node[] AddressFromString(string address) { + // List<Node> nodes = new List<Node> (); + // string[] tmp = address.Split (';'); + // for (int i = 0; i < tmp.Length; i++) + // nodes.Add (Node.Parse (tmp [i])); + // return nodes.ToArray(); + //} + + + // public class PropertyBinding { + // public string OriginePropertyName = ""; + // public MemberAddress Destination; + // } + + public Type RootType = null; + public Context context; + + InstanciatorInvoker loader = null; + DynamicMethod dm = null; + + ILGenerator il { get { return context.il; }} + + /// <summary> + /// Finalize instatiator MSIL and return LoaderInvoker delegate + /// </summary> + public InstanciatorInvoker GetLoader(){ + if (loader != null) + return loader; + + il.Emit(OpCodes.Ret); + loader = (InstanciatorInvoker)dm.CreateDelegate (typeof(InstanciatorInvoker)); + return loader; + } + + protected int curDepth { + get { return context.nodesStack.Count - 1;} + } + protected Node curNode { + get { return context.nodesStack.Peek(); } + } + //protected Stack<int> curTemplateDepth = new Stack<int>(new int[] {0}); //current template root depth + + #region CTOR + public Reader (string path) + : this(Interface.GetStreamFromPath (path)){ + } + public Reader (Stream stream) + : base(stream) + { + createInstantiator (); + } + /// <summary> + /// Used to parse xmlFrament with same code generator linked + /// If ilGen=null, a new Code Generator will be created. + /// </summary> + public Reader (Context ctx, string xmlFragment) + : base(xmlFragment, XmlNodeType.Element,null){ + + context = ctx; +// +// if (IMLCtx != null) +// return; +// +// createInstantiator(); + } + #endregion + + + void createBindingDelegates(){ +// foreach (Dictionary<string, MemberAddress> pb in IMLCtx.PropertyBindings) { +// +// } + } + + + void registerPropertyBinding(string origNode, string origProp, MemberAddress ma){ + if (!context.PropertyBindings.ContainsKey(origNode)) + context.PropertyBindings [origNode] = new Dictionary<string, MemberAddress> (); + context.PropertyBindings [origNode] [origProp] = ma; + } + void readPropertyBinding(string srcProperty, string expression){ + //if binding exp = '{}' => binding is done on datasource + if (string.IsNullOrEmpty (expression)) { + registerPropertyBinding ("DS", "", new MemberAddress (context.nodesStack.ToArray (), srcProperty)); + return; + } + +// if (expression.StartsWith ("²")) { +// expression = expression.Substring (1); +// TwoWayBinding = true; +// } +// + string [] bindingExp = expression.Split ('/'); + + + if (bindingExp.Length == 1) { + registerPropertyBinding ("DS", bindingExp [0], + new MemberAddress (context.nodesStack.ToArray (), srcProperty)); + return; + } + + string targetName = ""; + string nodeId = ""; + Node[] target = context.nodesStack.ToArray (); + + int nodeIdx = target.Length - 1;//index of target in nodeStack + int ptr = 0;//pointer in bindingExp splitted on '/' + +// //if exp start with '/' => WidgetName.property + if (string.IsNullOrEmpty (bindingExp [0])) { + string[] bindTrg = bindingExp [1].Split ('.'); + if (bindTrg.Length == 1) { + nodeId = Node.AddressToString (target); + targetName = bindTrg [0]; + }else if (bindTrg.Length == 2) { + nodeId = context.Names [bindTrg [0]]; + targetName = bindTrg [1]; + } else + throw new Exception ("Syntax error in binding, expected 'go dot member'"); + } else { + if (bindingExp [0] == ".") { //template binding + //parse nodes up until template node + while (nodeIdx > 0) { + if (target [nodeIdx].Type == SubNodeType.Template) + break; + nodeIdx--; + } + ptr++; + } else { + while (ptr < bindingExp.Length - 1) { + if (bindingExp [ptr] == "..") + nodeIdx--; + else + break; + ptr++; + } + } + Node[] origine = new Node[nodeIdx + 1]; + try { + Array.Copy (target, origine, nodeIdx + 1); + + int destLength = target.Length - nodeIdx; + Node[] dest = new Node[destLength]; + Array.Copy (target, nodeIdx, dest, 0, destLength); + + } catch (Exception ex) { + System.Diagnostics.Debug.WriteLine (ex.ToString ()); + } + + + nodeId = Node.AddressToString (origine); + targetName = bindingExp [ptr]; + } + + registerPropertyBinding (nodeId, targetName, new MemberAddress (context.nodesStack.ToArray (), srcProperty)); + } + + } +} + diff --git a/src/IMLReader.cs b/src/IMLReader.cs deleted file mode 100644 index ad4fd835..00000000 --- a/src/IMLReader.cs +++ /dev/null @@ -1,346 +0,0 @@ -// -// IMLReader.cs -// -// Author: -// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> -// -// Copyright (c) 2016 jp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -using System; -using System.Xml; -using System.IO; -using System.Reflection; -using System.Reflection.Emit; -using System.Linq; -using System.Collections.Generic; - -namespace Crow -{ - public class IMLReader : XmlTextReader - { - InstanciatorInvoker loader = null; - DynamicMethod dm = null; - - public ILGenerator il = null; - public Type RootType = null; - - /// <summary> - /// Finalize instatiator MSIL and return LoaderInvoker delegate - /// </summary> - public InstanciatorInvoker GetLoader(){ - if (loader != null) - return loader; - - il.Emit(OpCodes.Ret); - loader = (InstanciatorInvoker)dm.CreateDelegate (typeof(InstanciatorInvoker)); - return loader; - } - - #region CTOR - public IMLReader (string path) - : this(Interface.GetStreamFromPath (path)){ - } - public IMLReader (Stream stream) - : base(stream) - { - createInstantiator (); - } - /// <summary> - /// Used to parse xmlFrament with same code generator linked - /// If ilGen=null, a new Code Generator will be created. - /// </summary> - public IMLReader (ILGenerator ilGen, string xmlFragment) - : base(xmlFragment, XmlNodeType.Element,null){ - il = ilGen; - - if (il != null) - return; - - createInstantiator(); - } - #endregion - - void createInstantiator(){ - readRootType(); - InitEmitter(); - emitLoader(RootType); - Read();//close tag - } - /// <summary> - /// Inits il generator, RootType must have been read first - /// </summary> - void InitEmitter(){ - - dm = new DynamicMethod("dyn_instantiator", - MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot, - CallingConventions.Standard, - typeof(void),new Type[] {typeof(object), typeof(Interface)}, RootType, true); - - il = dm.GetILGenerator(256); - il.DeclareLocal(typeof(GraphicObject)); - il.Emit(OpCodes.Nop); - //set local GraphicObject to root object passed as 1st argument - il.Emit (OpCodes.Ldarg_0); - il.Emit (OpCodes.Stloc_0); - CompilerServices.emitSetCurInterface (il); - } - void emitLoader(Type crowType){ - string tmpXml = ReadOuterXml (); - - il.Emit (OpCodes.Ldloc_0);//save current go onto the stack if child has to be added - - #region Template and ItemTemplates loading - if (typeof(TemplatedControl).IsAssignableFrom (crowType)) { - //if its a template, first read template elements - using (IMLReader reader = new IMLReader (il, tmpXml)) { - - string templatePath = reader.GetAttribute ("Template"); - //string itemTemplatePath = reader.GetAttribute ("ItemTemplate"); - - List<string[]> itemTemplateIds = new List<string[]> (); - bool inlineTemplate = false; - reader.Read (); - int depth = reader.Depth + 1; - while (reader.Read ()) { - if (!reader.IsStartElement () || reader.Depth > depth) - continue; - if (reader.Name == "Template") { - inlineTemplate = true; - reader.Read (); - - readChildren (reader, crowType,true); - } else if (reader.Name == "ItemTemplate") { - string dataType = "default", datas = "", path = ""; - while (reader.MoveToNextAttribute ()) { - if (reader.Name == "DataType") - dataType = reader.Value; - else if (reader.Name == "Data") - datas = reader.Value; - else if (reader.Name == "Path") - path = reader.Value; - } - reader.MoveToElement (); - - string itemTmpID; - - if (string.IsNullOrEmpty (path)) { - using (IMLReader iTmp = new IMLReader (null, reader.ReadInnerXml ())) { - itemTmpID = Guid.NewGuid ().ToString (); - Interface.Instantiators [itemTmpID] = - new ItemTemplate (iTmp.RootType, iTmp.GetLoader (), dataType, datas); - } - }else{ - if (!reader.IsEmptyElement) - throw new Exception ("ItemTemplate with Path attribute may not include sub nodes"); - itemTmpID = path; - Interface.Instantiators [itemTmpID] = - new ItemTemplate (itemTmpID, dataType, datas); - } - itemTemplateIds.Add (new string[] { dataType, itemTmpID, datas }); - } - } - - if (!inlineTemplate) { - reader.il.Emit (OpCodes.Ldloc_0);//Load this templateControl ref - if (string.IsNullOrEmpty (templatePath)) { - reader.il.Emit (OpCodes.Ldnull);//default template loading - }else{ - reader.il.Emit (OpCodes.Ldarg_1);//load currentInterface - reader.il.Emit (OpCodes.Ldstr, templatePath); //Load template path string - reader.il.Emit (OpCodes.Callvirt,//call Interface.Load(path) - typeof(Interface).GetMethod ("Load", BindingFlags.Instance | BindingFlags.Public)); - } - reader.il.Emit (OpCodes.Callvirt,//load template - crowType.GetMethod ("loadTemplate", BindingFlags.Instance | BindingFlags.NonPublic)); - } - foreach (string[] iTempId in itemTemplateIds) { - reader.il.Emit (OpCodes.Ldloc_0);//load TempControl ref - reader.il.Emit (OpCodes.Ldfld,//load ItemTemplates dic field - typeof(TemplatedGroup).GetField("ItemTemplates")); - reader.il.Emit (OpCodes.Ldstr, iTempId[0]);//load key - reader.il.Emit (OpCodes.Ldstr, iTempId[1]);//load value - reader.il.Emit (OpCodes.Callvirt, - typeof(Interface).GetMethod ("GetItemTemplate")); - reader.il.Emit (OpCodes.Callvirt, - typeof(Dictionary<string, ItemTemplate>).GetMethod ("set_Item", - new Type[] { typeof(string), typeof(ItemTemplate) })); - - if (!string.IsNullOrEmpty (iTempId [2])) { - //expand delegate creation - reader.il.Emit (OpCodes.Ldloc_0);//load TempControl ref - reader.il.Emit (OpCodes.Ldfld,//load ItemTemplates dic field - typeof(TemplatedGroup).GetField ("ItemTemplates")); - reader.il.Emit (OpCodes.Ldstr, iTempId [0]);//load key - reader.il.Emit (OpCodes.Callvirt, - typeof(Dictionary<string, ItemTemplate>).GetMethod ("get_Item", - new Type[] { typeof(string) })); - reader.il.Emit (OpCodes.Ldloc_0);//load root of treeView - reader.il.Emit (OpCodes.Callvirt, - typeof(ItemTemplate).GetMethod ("CreateExpandDelegate")); - } - } - } - } - #endregion - - using (IMLReader reader = new IMLReader(il,tmpXml)){ - reader.Read (); - - #region Styling and default values loading - if (reader.HasAttributes) { - string style = reader.GetAttribute ("Style"); - if (!string.IsNullOrEmpty (style)) { - PropertyInfo pi = crowType.GetProperty ("Style"); - CompilerServices.EmitSetValue (reader.il, pi, style); - } - } - reader.il.Emit (OpCodes.Ldloc_0); - reader.il.Emit (OpCodes.Callvirt, typeof(GraphicObject).GetMethod ("loadDefaultValues")); - #endregion - - #region Attributes reading - if (reader.HasAttributes) { - - while (reader.MoveToNextAttribute ()) { - if (reader.Name == "Style") - continue; - - MemberInfo mi = crowType.GetMember (reader.Name).FirstOrDefault(); - if (mi == null) - throw new Exception ("Member '" + reader.Name + "' not found in " + crowType.Name); - if (mi.MemberType == MemberTypes.Event) { - CompilerServices.emitBindingCreation (reader.il, reader.Name, reader.Value); - continue; - } - PropertyInfo pi = mi as PropertyInfo; - if (pi == null) - throw new Exception ("Member '" + reader.Name + "' not found in " + crowType.Name); - - if (reader.Value.StartsWith ("{")) { - CompilerServices.emitBindingCreation (reader.il, reader.Name, reader.Value.Substring (1, reader.Value.Length - 2)); - }else - CompilerServices.EmitSetValue (reader.il, pi, reader.Value); - - } - reader.MoveToElement (); - } - #endregion - - if (reader.IsEmptyElement) { - reader.il.Emit (OpCodes.Pop);//pop saved ref to current object - return; - } - - readChildren (reader, crowType); - } - il.Emit (OpCodes.Pop);//pop saved ref to current object - } - /// <summary> - /// Parse child node an generate corresponding msil - /// </summary> - void readChildren(IMLReader reader, Type crowType, bool templateLoading = false){ - MethodInfo miAddChild = null; - bool endTagReached = false; - while (reader.Read()){ - switch (reader.NodeType) { - case XmlNodeType.EndElement: - endTagReached = true; - break; - case XmlNodeType.Element: - //Templates - if (reader.Name == "Template" || - reader.Name == "ItemTemplate") { - reader.Skip (); - continue; - } - - if (miAddChild == null) { - if (typeof(Group).IsAssignableFrom (crowType)) - miAddChild = typeof(Group).GetMethod ("AddChild"); - else if (typeof(Container).IsAssignableFrom (crowType)) - miAddChild = typeof(Container).GetMethod ("SetChild"); - else if (typeof(TemplatedContainer).IsAssignableFrom (crowType) && !templateLoading) - miAddChild = typeof(TemplatedContainer).GetProperty ("Content").GetSetMethod (); - else if (typeof(TemplatedGroup).IsAssignableFrom (crowType) && !templateLoading) - miAddChild = typeof(TemplatedGroup).GetMethod ("AddItem", - BindingFlags.Instance | BindingFlags.Public); - else if (typeof(TemplatedControl).IsAssignableFrom (crowType)) - miAddChild = typeof(TemplatedControl).GetMethod ("loadTemplate", - BindingFlags.Instance | BindingFlags.NonPublic); - else if (typeof(PrivateContainer).IsAssignableFrom (crowType)) - miAddChild = typeof(PrivateContainer).GetMethod ("SetChild", - BindingFlags.Instance | BindingFlags.NonPublic); - } - //push current instance on stack for parenting - //loc_0 will be used for child - reader.il.Emit (OpCodes.Ldloc_0); - - Type t = Type.GetType ("Crow." + reader.Name); - if (t == null) { - Assembly a = Assembly.GetEntryAssembly (); - foreach (Type expT in a.GetExportedTypes ()) { - if (expT.Name == reader.Name) - t = expT; - } - } - if (t == null) - throw new Exception (reader.Name + " type not found"); - - reader.il.Emit (OpCodes.Newobj, t.GetConstructors () [0]);//TODO:search parameterless ctor - reader.il.Emit (OpCodes.Stloc_0);//child is now loc_0 - CompilerServices.emitSetCurInterface (il); - - reader.emitLoader (t); - - reader.il.Emit (OpCodes.Ldloc_0);//load child on stack for parenting - if (miAddChild == null) - System.Diagnostics.Debugger.Break (); - reader.il.Emit (OpCodes.Callvirt, miAddChild); - reader.il.Emit (OpCodes.Stloc_0); //reset local to current go - reader.il.Emit (OpCodes.Ldloc_0);//save current go onto the stack if child has to be added - break; - } - if (endTagReached) - break; - } - } - /// <summary> - /// read first node to set GraphicObject class for loading - /// and let reader position on that node - /// </summary> - Type readRootType () - { - string root = "Object"; - while (Read()) { - if (NodeType == XmlNodeType.Element) { - root = this.Name; - break; - } - } - - Type t = Type.GetType ("Crow." + root); - if (t == null) { - Assembly a = Assembly.GetEntryAssembly (); - foreach (Type expT in a.GetExportedTypes ()) { - if (expT.Name == root) - t = expT; - } - } - RootType = t; - return t; - } - } -} - diff --git a/src/Instantiator.cs b/src/Instantiator.cs index 1a8cdb75..6aaa7483 100644 --- a/src/Instantiator.cs +++ b/src/Instantiator.cs @@ -23,85 +23,970 @@ using System.Threading; using System.IO; using System.Text; using System.Diagnostics; +using System.Xml; +using System.Reflection.Emit; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +using Crow.IML; namespace Crow { - public delegate void InstanciatorInvoker(object instance, Interface iface); + public delegate object InstanciatorInvoker(Interface iface); + /// <summary> + /// Instantiator + /// </summary> public class Instantiator { + #region Dynamic Method ID generation + static long curId = 0; + internal static long NewId { + get { return curId++; } + } + #endregion + public Type RootType; InstanciatorInvoker loader; - string imlPath; + internal string sourcePath; #region CTOR - public Instantiator (string path){ - imlPath = path; - - #if DEBUG_LOAD + public Instantiator (string path) : this (Interface.GetStreamFromPath(path)) { + sourcePath = path; + } + public Instantiator (Stream stream) + { +#if DEBUG_LOAD Stopwatch loadingTime = new Stopwatch (); loadingTime.Start (); - #endif - try { - using (IMLReader itr = new IMLReader (path)){ - loader = itr.GetLoader (); - RootType = itr.RootType; - } - } catch (Exception ex) { - throw new Exception ("Error loading <" + path + ">:\n", ex); +#endif + using (XmlTextReader itr = new XmlTextReader (stream)) { + parseIML (itr); } - - #if DEBUG_LOAD +#if DEBUG_LOAD loadingTime.Stop (); Debug.WriteLine ("IML Instantiator creation '{2}' : {0} ticks, {1} ms", - loadingTime.ElapsedTicks, loadingTime.ElapsedMilliseconds, path); - #endif + loadingTime.ElapsedTicks, loadingTime.ElapsedMilliseconds, imlPath); +#endif + } + public Instantiator (Type _root, InstanciatorInvoker _loader) + { + RootType = _root; + loader = _loader; } - public static Instantiator CreateFromImlFragment(string fragment){ + public static Instantiator CreateFromImlFragment (string fragment) + { try { - using (Stream s = new MemoryStream(Encoding.UTF8.GetBytes(fragment))){ - return new Instantiator(s); + using (Stream s = new MemoryStream (Encoding.UTF8.GetBytes (fragment))) { + return new Instantiator (s); } } catch (Exception ex) { throw new Exception ("Error loading fragment:\n" + fragment + "\n", ex); } } - public Instantiator (Stream stream){ - #if DEBUG_LOAD - Stopwatch loadingTime = new Stopwatch (); - loadingTime.Start (); - #endif - using (IMLReader itr = new IMLReader (stream)){ - loader = itr.GetLoader (); - RootType = itr.RootType; + #endregion + + public GraphicObject CreateInstance(Interface iface){ + return loader (iface) as GraphicObject; + } + + List<DynamicMethod> dsValueChangedDynMeths = new List<DynamicMethod>(); + List<Delegate> cachedDelegates = new List<Delegate>(); + List<int> templateCachedDelegateIndices = new List<int>();//store indices of template delegate to be handled by root parentChanged event + Delegate templateBinding; + + #region IML parsing + /// <summary> + /// Parses IML and build a dynamic method that will be used to instanciate one or multiple occurence of the IML file or fragment + /// </summary> + void parseIML (XmlTextReader reader) { + Context ctx = new Context (findRootType (reader)); + + ctx.nodesStack.Push (new Node (ctx.RootType)); + emitLoader (reader, ctx); + ctx.nodesStack.Pop (); + + foreach (int idx in templateCachedDelegateIndices) + ctx.emitCachedDelegateHandlerAddition(idx, CompilerServices.eiLogicalParentChanged); + + ctx.ResolveNamedTargets (); + + emitBindingDelegates (ctx); + + ctx.il.Emit (OpCodes.Ldloc_0);//load root obj to return + ctx.il.Emit(OpCodes.Ret); + + reader.Read ();//close tag + RootType = ctx.RootType; + loader = (InstanciatorInvoker)ctx.dm.CreateDelegate (typeof (InstanciatorInvoker), this); + } + /// <summary> + /// read first node to set GraphicObject class for loading + /// and let reader position on that node + /// </summary> + Type findRootType (XmlTextReader reader) + { + string root = "Object"; + while (reader.Read ()) { + if (reader.NodeType == XmlNodeType.Element) { + root = reader.Name; + break; + } } - #if DEBUG_LOAD - loadingTime.Stop (); - Debug.WriteLine ("IML Instantiator creation '{2}' : {0} ticks, {1} ms", - loadingTime.ElapsedTicks, loadingTime.ElapsedMilliseconds, imlPath); + + Type t = Type.GetType ("Crow." + root); + if (t == null) { + Assembly a = Assembly.GetEntryAssembly (); + foreach (Type expT in a.GetExportedTypes ()) { + if (expT.Name == root) + t = expT; + } + } + return t; + } + void emitLoader (XmlTextReader reader, Context ctx) + { + string tmpXml = reader.ReadOuterXml (); + + if (ctx.nodesStack.Peek().HasTemplate) + emitTemplateLoad (ctx, tmpXml); + + emitGOLoad (ctx, tmpXml); + + //emitCheckAndBindValueChanged (ctx); + } + void emitTemplateLoad (Context ctx, string tmpXml) { + //if its a template, first read template elements + using (XmlTextReader reader = new XmlTextReader (tmpXml, XmlNodeType.Element, null)) { + List<string[]> itemTemplateIds = new List<string[]> (); + bool inlineTemplate = false; + + string templatePath = reader.GetAttribute ("Template"); + + reader.Read (); + int depth = reader.Depth + 1; + while (reader.Read ()) { + if (!reader.IsStartElement () || reader.Depth > depth) + continue; + if (reader.Name == "Template") { + inlineTemplate = true; + reader.Read (); + readChildren (reader, ctx, -1); + } else if (reader.Name == "ItemTemplate") { + string dataType = "default", datas = "", path = ""; + while (reader.MoveToNextAttribute ()) { + if (reader.Name == "DataType") + dataType = reader.Value; + else if (reader.Name == "Data") + datas = reader.Value; + else if (reader.Name == "Path") + path = reader.Value; + } + reader.MoveToElement (); + + string itemTmpID; + + if (string.IsNullOrEmpty (path)) { + itemTmpID = Guid.NewGuid ().ToString (); + Interface.Instantiators [itemTmpID] = + new ItemTemplate (new MemoryStream (Encoding.UTF8.GetBytes (reader.ReadInnerXml ())), dataType, datas); + + } else { + if (!reader.IsEmptyElement) + throw new Exception ("ItemTemplate with Path attribute may not include sub nodes"); + itemTmpID = path; + Interface.Instantiators [itemTmpID] = + new ItemTemplate (Interface.GetStreamFromPath (itemTmpID), dataType, datas); + } + itemTemplateIds.Add (new string [] { dataType, itemTmpID, datas }); + } + } + + if (!inlineTemplate) {//load from path or default template + ctx.il.Emit (OpCodes.Ldloc_0);//Load current templatedControl ref + if (string.IsNullOrEmpty (templatePath)) { + ctx.il.Emit (OpCodes.Ldnull);//default template loading + } else { + ctx.il.Emit (OpCodes.Ldarg_1);//load currentInterface + ctx.il.Emit (OpCodes.Ldstr, templatePath); //Load template path string + ctx.il.Emit (OpCodes.Callvirt,//call Interface.Load(path) + CompilerServices.miIFaceLoad); + } + ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miLoadTmp);//load template + } + //copy item templates (review this) + foreach (string [] iTempId in itemTemplateIds) { + ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref + ctx.il.Emit (OpCodes.Ldfld, CompilerServices.fldItemTemplates);//load ItemTemplates dic field + ctx.il.Emit (OpCodes.Ldstr, iTempId [0]);//load key + ctx.il.Emit (OpCodes.Ldstr, iTempId [1]);//load value + ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miGetITemp); + ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miAddITemp); + + if (!string.IsNullOrEmpty (iTempId [2])) { + //expand delegate creation + ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref + ctx.il.Emit (OpCodes.Ldfld, CompilerServices.fldItemTemplates); + ctx.il.Emit (OpCodes.Ldstr, iTempId [0]);//load key + ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miGetITempFromDic); + ctx.il.Emit (OpCodes.Ldloc_0);//load root of treeView + ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miCreateExpDel); + } + } + } + } + void emitGOLoad (Context ctx, string tmpXml) { + using (XmlTextReader reader = new XmlTextReader (tmpXml, XmlNodeType.Element, null)) { + reader.Read (); + + #region Styling and default values loading + if (reader.HasAttributes) { + string style = reader.GetAttribute ("Style"); + if (!string.IsNullOrEmpty (style)) + CompilerServices.EmitSetValue (ctx.il, CompilerServices.piStyle, style); + } + ctx.il.Emit (OpCodes.Ldloc_0); + ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miLoadDefaultVals); + #endregion + + #region Attributes reading + if (reader.HasAttributes) { + + while (reader.MoveToNextAttribute ()) { + if (reader.Name == "Style") + continue; + + MemberInfo mi = ctx.CurrentNodeType.GetMember (reader.Name).FirstOrDefault (); + if (mi == null) + throw new Exception ("Member '" + reader.Name + "' not found in " + ctx.CurrentNodeType.Name); + + if (mi.MemberType == MemberTypes.Event) { + foreach (string exp in CompilerServices.splitOnSemiColumnOutsideAccolades(reader.Value)) { + string trimed = exp.Trim(); + if (trimed.StartsWith ("{", StringComparison.OrdinalIgnoreCase)) + compileAndStoreDynHandler (ctx, mi as EventInfo, trimed.Substring (1, trimed.Length - 2)); + else + emitHandlerBinding (ctx, mi as EventInfo, trimed); + } + + continue; + } + PropertyInfo pi = mi as PropertyInfo; + if (pi == null) + throw new Exception ("Member '" + reader.Name + "' is not a property in " + ctx.CurrentNodeType.Name); + + if (pi.Name == "Name") + ctx.StoreCurrentName (reader.Value); + + if (reader.Value.StartsWith ("{", StringComparison.OrdinalIgnoreCase)) + readPropertyBinding (ctx, reader.Name, reader.Value.Substring (1, reader.Value.Length - 2)); + else + CompilerServices.EmitSetValue (ctx.il, pi, reader.Value); + + } + reader.MoveToElement (); + } + #endregion + + readChildren (reader, ctx); + + ctx.nodesStack.ResetCurrentNodeIndex (); + } + } + + /// <summary> + /// Parse child node an generate corresponding msil + /// </summary> + void readChildren (XmlTextReader reader, Context ctx, int startingIdx = 0) + { + bool endTagReached = false; + int nodeIdx = startingIdx; + while (reader.Read ()) { + switch (reader.NodeType) { + case XmlNodeType.EndElement: + endTagReached = true; + break; + case XmlNodeType.Element: + //skip Templates + if (reader.Name == "Template" || + reader.Name == "ItemTemplate") { + reader.Skip (); + continue; + } + + //push 2x current instance on stack for parenting and reseting loc0 to parent + //loc_0 will be used for child + ctx.il.Emit (OpCodes.Ldloc_0); + ctx.il.Emit (OpCodes.Ldloc_0); + + Type t = Type.GetType ("Crow." + reader.Name); + if (t == null) { + Assembly a = Assembly.GetEntryAssembly (); + foreach (Type expT in a.GetExportedTypes ()) { + if (expT.Name == reader.Name) + t = expT; + } + } + if (t == null) + throw new Exception (reader.Name + " type not found"); + + ctx.il.Emit (OpCodes.Newobj, t.GetConstructors () [0]);//TODO:search parameterless ctor + ctx.il.Emit (OpCodes.Stloc_0);//child is now loc_0 + CompilerServices.emitSetCurInterface (ctx.il); + + ctx.nodesStack.Push (new Node (t, nodeIdx)); + emitLoader (reader, ctx); + ctx.nodesStack.Pop (); + + ctx.il.Emit (OpCodes.Ldloc_0);//load child on stack for parenting + ctx.il.Emit (OpCodes.Callvirt, ctx.nodesStack.Peek().GetAddMethod(nodeIdx)); + ctx.il.Emit (OpCodes.Stloc_0); //reset local to current go + + nodeIdx++; + + break; + } + if (endTagReached) + break; + } + } + #endregion + + void readPropertyBinding (Context ctx, string sourceMember, string expression) + { + NodeAddress sourceNA = ctx.CurrentNodeAddress; + BindingDefinition bindingDef = splitBindingExp (sourceNA, sourceMember, expression); + + #if DEBUG_BINDING + Debug.WriteLine("Property Binding: " + bindingDef.ToString()); #endif + + if (bindingDef.IsDataSourceBinding)//bind on data source + emitDataSourceBindings (ctx, bindingDef); + else + ctx.StorePropertyBinding (bindingDef); } - public Instantiator (Type _root, InstanciatorInvoker _loader) + + /// <summary> + /// Splits the binding expression + /// </summary> + /// <returns><c>true</c>, if it's a two way binding, <c>false</c> otherwise.</returns> + /// <param name="sourceNA">Source Node address</param> + /// <param name="expression">Expression.</param> + /// <param name="targetNA">Target Node Address</param> + /// <param name="targetMember">Target member name</param> + /// <param name="targetName">Target node name</param> + BindingDefinition splitBindingExp(NodeAddress sourceNA, string sourceMember, string expression){ + BindingDefinition bindingDef = new BindingDefinition(sourceNA, sourceMember); + if (string.IsNullOrEmpty (expression)) { + return bindingDef; + } else { + if (expression.StartsWith ("²")) { + bindingDef.TwoWay = true; + expression = expression.Substring (1); + } + string[] bindingExp = expression.Split ('/'); + + if (bindingExp.Length > 1) + bindingDef.TargetNA = CompilerServices.getNodeAdressFromBindingExp (sourceNA, bindingExp); + + string [] bindTrg = bindingExp.Last().Split ('.'); + + if (bindTrg.Length == 1) + bindingDef.TargetMember = bindTrg [0]; + else if (bindTrg.Length == 2) { + //named target + bindingDef.TargetName = bindTrg[0]; + bindingDef.TargetMember = bindTrg [1]; + } else + throw new Exception ("Syntax error in binding, expected 'go dot member'"); + } + + return bindingDef; + } + + #region Emit Helper + void dataSourceChangedEmitHelper(object dscSource, object dataSource, int dynMethIdx){ + if (dataSource is IValueChange) + (dataSource as IValueChange).ValueChanged += + (EventHandler<ValueChangeEventArgs>)dsValueChangedDynMeths [dynMethIdx].CreateDelegate (typeof(EventHandler<ValueChangeEventArgs>), dscSource); + } + #endregion + + #region Event Bindings + /// <summary> + /// Compile events expression in IML attributes, and store the result in the instanciator + /// Those handlers will be bound when instatiing + /// </summary> + void compileAndStoreDynHandler (Context ctx, EventInfo sourceEvent, string expression) { - RootType = _root; - loader = _loader; + //store event handler dynamic method in instanciator + int dmIdx = cachedDelegates.Count; + cachedDelegates.Add (CompilerServices.compileDynEventHandler (sourceEvent, expression, ctx.CurrentNodeAddress)); + ctx.emitCachedDelegateHandlerAddition(dmIdx, sourceEvent); + } + /// <summary> Emits handler method bindings </summary> + void emitHandlerBinding (Context ctx, EventInfo sourceEvent, string expression){ + NodeAddress currentNode = ctx.CurrentNodeAddress; + BindingDefinition bindingDef = splitBindingExp (currentNode, sourceEvent.Name, expression); + + #if DEBUG_BINDING + Debug.WriteLine("Event Binding: " + bindingDef.ToString()); + #endif + + if (bindingDef.IsTemplateBinding | bindingDef.IsDataSourceBinding) { + //we need to bind datasource method to source event + DynamicMethod dm = new DynamicMethod ("dyn_dschangedForHandler" + NewId, + typeof(void), + CompilerServices.argsBoundDSChange, true); + + ILGenerator il = dm.GetILGenerator (256); + System.Reflection.Emit.Label cancel = il.DefineLabel (); + + il.DeclareLocal (typeof(MethodInfo));//used to cancel binding if method doesn't exist + + il.Emit (OpCodes.Nop); + + emitRemoveOldDataSourceHandler (il, sourceEvent.Name, bindingDef.TargetMember, false); + + //fetch method in datasource and test if it exist + il.Emit (OpCodes.Ldarg_2);//load new datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Brfalse, cancel);//cancel if new datasource is null + il.Emit (OpCodes.Ldarg_2);//load new datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//load handler method name + il.Emit (OpCodes.Call, CompilerServices.miGetMethInfoWithRefx); + il.Emit (OpCodes.Stloc_0);//save MethodInfo + il.Emit (OpCodes.Ldloc_0);//push mi for test if null + + il.Emit (OpCodes.Brfalse, cancel); + + il.Emit (OpCodes.Ldarg_1);//load datasource change source where the event is as 1st arg of handler.add + if (bindingDef.IsTemplateBinding)//fetch source instance with address + CompilerServices.emitGetInstance (il, bindingDef.SourceNA); + + //load handlerType of sourceEvent to create delegate (1st arg) + il.Emit (OpCodes.Ldtoken, sourceEvent.EventHandlerType); + il.Emit (OpCodes.Call, CompilerServices.miGetTypeFromHandle); + il.Emit (OpCodes.Ldarg_2);//load new datasource where the method is defined + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Ldloc_0);//load methodInfo (3rd arg) + + il.Emit (OpCodes.Callvirt, CompilerServices.miCreateBoundDel); + il.Emit (OpCodes.Callvirt, sourceEvent.AddMethod);//call add event + + System.Reflection.Emit.Label finish = il.DefineLabel (); + il.Emit (OpCodes.Br, finish); + il.MarkLabel (cancel); + il.EmitWriteLine (string.Format ("Handler method '{0}' for '{1}' not found in new dataSource ", bindingDef.TargetMember, sourceEvent.Name)); + il.MarkLabel (finish); + il.Emit (OpCodes.Ret); + + //store dschange delegate in instatiator instance for access while instancing graphic object + int delDSIndex = cachedDelegates.Count; + cachedDelegates.Add (dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); + + if (bindingDef.IsDataSourceBinding) + ctx.emitCachedDelegateHandlerAddition (delDSIndex, CompilerServices.eiDSChange); + else //template handler binding, will be added to root parentChanged + templateCachedDelegateIndices.Add (delDSIndex); + } else {//normal in tree handler binding, store until tree is complete (end of parse) + ctx.UnresolvedTargets.Add (new EventBinding ( + bindingDef.SourceNA, sourceEvent, + bindingDef.TargetNA, bindingDef.TargetMember, bindingDef.TargetName)); + } } #endregion - public GraphicObject CreateInstance(Interface iface){ - GraphicObject tmp = (GraphicObject)Activator.CreateInstance(RootType); - loader (tmp, iface); - return tmp; + #region Property Bindings + /// <summary> + /// Create and store in the instanciator the ValueChanged delegates + /// those delegates uses grtree functions to set destination value so they don't + /// need to be bound to destination instance as in the ancient system. + /// </summary> + void emitBindingDelegates(Context ctx){ + foreach (KeyValuePair<NodeAddress,Dictionary<string, List<MemberAddress>>> bindings in ctx.Bindings ) { + if (bindings.Key.Count == 0)//template binding + emitTemplateBindings (ctx, bindings.Value); + else + emitPropertyBindings (ctx, bindings.Key, bindings.Value); + } } - public string GetImlSourcesCode(){ - try { - using (StreamReader sr = new StreamReader (imlPath)) - return sr.ReadToEnd(); - } catch (Exception ex) { - throw new Exception ("Error getting sources for <" + imlPath + ">:", ex); + void emitPropertyBindings(Context ctx, NodeAddress origine, Dictionary<string, List<MemberAddress>> bindings){ + Type origineNodeType = origine.NodeType; + + //value changed dyn method + DynamicMethod dm = new DynamicMethod ("dyn_valueChanged" + NewId, + typeof (void), CompilerServices.argsValueChange, true); + ILGenerator il = dm.GetILGenerator (256); + + System.Reflection.Emit.Label endMethod = il.DefineLabel (); + + il.DeclareLocal (CompilerServices.TObject); + + il.Emit (OpCodes.Nop); + + int i = 0; + foreach (KeyValuePair<string, List<MemberAddress>> bindingCase in bindings ) { + + System.Reflection.Emit.Label nextTest = il.DefineLabel (); + + #region member name test + //load source member name + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); + + il.Emit (OpCodes.Ldstr, bindingCase.Key);//load name to test + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Callvirt, CompilerServices.stringEquals); + il.Emit (OpCodes.Brfalse, nextTest);//if not equal, jump to next case + #endregion + + #region destination member affectations + PropertyInfo piOrig = origineNodeType.GetProperty (bindingCase.Key); + Type origineType = null; + if (piOrig != null) + origineType = piOrig.PropertyType; + foreach (MemberAddress ma in bindingCase.Value) { + //first we have to load destination instance onto the stack, it is access + //with graphic tree functions deducted from nodes topology + il.Emit (OpCodes.Ldarg_0);//load source instance of ValueChanged event + + NodeAddress destination = ma.Address; + + if (destination.Count == 0){//template reverse binding + //fetch destination instance (which is the template root) + for (int j = 0; j < origine.Count ; j++) + il.Emit(OpCodes.Callvirt, CompilerServices.miGetLogicalParent); + }else + CompilerServices.emitGetInstance (il, origine, destination); + + if (origineType != null && destination.Count > 0){//else, prop less binding or reverse template bind, no init requiered + //for initialisation dynmeth, push destination instance loc_0 is root node in ctx + ctx.il.Emit(OpCodes.Ldloc_0); + CompilerServices.emitGetInstance (ctx.il, destination); + + //init dynmeth: load actual value from origine + ctx.il.Emit (OpCodes.Ldloc_0); + CompilerServices.emitGetInstance (ctx.il, origine); + ctx.il.Emit (OpCodes.Callvirt, origineNodeType.GetProperty (bindingCase.Key).GetGetMethod()); + } + //load new value + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); + + if (origineType == null)//property less binding, no init + CompilerServices.emitConvert (il, ma.Property.PropertyType); + else if (destination.Count > 0) { + if (origineType.IsValueType) + ctx.il.Emit(OpCodes.Box, origineType); + + CompilerServices.emitConvert (ctx.il, origineType, ma.Property.PropertyType); + CompilerServices.emitConvert (il, origineType, ma.Property.PropertyType); + + ctx.il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod());//set init value + } else {// reverse templateBinding + il.Emit (OpCodes.Ldstr, ma.memberName);//arg 3 of setValueWithReflexion + il.Emit (OpCodes.Call, CompilerServices.miSetValWithRefx); + continue; + } + il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod());//set value on value changes + } + #endregion + il.Emit (OpCodes.Br, endMethod); + il.MarkLabel (nextTest); + + i++; + } + + il.MarkLabel (endMethod); + il.Emit (OpCodes.Ret); + + //store and emit Add in ctx + int dmIdx = cachedDelegates.Count; + cachedDelegates.Add (dm.CreateDelegate (typeof(EventHandler<ValueChangeEventArgs>))); + ctx.emitCachedDelegateHandlerAddition (dmIdx, CompilerServices.eiValueChange, origine); + } + void emitTemplateBindings(Context ctx, Dictionary<string, List<MemberAddress>> bindings){ + //value changed dyn method + DynamicMethod dm = new DynamicMethod ("dyn_tmpValueChanged", + typeof (void), CompilerServices.argsValueChange, true); + ILGenerator il = dm.GetILGenerator (256); + + //create parentchanged dyn meth in parallel to have only one loop over bindings + DynamicMethod dmPC = new DynamicMethod ("dyn_InitAndLogicalParentChanged", + typeof (void), + CompilerServices.argsBoundDSChange, true); + ILGenerator ilPC = dmPC.GetILGenerator (256); + + il.Emit (OpCodes.Nop); + ilPC.Emit (OpCodes.Nop); + + System.Reflection.Emit.Label endMethod = il.DefineLabel (); + + il.DeclareLocal (CompilerServices.TObject); + ilPC.DeclareLocal (CompilerServices.TObject);//used for checking propery less bindings + ilPC.DeclareLocal (typeof(MemberInfo));//used for checking propery less bindings + + System.Reflection.Emit.Label cancel = ilPC.DefineLabel (); + + #region Unregister previous parent event handler + //unregister previous parent handler if not null + ilPC.Emit (OpCodes.Ldarg_2);//load old parent + ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); + ilPC.Emit (OpCodes.Brfalse, cancel);//old parent is null + + ilPC.Emit (OpCodes.Ldarg_2);//load old parent + ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); + //Load cached delegate + ilPC.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + ilPC.Emit(OpCodes.Ldfld, CompilerServices.fiTemplateBinding); + + //add template bindings dynValueChanged delegate to new parent event + ilPC.Emit(OpCodes.Callvirt, CompilerServices.eiValueChange.RemoveMethod);//call remove event + #endregion + + ilPC.MarkLabel(cancel); + + #region check if new parent is null + cancel = ilPC.DefineLabel (); + ilPC.Emit (OpCodes.Ldarg_2);//load datasource change arg + ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + ilPC.Emit (OpCodes.Brfalse, cancel);//new ds is null + #endregion + + int i = 0; + foreach (KeyValuePair<string, List<MemberAddress>> bindingCase in bindings ) { + + System.Reflection.Emit.Label nextTest = il.DefineLabel (); + + #region member name test + //load source member name + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); + + il.Emit (OpCodes.Ldstr, bindingCase.Key);//load name to test + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Callvirt, CompilerServices.stringEquals); + il.Emit (OpCodes.Brfalse, nextTest);//if not equal, jump to next case + #endregion + + #region destination member affectations + + foreach (MemberAddress ma in bindingCase.Value) { + if (ma.Address.Count == 0){ + Debug.WriteLine("\t\tBUG: reverse template binding in normal template binding"); + continue;//template binding + } + //first we try to get memberInfo of new parent, if it doesn't exist, it's a propery less binding + ilPC.Emit (OpCodes.Ldarg_2);//load new parent onto the stack for handler addition + ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + ilPC.Emit (OpCodes.Stloc_0);//save new parent + //get parent type + ilPC.Emit (OpCodes.Ldloc_0);//push parent instance + ilPC.Emit (OpCodes.Ldstr, bindingCase.Key);//load member name + ilPC.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); + ilPC.Emit (OpCodes.Stloc_1);//save memberInfo + ilPC.Emit (OpCodes.Ldloc_1);//push mi for test if null + System.Reflection.Emit.Label propLessReturn = ilPC.DefineLabel (); + ilPC.Emit (OpCodes.Brfalse, propLessReturn); + + + //first we have to load destination instance onto the stack, it is access + //with graphic tree functions deducted from nodes topology + il.Emit (OpCodes.Ldarg_0);//load source instance of ValueChanged event + CompilerServices.emitGetChild (il, typeof(TemplatedControl), -1); + CompilerServices.emitGetInstance (il, ma.Address); + + ilPC.Emit (OpCodes.Ldarg_2);//load destination instance to set actual value of member + ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + CompilerServices.emitGetChild (ilPC, typeof(TemplatedControl), -1); + CompilerServices.emitGetInstance (ilPC, ma.Address); + + //load new value + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); + + //for the parent changed dyn meth we need to fetch actual value for initialisation thrue reflexion + ilPC.Emit (OpCodes.Ldloc_0);//push root instance of instanciator as parentChanged source + ilPC.Emit (OpCodes.Ldloc_1);//push mi for value fetching + ilPC.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); + + CompilerServices.emitConvert (il, ma.Property.PropertyType); + CompilerServices.emitConvert (ilPC, ma.Property.PropertyType); + + il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); + ilPC.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); + + ilPC.MarkLabel(propLessReturn); + } + #endregion + il.Emit (OpCodes.Br, endMethod); + il.MarkLabel (nextTest); + + i++; } + //il.Emit (OpCodes.Pop); + il.MarkLabel (endMethod); + il.Emit (OpCodes.Ret); + + //store template bindings in instanciator + templateBinding = dm.CreateDelegate (typeof(EventHandler<ValueChangeEventArgs>)); + + #region emit LogicalParentChanged method + + //load new parent onto the stack for handler addition + ilPC.Emit (OpCodes.Ldarg_2); + ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + + //Load cached delegate + ilPC.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + ilPC.Emit(OpCodes.Ldfld, CompilerServices.fiTemplateBinding); + + //add template bindings dynValueChanged delegate to new parent event + ilPC.Emit(OpCodes.Callvirt, CompilerServices.eiValueChange.AddMethod);//call add event + + ilPC.MarkLabel (cancel); + ilPC.Emit (OpCodes.Ret); + + //store dschange delegate in instatiator instance for access while instancing graphic object + int delDSIndex = cachedDelegates.Count; + cachedDelegates.Add(dmPC.CreateDelegate (CompilerServices.ehTypeDSChange, this)); + #endregion + + ctx.emitCachedDelegateHandlerAddition(delDSIndex, CompilerServices.eiLogicalParentChanged); + } + /// <summary> + /// create the valuechanged handler, the datasourcechanged handler and emit event handling + /// </summary> + void emitDataSourceBindings(Context ctx, BindingDefinition bindingDef){ + DynamicMethod dm = null; + ILGenerator il = null; + int dmVC = 0; + PropertyInfo piSource = ctx.CurrentNodeType.GetProperty(bindingDef.SourceMember); + //if no dataSource member name is provided, valuechange is not handle and datasource change + //will be used as origine value + string delName = "dyn_DSvalueChanged" + NewId; + if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ + #region create valuechanged method + dm = new DynamicMethod (delName, + typeof (void), + CompilerServices.argsBoundValueChange, true); + + il = dm.GetILGenerator (256); + + System.Reflection.Emit.Label endMethod = il.DefineLabel (); + + il.DeclareLocal (CompilerServices.TObject); + + il.Emit (OpCodes.Nop); + + //load value changed member name onto the stack + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); + + //test if it's the expected one + il.Emit (OpCodes.Ldstr, bindingDef.TargetMember); + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Callvirt, CompilerServices.stringEquals); + il.Emit (OpCodes.Brfalse, endMethod); + //set destination member with valueChanged new value + //load destination ref + il.Emit (OpCodes.Ldarg_0); + //load new value onto the stack + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); + + //by default, source value type is deducted from target member type to allow + //memberless binding, if targetMember exists, it will be used to determine target + //value type for conversion + + CompilerServices.emitConvert (il, piSource.PropertyType); + + il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); + + il.MarkLabel (endMethod); + il.Emit (OpCodes.Ret); + + //vc dyn meth is stored in a cached list, it will be bound to datasource only + //when datasource of source graphic object changed + dmVC = dsValueChangedDynMeths.Count; + dsValueChangedDynMeths.Add (dm); + #endregion + } + + #region emit dataSourceChanged event handler + //now we create the datasource changed method that will init the destination member with + //the actual value of the origin member of the datasource and then will bind the value changed + //dyn methode. + //dm is bound to the instanciator instance to have access to cached dyn meth and delegates + dm = new DynamicMethod ("dyn_dschanged", + typeof (void), + CompilerServices.argsBoundDSChange, true); + + il = dm.GetILGenerator (256); + + il.DeclareLocal (CompilerServices.TObject);//used for checking propery less bindings + il.DeclareLocal (typeof(MemberInfo));//used for checking propery less bindings + System.Reflection.Emit.Label cancel = il.DefineLabel (); + System.Reflection.Emit.Label cancelInit = il.DefineLabel (); + + il.Emit (OpCodes.Nop); + + emitRemoveOldDataSourceHandler(il, "ValueChanged", delName); + + if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ + if (bindingDef.TwoWay){ + System.Reflection.Emit.Label cancelRemove = il.DefineLabel (); + //remove handler if not null + il.Emit (OpCodes.Ldarg_2);//load old parent + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); + il.Emit (OpCodes.Brfalse, cancelRemove);//old parent is null + + //remove handler + il.Emit (OpCodes.Ldarg_2);//1st arg load old datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); + il.Emit (OpCodes.Ldstr, "ValueChanged");//2nd arg event name + il.Emit (OpCodes.Ldarg_1);//3d arg: instance bound to delegate (the source) + il.Emit (OpCodes.Call, CompilerServices.miRemEvtHdlByTarget); + il.MarkLabel(cancelRemove); + } + il.Emit (OpCodes.Ldarg_2);//load datasource change arg + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Brfalse, cancel);//new ds is null + } + + #region fetch initial Value + if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ + il.Emit (OpCodes.Ldarg_2);//load new datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//load member name + il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); + il.Emit (OpCodes.Stloc_1);//save memberInfo + il.Emit (OpCodes.Ldloc_1);//push mi for test if null + il.Emit (OpCodes.Brfalse, cancelInit);//propertyLessBinding + } + + il.Emit (OpCodes.Ldarg_1);//load source of dataSourceChanged which is the dest instance + il.Emit (OpCodes.Ldarg_2);//load new datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ + il.Emit (OpCodes.Ldloc_1);//push mi for value fetching + il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); + } + CompilerServices.emitConvert (il, piSource.PropertyType); + il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); + #endregion + + if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ + il.MarkLabel(cancelInit); + //check if new dataSource implement IValueChange + il.Emit (OpCodes.Ldarg_2);//load new datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Isinst, typeof(IValueChange)); + il.Emit (OpCodes.Brfalse, cancel); + + il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + il.Emit (OpCodes.Ldarg_1);//load datasource change source + il.Emit (OpCodes.Ldarg_2);//load new datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit(OpCodes.Ldc_I4, dmVC);//load index of dynmathod + il.Emit (OpCodes.Call, CompilerServices.miDSChangeEmitHelper); + + if (bindingDef.TwoWay){ + il.Emit (OpCodes.Ldarg_1);//arg1: dataSourceChange source, the origine of the binding + il.Emit (OpCodes.Ldstr, bindingDef.SourceMember);//arg2: orig member + il.Emit (OpCodes.Ldarg_2);//arg3: new datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//arg4: dest member + il.Emit (OpCodes.Call, CompilerServices.miDSReverseBinding); + } + + il.MarkLabel (cancel); + } + il.Emit (OpCodes.Ret); + + //store dschange delegate in instatiator instance for access while instancing graphic object + int delDSIndex = cachedDelegates.Count; + cachedDelegates.Add(dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); + #endregion + + ctx.emitCachedDelegateHandlerAddition(delDSIndex, CompilerServices.eiDSChange); } + /// <summary> + /// Two way binding for datasource, graphicObj=>dataSource link, datasource value has priority + /// and will be set as init for source property (in emitDataSourceBindings func) + /// </summary> + /// <param name="orig">Graphic object instance, source of binding</param> + /// <param name="origMember">Origin member name</param> + /// <param name="dest">datasource instance, target of the binding</param> + /// <param name="destMember">Destination member name</param> + static void dataSourceReverseBinding(IValueChange orig, string origMember, object dest, string destMember){ + Type tOrig = orig.GetType (); + Type tDest = dest.GetType (); + PropertyInfo piOrig = tOrig.GetProperty (origMember); + PropertyInfo piDest = tDest.GetProperty (destMember); + + #region ValueChanged emit + DynamicMethod dm = new DynamicMethod ("dyn_valueChanged" + NewId, + typeof (void), CompilerServices.argsBoundValueChange, true); + ILGenerator il = dm.GetILGenerator (256); + + System.Reflection.Emit.Label endMethod = il.DefineLabel (); + + il.DeclareLocal (CompilerServices.TObject); + il.Emit (OpCodes.Nop); + + //load value changed member name onto the stack + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); + + //test if it's the expected one + il.Emit (OpCodes.Ldstr, origMember); + il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal + il.Emit (OpCodes.Callvirt, CompilerServices.stringEquals); + il.Emit (OpCodes.Brfalse, endMethod); + //set destination member with valueChanged new value + //load destination ref + il.Emit (OpCodes.Ldarg_0); + //load new value onto the stack + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); + + CompilerServices.emitConvert (il, piOrig.PropertyType, piDest.PropertyType); + + il.Emit (OpCodes.Callvirt, piDest.GetSetMethod ()); + + il.MarkLabel (endMethod); + il.Emit (OpCodes.Ret); + #endregion + + orig.ValueChanged += (EventHandler<ValueChangeEventArgs>)dm.CreateDelegate (typeof(EventHandler<ValueChangeEventArgs>), dest); + } + #endregion + + /// <summary> Emits remove old data source event handler./summary> + void emitRemoveOldDataSourceHandler(ILGenerator il, string eventName, string delegateName, bool DSSide = true){ + System.Reflection.Emit.Label cancel = il.DefineLabel (); + + il.Emit (OpCodes.Ldarg_2);//load old parent + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); + il.Emit (OpCodes.Brfalse, cancel);//old parent is null + + //remove handler + if (DSSide){//event is defined in the dataSource instance + il.Emit (OpCodes.Ldarg_2);//1st arg load old datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); + }else//the event is in the source + il.Emit (OpCodes.Ldarg_1);//1st arg load old datasource + il.Emit (OpCodes.Ldstr, eventName);//2nd arg event name + il.Emit (OpCodes.Ldstr, delegateName);//3d arg: delegate name + il.Emit (OpCodes.Call, CompilerServices.miRemEvtHdlByName); + il.MarkLabel(cancel); + } + } } diff --git a/src/Interface.cs b/src/Interface.cs index 1659c0ea..542cf2dd 100644 --- a/src/Interface.cs +++ b/src/Interface.cs @@ -63,7 +63,8 @@ namespace Crow #region Static and constants public static int DoubleClick = 200;//ms - internal static Stopwatch clickTimer = new Stopwatch(); + internal Stopwatch clickTimer = new Stopwatch(); + internal GraphicObject eligibleForDoubleClick = null; public static int TabSize = 4; public static string LineBreak = "\r\n"; //TODO: shold be declared in graphicObject @@ -81,8 +82,8 @@ namespace Crow public const int MaxCacheSize = 2048; /// <summary> Above this count, the layouting is discard for the widget and it /// will not be rendered on screen </summary> - public const int MaxLayoutingTries = 5; - public const int MaxDiscardCount = 10; + public const int MaxLayoutingTries = 3; + public const int MaxDiscardCount = 5; /// <summary> Global font rendering settings for Cairo </summary> public static FontOptions FontRenderingOptions; #endregion @@ -97,14 +98,6 @@ namespace Crow public string Clipboard;//TODO:use object instead for complex copy paste public void EnqueueForRepaint(GraphicObject g) { -// if (g.RegisteredLayoutings != LayoutingType.None) -// return; - ILayoutable l = g; - while (l.Parent != null) - l = l.Parent; - if (!(l is Interface)) - return; - lock (DrawingQueue) { if (g.IsQueueForRedraw) return; @@ -241,10 +234,10 @@ namespace Crow #endregion #if MEASURE_TIME - public Stopwatch clippingTime = new Stopwatch (); - public Stopwatch layoutTime = new Stopwatch (); - public Stopwatch updateTime = new Stopwatch (); - public Stopwatch drawingTime = new Stopwatch (); + public PerformanceMeasure clippingMeasure = new PerformanceMeasure("Clipping", 100); + public PerformanceMeasure layoutingMeasure = new PerformanceMeasure("Layouting", 100); + public PerformanceMeasure updateMeasure = new PerformanceMeasure("Update", 100); + public PerformanceMeasure drawingMeasure = new PerformanceMeasure("Drawing", 100); #endif public List<GraphicObject> GraphicTree = new List<GraphicObject>(); @@ -312,10 +305,10 @@ namespace Crow if (_focusedWidget == value) return; if (_focusedWidget != null) - _focusedWidget.onUnfocused (this, null); + _focusedWidget.HasFocus = false; _focusedWidget = value; if (_focusedWidget != null) - _focusedWidget.onFocused (this, null); + _focusedWidget.HasFocus = true; } } #endregion @@ -328,7 +321,7 @@ namespace Crow // public static LayoutingQueueItem[] MultipleRunsLQIs { // get { return curUpdateLQIs.Where(l=>l.LayoutingTries>2 || l.DiscardCount > 0).ToArray(); } // } - public LayoutingQueueItem currentLQI = null; + public LayoutingQueueItem currentLQI; #else public List<LQIList> LQIs = null;//still create the var for CrowIDE #endif @@ -352,7 +345,7 @@ namespace Crow return; #if MEASURE_TIME - updateTime.Restart(); + updateMeasure.StartCycle(); #endif processLayouting (); @@ -371,19 +364,19 @@ namespace Crow processDrawing (); #if MEASURE_TIME - updateTime.Stop (); + updateMeasure.StopCycle(); #endif Monitor.Exit (UpdateMutex); } void processLayouting(){ #if MEASURE_TIME - layoutTime.Restart(); + layoutingMeasure.StartCycle(); #endif DiscardQueue = new Queue<LayoutingQueueItem> (); lock (LayoutMutex) { //Debug.WriteLine ("======= Layouting queue start ======="); - LayoutingQueueItem lqi = null; + LayoutingQueueItem lqi; while (LayoutingQueue.Count > 0) { lqi = LayoutingQueue.Dequeue (); #if DEBUG_LAYOUTING @@ -391,49 +384,38 @@ namespace Crow curLQIsTries.Add(currentLQI); #endif lqi.ProcessLayouting (); - #if DEBUG_LAYOUTING - currentLQI = null; - #endif } LayoutingQueue = DiscardQueue; } DiscardQueue = null; #if MEASURE_TIME - layoutTime.Stop (); + layoutingMeasure.StopCycle(); #endif } void clippingRegistration(){ #if MEASURE_TIME - clippingTime.Restart (); + clippingMeasure.StartCycle(); #endif - lock (DrawingQueue) { - while (DrawingQueue.Count > 0) { - GraphicObject g = DrawingQueue.Dequeue (); - g.IsQueueForRedraw = false; - try { - if (g.Parent == null) - continue; - g.Parent.RegisterClip (g.LastPaintedSlot); - if (g.getSlot () != g.LastPaintedSlot) - g.Parent.RegisterClip (g.getSlot ()); - } catch (Exception ex) { - Debug.WriteLine ("Error Register Clip: " + ex.ToString ()); - } - } + GraphicObject g = null; + while (DrawingQueue.Count > 0) { + lock (DrawingQueue) + g = DrawingQueue.Dequeue (); + g.IsQueueForRedraw = false; + g.Parent.RegisterClip (g.LastPaintedSlot); + g.Parent.RegisterClip (g.getSlot ()); } + #if MEASURE_TIME - clippingTime.Stop (); + clippingMeasure.StopCycle(); #endif } void processDrawing(){ #if MEASURE_TIME - drawingTime.Restart(); + drawingMeasure.StartCycle(); #endif using (surf = new ImageSurface (bmp, Format.Argb32, ClientRectangle.Width, ClientRectangle.Height, ClientRectangle.Width * 4)) { using (ctx = new Context (surf)){ - - if (clipping.count > 0) { //Link.draw (ctx); clipping.clearAndClip(ctx); @@ -468,12 +450,15 @@ namespace Crow DirtyRect.Width = Math.Max (0, DirtyRect.Width); DirtyRect.Height = Math.Max (0, DirtyRect.Height); - dirtyBmp = new byte[4 * DirtyRect.Width * DirtyRect.Height]; - for (int y = 0; y < DirtyRect.Height; y++) { - Array.Copy (bmp, - ((DirtyRect.Top + y) * ClientRectangle.Width * 4) + DirtyRect.Left * 4, - dirtyBmp, y * DirtyRect.Width * 4, DirtyRect.Width * 4); - } + if (DirtyRect.Width > 0) { + dirtyBmp = new byte[4 * DirtyRect.Width * DirtyRect.Height]; + for (int y = 0; y < DirtyRect.Height; y++) { + Array.Copy (bmp, + ((DirtyRect.Top + y) * ClientRectangle.Width * 4) + DirtyRect.Left * 4, + dirtyBmp, y * DirtyRect.Width * 4, DirtyRect.Width * 4); + } + } else + IsDirty = false; } clipping.Reset (); } @@ -481,7 +466,7 @@ namespace Crow } } #if MEASURE_TIME - drawingTime.Stop (); + drawingMeasure.StopCycle(); #endif } @@ -499,7 +484,6 @@ namespace Crow public void DeleteWidget(GraphicObject g) { g.Visible = false;//trick to ensure clip is added to refresh zone - g.ClearBinding(); GraphicTree.Remove (g); } public void PutOnTop(GraphicObject g) @@ -519,8 +503,8 @@ namespace Crow //TODO:parent is not reset to null because object will be added //to ObjectToRedraw list, and without parent, it fails GraphicObject g = GraphicTree [i]; + g.DataSource = null; g.Visible = false; - g.ClearBinding (); GraphicTree.RemoveAt (0); } #if DEBUG_LAYOUTING @@ -598,24 +582,22 @@ namespace Crow GraphicObject topc = null; while (tmp is GraphicObject) { topc = tmp; - tmp = tmp.Parent as GraphicObject; + tmp = tmp.LogicalParent as GraphicObject; } int idxhw = GraphicTree.IndexOf (topc); if (idxhw != 0) { int i = 0; while (i < idxhw) { - if (GraphicTree [i].MouseIsIn (e.Position)) { - while (HoverWidget != null) { - if (HoverWidget is Popper) { - if ((HoverWidget as Popper).Content == GraphicTree[i]) - break; + if (GraphicTree [i].localLogicalParentIsNull) { + if (GraphicTree [i].MouseIsIn (e.Position)) { + while (HoverWidget != null) { + HoverWidget.onMouseLeave (HoverWidget, e); + HoverWidget = HoverWidget.LogicalParent as GraphicObject; } - HoverWidget.onMouseLeave (HoverWidget, e); - HoverWidget = HoverWidget.LogicalParent as GraphicObject; - } - GraphicTree [i].checkHoverWidget (e); - return true; + GraphicTree [i].checkHoverWidget (e); + return true; + } } i++; } diff --git a/src/ItemTemplate.cs b/src/ItemTemplate.cs index fe26f7a7..1f03f7f8 100644 --- a/src/ItemTemplate.cs +++ b/src/ItemTemplate.cs @@ -31,8 +31,11 @@ using System.Linq; namespace Crow { + public delegate bool BooleanTestOnInstance(object instance); + public class ItemTemplate : Instantiator { public EventHandler Expand; + public BooleanTestOnInstance HasSubItems; string strDataType; string fetchMethodName; @@ -41,9 +44,10 @@ namespace Crow : base(path) { strDataType = _dataType; fetchMethodName = _fetchDataMethod; + } - public ItemTemplate (Type _root, InstanciatorInvoker _loader,string _dataType, string _fetchDataMethod) - :base(_root, _loader) + public ItemTemplate (Stream ImlFragment, string _dataType, string _fetchDataMethod) + :base(ImlFragment) { strDataType = _dataType; fetchMethodName = _fetchDataMethod; @@ -53,7 +57,6 @@ namespace Crow public void CreateExpandDelegate (TemplatedGroup host){ Type dataType = Type.GetType(strDataType); Type tmpGrpType = typeof(TemplatedGroup); - Type hostType = tmpGrpType;//not sure is the best place to put the dyn method Type evtType = typeof(EventHandler); PropertyInfo piData = tmpGrpType.GetProperty ("Data"); @@ -62,14 +65,14 @@ namespace Crow ParameterInfo [] evtParams = evtInvoke.GetParameters (); Type handlerArgsType = evtParams [1].ParameterType; - Type [] args = { typeof (object), typeof (object), handlerArgsType }; - DynamicMethod dm = new DynamicMethod ("dyn_expand_" + fetchMethodName, - typeof (void), - args, - hostType); + Type [] args = { CompilerServices.TObject, CompilerServices.TObject, handlerArgsType }; + #region Expand dyn meth + //DM is bound to templatedGroup root (arg0) + //arg1 is the sender of the expand event + DynamicMethod dm = new DynamicMethod ("dyn_expand_" + fetchMethodName, + typeof (void), args, true); - #region IL generation System.Reflection.Emit.Label gotoEnd; System.Reflection.Emit.Label ifDataIsNull; @@ -81,9 +84,8 @@ namespace Crow il.Emit (OpCodes.Ldarg_1);//load sender of expand event - MethodInfo miFindByName = typeof(GraphicObject).GetMethod("FindByName"); il.Emit(OpCodes.Ldstr, "List"); - il.Emit (OpCodes.Callvirt, miFindByName); + il.Emit (OpCodes.Callvirt, CompilerServices.miFindByName); il.Emit (OpCodes.Stloc_0); //check that 'Data' of list is not already set @@ -103,11 +105,10 @@ namespace Crow //call 'fetchMethodName' from the dataSource to build the sub nodes list il.Emit (OpCodes.Ldloc_0);//push 'List' (of sub nodes) into the stack il.Emit (OpCodes.Ldarg_1);//get the dataSource of the sender - il.Emit (OpCodes.Callvirt, typeof(GraphicObject).GetProperty("DataSource").GetGetMethod ()); + il.Emit (OpCodes.Callvirt, CompilerServices.miGetDataSource); - if (fetchMethodName != "self"){//special keyword self allows the use of recurent list<<< - emitGetSubData(il, dataType); - } + if (fetchMethodName != "self")//special keyword self allows the use of recurent list<<< + emitGetSubData(il, dataType); //set 'return' from the fetch method as 'data' of the list il.Emit (OpCodes.Callvirt, piData.GetSetMethod ()); @@ -115,9 +116,28 @@ namespace Crow il.MarkLabel(gotoEnd); il.Emit (OpCodes.Ret); + Expand = (EventHandler)dm.CreateDelegate (evtType, host); #endregion - Expand = (EventHandler)dm.CreateDelegate (evtType, host); + #region Items counting dyn method + //dm is unbound, arg0 is instance of Item container to expand + dm = new DynamicMethod ("dyn_count_" + fetchMethodName, + typeof (bool), new Type[] {CompilerServices.TObject}, true); + il = dm.GetILGenerator (256); + + //get the dataSource of the arg0 + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Callvirt, CompilerServices.miGetDataSource); + + if (fetchMethodName != "self") + emitGetSubData(il, dataType); + + il.Emit (OpCodes.Callvirt, CompilerServices.miGetColCount); + il.Emit (OpCodes.Ldc_I4_0); + il.Emit (OpCodes.Cgt); + il.Emit (OpCodes.Ret); + HasSubItems = (BooleanTestOnInstance)dm.CreateDelegate (typeof(BooleanTestOnInstance)); + #endregion } void emitGetSubData(ILGenerator il, Type dataType){ MethodInfo miGetDatas = dataType.GetMethod (fetchMethodName, new Type[] {}); diff --git a/src/LayoutingQueueItem.cs b/src/LayoutingQueueItem.cs index 2257ae8e..95bc6bb7 100644 --- a/src/LayoutingQueueItem.cs +++ b/src/LayoutingQueueItem.cs @@ -38,10 +38,11 @@ namespace Crow ArrangeChildren = 0x10, All = 0xFF } + /// <summary> /// Element class of the LayoutingQueue /// </summary> - public class LayoutingQueueItem + public struct LayoutingQueueItem { /// <summary> Instance of widget to be layouted</summary> public ILayoutable Layoutable; @@ -51,9 +52,7 @@ namespace Crow public int LayoutingTries, DiscardCount; #if DEBUG_LAYOUTING - public Stopwatch LQITime = new Stopwatch(); - public LQIList triggeredLQIs = new LQIList(); - public LayoutingQueueItem wasTriggeredBy = null; + public Stopwatch LQITime; public GraphicObject graphicObject { get { return Layoutable as GraphicObject; } } @@ -69,23 +68,21 @@ namespace Crow public Measure Height { get { return graphicObject.Height; } } - public bool HasTriggeredLQIs { get { return triggeredLQIs.Count > 0; }} public Rectangle Slot, NewSlot; #endif #region CTOR public LayoutingQueueItem (LayoutingType _layoutType, ILayoutable _graphicObject) - { + { LayoutType = _layoutType; Layoutable = _graphicObject; Layoutable.RegisteredLayoutings |= LayoutType; + LayoutingTries = 0; + DiscardCount = 0; #if DEBUG_LAYOUTING - GraphicObject g = graphicObject; - g.CurrentInterface.curLQIs.Add(this); - if (g.CurrentInterface.currentLQI != null){ - wasTriggeredBy = g.CurrentInterface.currentLQI; - g.CurrentInterface.currentLQI.triggeredLQIs.Add(this); - } + LQITime = new Stopwatch(); + Slot = Rectangle.Empty; + NewSlot = Rectangle.Empty; #endif } #endregion @@ -121,6 +118,10 @@ namespace Crow #endif } #if DEBUG_LAYOUTING + else{ + if (LayoutingTries > 2 || DiscardCount > 0) + Debug.WriteLine (this.ToString ()); + } LQITime.Stop(); #endif } @@ -145,11 +146,11 @@ namespace Crow } } public class LQIList : List<LayoutingQueueItem>{ - #if DEBUG_LAYOUTING - public List<LayoutingQueueItem> GetRootLQIs(){ - return this.Where (lqi => lqi.wasTriggeredBy == null).ToList (); - } - #endif +// #if DEBUG_LAYOUTING +// public List<LayoutingQueueItem> GetRootLQIs(){ +// return this.Where (lqi => lqi.wasTriggeredBy == null).ToList (); +// } +// #endif } } diff --git a/src/PerformanceMeasure.cs b/src/PerformanceMeasure.cs new file mode 100644 index 00000000..8fc0e531 --- /dev/null +++ b/src/PerformanceMeasure.cs @@ -0,0 +1,86 @@ +// +// PerformanceMeasure.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2017 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +using System; +using System.Diagnostics; + +namespace Crow +{ + public class PerformanceMeasure : IValueChange { + #region IValueChange implementation + public event EventHandler<ValueChangeEventArgs> ValueChanged; + public virtual void NotifyValueChanged(string MemberName, object _value) + { + if (ValueChanged != null) + ValueChanged.Invoke(this, new ValueChangeEventArgs(MemberName, _value)); + } + #endregion + + public Stopwatch timer = new Stopwatch (); + public string Name; + public long current, minimum, maximum, total, cptMeasures; + public long cancelLimit; + + public PerformanceMeasure(string name = "unamed", long _cancelLimit = 0){ + Name = name; + cancelLimit = _cancelLimit; + ResetStats (); + } + + public void StartCycle(){ + timer.Restart(); + } + public void StopCycle(){ + timer.Stop(); + computeStats (); + } + public void NotifyChanges(){ + if (cptMeasures == 0) + return; + NotifyValueChanged("minimum", minimum); + NotifyValueChanged("maximum", maximum); + NotifyValueChanged("current", current); + // NotifyValueChanged("total", total); + // NotifyValueChanged("cptMeasures", cptMeasures); + NotifyValueChanged("mean", total / cptMeasures); + } + + void computeStats(){ + current = timer.ElapsedTicks; + if (current < cancelLimit) + return; + cptMeasures++; + total += timer.ElapsedTicks; + if (timer.ElapsedTicks < minimum) + minimum = timer.ElapsedTicks; + if (timer.ElapsedTicks > maximum) + maximum = timer.ElapsedTicks; + } + void ResetStats(){ + Debug.WriteLine("reset measure cpt:{0}",cptMeasures); + cptMeasures = total = current = maximum = 0; + minimum = long.MaxValue; + } + void onResetClick(object sender, MouseButtonEventArgs e){ + ResetStats(); + } + } +} + diff --git a/src/Rectangle.cs b/src/Rectangle.cs index 9a4ef396..cc1aa56f 100644 --- a/src/Rectangle.cs +++ b/src/Rectangle.cs @@ -7,6 +7,7 @@ namespace Crow { public struct Rectangle { + internal static Type TRectangle = typeof(Rectangle); #region private fields int _x; int _y; @@ -200,34 +201,7 @@ namespace Crow { return r1.TopLeft == r2.TopLeft && r1.Size == r2.Size ? false : true; } - #endregion - - public RectanglesRelations test(Rectangle r) - { - if (r == this) - return RectanglesRelations.Equal; - - int nbrPtIncluded = 0; - - if (this.ContainsOrIsEqual(r.TopLeft)) - nbrPtIncluded++; - if (this.ContainsOrIsEqual(r.TopRight)) - nbrPtIncluded++; - if (this.ContainsOrIsEqual(r.BottomLeft)) - nbrPtIncluded++; - if (this.ContainsOrIsEqual(r.BottomRight)) - nbrPtIncluded++; - - switch (nbrPtIncluded) - { - case 0: - return RectanglesRelations.NoRelation; - case 4: - return RectanglesRelations.Contains; - default: - return RectanglesRelations.Intersect; - } - } + #endregion public static readonly Rectangle Zero = new Rectangle(0, 0, 0, 0); public static Rectangle Empty @@ -262,7 +236,7 @@ namespace Crow } public override bool Equals (object obj) { - return (obj == null || obj.GetType() != typeof(Rectangle)) ? + return (obj == null || obj.GetType() != TRectangle) ? false : this == (Rectangle)obj; } diff --git a/src/Rectangles.cs b/src/Rectangles.cs index 5168f3db..965b28c3 100644 --- a/src/Rectangles.cs +++ b/src/Rectangles.cs @@ -35,7 +35,7 @@ namespace Crow return false; return true; } - + public bool intersect(Rectangle r) { foreach (Rectangle rInList in list) @@ -43,102 +43,10 @@ namespace Crow return true; return false; } - - public Rectangles intersectingRects(Rectangle r) - { - Rectangles tmp = new Rectangles(); - - foreach (Rectangle rInList in list) - if (rInList.Intersect(r)) - tmp.list.Add(rInList);//on bypass le test déjà fait a l'ajout intial du rect dans la liste - - return tmp; - } - public Rectangles SmallerContainedRects(Rectangle r) - { - Rectangles tmp = new Rectangles(); - - foreach (Rectangle rInList in list) - if (r.ContainsOrIsEqual(rInList) && rInList.Size < r.Size) - tmp.list.Add(rInList); - - return tmp; - } - /// <summary> - /// Return rectangles with size smaller than r.size - /// </summary> - public Rectangles SmallerRects(Rectangle r) - { - Rectangles tmp = new Rectangles(); - - foreach (Rectangle rInList in list) - if (rInList.Size < r.Size) - tmp.list.Add(rInList); - - return tmp; - } - public Rectangles containedOrEqualRects(Rectangle r) - { - Rectangles tmp = new Rectangles(); - - foreach (Rectangle rInList in list) - if (r.ContainsOrIsEqual(rInList))// && rInList.Size <= r.Size) - tmp.list.Add(rInList); - - return tmp; - } - public void Srcoll(GraphicObject w) - { - Scroller sw = w as Scroller; - if (sw == null) - return; - - List<Rectangle> newList = new List<Rectangle>(); - - foreach (Rectangle rInList in list) - { - Rectangle r = rInList; - - if (sw.VerticalScrolling) - r.Top -= (int)sw.ScrollY; - if (sw.HorizontalScrolling) - r.Left -= (int)sw.ScrollX; - - newList.Add(r); - } - list = newList; - } - public void Rebase(GraphicObject w) - { - Rectangle r = w.Parent.ContextCoordinates(w.Slot); - List<Rectangle> newList = new List<Rectangle>(); - - foreach (Rectangle rInList in list) - { - Rectangle rebasedR = rInList; - rebasedR.TopLeft-= r.TopLeft; - - Scroller sw = w as Scroller; - if (sw != null) - { - if (sw.VerticalScrolling) { - rebasedR.Top -= (int)sw.ScrollY; -// if (sw.scrollY < 0) -// Debug.WriteLine (".."); - }if (sw.HorizontalScrolling) - rebasedR.Left -= (int)sw.ScrollX; - } - - newList.Add(rebasedR); - } - list = newList; - } public void stroke(Context ctx, Color c) { foreach (Rectangle r in list) - { ctx.Rectangle(r); - } ctx.SetSourceColor(c); @@ -153,17 +61,15 @@ namespace Crow ctx.Rectangle(r); ctx.ClipPreserve(); - ctx.Operator = Operator.Clear; + ctx.Operator = Operator.Clear; ctx.Fill(); - ctx.Operator = Operator.Over; + ctx.Operator = Operator.Over; } public void clip(Context ctx) { foreach (Rectangle r in list) - { - ctx.Rectangle(r); - } + ctx.Rectangle(r); ctx.Clip(); } @@ -187,16 +93,11 @@ namespace Crow } public void clear(Context ctx) { - //ctx.Save(); - foreach (Rectangle r in list) - { ctx.Rectangle(r); - } ctx.Operator = Operator.Clear; ctx.Fill(); ctx.Operator = Operator.Over; - //ctx.Restore(); } public override string ToString () { diff --git a/src/Size.cs b/src/Size.cs index 5059d31a..996de49f 100644 --- a/src/Size.cs +++ b/src/Size.cs @@ -7,6 +7,7 @@ namespace Crow { public struct Size { + internal static Type TSize = typeof(Size); public static Size Zero { get { return new Size(0, 0); } } @@ -143,7 +144,7 @@ namespace Crow } public override bool Equals (object obj) { - return (obj == null || obj.GetType() != typeof(Size)) ? + return (obj == null || obj.GetType() != TSize) ? false : this == (Size)obj; } |