diff options
| author | jpbruyere <jp.bruyere@hotmail.com> | 2017-01-01 18:30:51 +0100 |
|---|---|---|
| committer | jpbruyere <jp.bruyere@hotmail.com> | 2017-01-01 18:30:51 +0100 |
| commit | 0f1346e33b59f7693f4ff40b5524c93d2629c5ce (patch) | |
| tree | 7f531b6bf6fe3f37da101a12a9b2529403e4c53c /src | |
| parent | 7c00385bda3a9a913050480fada2efab8d6d3090 (diff) | |
| parent | 7fa6c6ca89e7a3aeadd815867de8a2a3b955134a (diff) | |
New binding system and new inherited measures
Diffstat (limited to 'src')
43 files changed, 3333 insertions, 1352 deletions
diff --git a/src/Colors.cs b/src/Colors.cs index 6787c382..65a7f037 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) { @@ -1046,7 +1048,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; } @@ -1071,5 +1073,34 @@ namespace Crow { return (Color)s; } + public static Color FromHSV(double _h, double _v = 1.0, double _s = 1.0){ + Color c = Color.Black; + + if (_s == 0) {//HSV from 0 to 1 + c.R = _v; + c.G = _v; + c.B = _v; + }else{ + double var_h = _h * 6.0; + + if (var_h == 6.0) + var_h = 0; //H must be < 1 + double var_i = Math.Floor( var_h ); //Or ... var_i = floor( var_h ) + double var_1 = _v * ( 1.0 - _s ); + double var_2 = _v * (1.0 - _s * (var_h - var_i)); + double var_3 = _v * (1.0 - _s * (1.0 - (var_h - var_i))); + + if (var_i == 0.0) { + c.R = _v; + c.G = var_3; + c.B = var_1; + }else if ( var_i == 1.0 ) { c.R = var_2 ; c.G = _v ; c.B = var_1; } + else if ( var_i == 2 ) { c.R = var_1 ; c.G = _v ; c.B = var_3; } + else if ( var_i == 3 ) { c.R = var_1 ; c.G = var_2 ; c.B = _v; } + else if ( var_i == 4 ) { c.R = var_3 ; c.G = var_1 ; c.B = _v; } + else { c.R = _v ; c.G = var_1 ; c.B = var_2; } + } + return c; + } } } diff --git a/src/CompilerServices/Bindings.cs b/src/CompilerServices/Bindings.cs index 64f412f6..2f4077b2 100644 --- a/src/CompilerServices/Bindings.cs +++ b/src/CompilerServices/Bindings.cs @@ -199,10 +199,10 @@ namespace Crow 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)); +// 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 diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index 43c9d704..c0acc5d5 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 stringEquals = typeof (string).GetMethod - ("Equals", new Type [3] { typeof (string), typeof (string), typeof (StringComparison) }); - 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); - #region ValueChange Reflexion member info - 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 miGetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle"); + internal static MethodInfo miGetEvent = typeof(Type).GetMethod("GetEvent", new Type[] {typeof(string)}); + + 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,275 +204,385 @@ 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 }); + #region conversions -#if DEBUG_BINDING - Debug.WriteLine ("\tHandler binded => " + b.ToString()); -#endif - b.Resolved = true; - } catch (Exception ex) { - //Debug.WriteLine ("\tERROR: " + ex.ToString ()); - } - continue; - } + internal static MethodInfo GetConvertMethod (Type targetType) + { + string name; - if (!b.TryFindTarget ()) - continue; + 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)); - //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; - } + return typeof (Convert).GetMethod (name, BindingFlags.Static | BindingFlags.Public, null, new Type [] { typeof (object) }, null); + } + #endregion - Type target_Type = Bindings [0].Source.Instance.GetType (); + /// <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; + } - //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 (); + /// <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; - DynamicMethod dm = null; - ILGenerator il = null; + 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; - System.Reflection.Emit.Label [] jumpTable = null; - System.Reflection.Emit.Label endMethod = new System.Reflection.Emit.Label (); + if (query.Count () > 0) + break; - #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); + curType = curType.BaseType; + } while (curType != null); - il = dm.GetILGenerator (256); + return query; + } - 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)); + 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; - 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 + return GetExtensionMethods (Assembly.GetExecutingAssembly(), t) + .Where (em => em.Name == methodName).FirstOrDefault (); + } - 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 + 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; - } 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 + MemberInfo miDest = getMemberInfoWithReflexion (dest, destMember); - //if no dyn update, skip jump table - if (il == null) - continue; + if (miDest == null) { + Debug.WriteLine ("Reverse template binding error: " + destMember + " not found in " + dest); + return; + } - 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 ()); - il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal - il.Emit (OpCodes.Callvirt, stringEquals); - il.Emit (OpCodes.Brtrue, jumpTable [i]); - i++; - } + if (miDest.MemberType == MemberTypes.Property) + destType =(miDest as PropertyInfo).PropertyType; + else if (miDest.MemberType == MemberTypes.Field) + destType =(miDest as FieldInfo).FieldType; - if (il == null) - continue; + 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); + } - il.Emit (OpCodes.Br, endMethod); + 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); - i = 0; - foreach (Binding b in grouped) { + 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.MarkLabel (jumpTable [i]); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Isinst, dstType); + il.Emit (OpCodes.Brfalse, convert); + if (dstType.IsValueType) + il.Emit (OpCodes.Unbox_Any, dstType); + else + il.Emit (OpCodes.Isinst, dstType); + il.Emit (OpCodes.Br, endConvert); - //load 2 times to check first for null - il.Emit (OpCodes.Ldloc_1); - il.Emit (OpCodes.Ldloc_1); + il.MarkLabel (convert); - 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); + if (dstType == typeof(string)) { + il.Emit (OpCodes.Callvirt, CompilerServices.miObjToString); + } 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 + il.Emit (OpCodes.Ldloc_0); + 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.Ldloc_0); + il.Emit (OpCodes.Isinst, dstType); + il.Emit (OpCodes.Br, endConvert); - il.MarkLabel (labSetValue); - //new value not null + 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); + } - //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"); + 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> + /// Removes delegate from event handler by name + /// </summary> + 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 } - - - - 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) { - il.Emit (OpCodes.Callvirt, CompilerServices.GetConvertMethod (b.Source.Property.PropertyType)); - } else - il.Emit (OpCodes.Unbox_Any, b.Source.Property.PropertyType); - - il.Emit (OpCodes.Callvirt, b.Source.Property.GetSetMethod ()); - - //il.BeginCatchBlock (typeof (Exception)); - //il.Emit (OpCodes.Pop); - //il.EndExceptionBlock (); - - il.Emit (OpCodes.Br, endMethod); - i++; - } - il.MarkLabel (endMethod); - il.Emit (OpCodes.Pop); - il.Emit (OpCodes.Ret); - - Delegate del = dm.CreateDelegate (eiValueChange.EventHandlerType, Bindings [0].Source.Instance); - miValueChangeAdd.Invoke (grouped [0].Target.Instance, new object [] { del }); } } - /// <summary> - /// Compile events expression in GOML attributes + /// Removes delegate from event handler by searching for the object they are bond to /// </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 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); + if (mi == null) + 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 (); @@ -428,51 +590,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 @@ -501,108 +671,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; - } - - #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); + return dm.CreateDelegate (sourceEvent.EventHandlerType); } - #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/Enums.cs b/src/Enums.cs index aac12e16..46f8cdab 100644 --- a/src/Enums.cs +++ b/src/Enums.cs @@ -11,7 +11,7 @@ namespace Crow Vertical } - public enum Alignment : byte + public enum Alignment { Top = 0x01, Left = 0x02, 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 new file mode 100644 index 00000000..cc8dfac3 --- /dev/null +++ b/src/GraphicObjects/ColorPicker.cs @@ -0,0 +1,223 @@ +// +// ColorPicker.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.Serialization; +using System.ComponentModel; + +namespace Crow +{ + public class ColorPicker : TemplatedControl + { + public ColorPicker () : base () + { + } + + const double div = 255.0; + const double colDiv = 1.0 / div; + + Color curColor; + double h,s,v; + + [XmlAttributeAttribute()] + public virtual double R { + get { return Math.Round(curColor.R * div); } + set { + if (R == value) + return; + curColor.R = value * colDiv; + NotifyValueChanged ("R", R); + hsvFromRGB (); + notifyCurColorHasChanged (); + } + } + [XmlAttributeAttribute()] + public virtual double G { + get { return Math.Round(curColor.G * div); } + set { + if (G == value) + return; + curColor.G = value * colDiv; + NotifyValueChanged ("G", G); + notifyCurColorHasChanged (); + hsvFromRGB (); + } + } + [XmlAttributeAttribute()] + public virtual double B { + get { return Math.Round(curColor.B * div); } + set { + if (B == value) + return; + curColor.B = value * colDiv; + NotifyValueChanged ("B", B); + notifyCurColorHasChanged (); + hsvFromRGB (); + } + } + [XmlAttributeAttribute()] + public virtual double A { + get { return Math.Round(curColor.A * div); } + set { + if (A == value) + return; + curColor.A = value * colDiv; + NotifyValueChanged ("A", A); + notifyCurColorHasChanged (); + hsvFromRGB (); + } + } + [XmlAttributeAttribute()] + public virtual double H { + get { return Math.Round (h, 3); } + set { + if (H == value) + return; + h = value; + NotifyValueChanged ("H", H); + rgbFromHSV (); + } + } + [XmlAttributeAttribute()] + public virtual double S { + get { return Math.Round (s, 2); } + set { + if (s == value) + return; + s = value; + NotifyValueChanged ("S", S); + rgbFromHSV (); + } + } + [XmlAttributeAttribute()] + public virtual double V { + get { return Math.Round (v, 2); } + set { + if (v == value) + return; + v = value; + NotifyValueChanged ("V", V); + rgbFromHSV (); + } + } + + [XmlAttributeAttribute] + public virtual Fill SelectedColor { + get { return new SolidColor(curColor); } + set { + Color c = (value as SolidColor).color; + if (curColor == c) + return; + curColor = c; + notifyCurColorHasChanged (); + notifyRGBAHasChanged (); + hsvFromRGB (); + } + } + + void notifyCurColorHasChanged(){ + NotifyValueChanged ("SelectedColor", SelectedColor); + string n = curColor.ToString (); + if (char.IsLetter(n[0])) + NotifyValueChanged ("SelectedColorName", n); + else + NotifyValueChanged ("SelectedColorName", "-"); + NotifyValueChanged ("HexColor", ((int)R).ToString ("X2") + ((int)G).ToString ("X2") + ((int)B).ToString ("X2") + ((int)A).ToString ("X2")); + } + void notifyRGBAHasChanged(){ + NotifyValueChanged ("R", R); + NotifyValueChanged ("G", G); + NotifyValueChanged ("B", B); + NotifyValueChanged ("A", A); + } + void notifyHSVHasChanged(){ + NotifyValueChanged ("H", H); + NotifyValueChanged ("S", S); + NotifyValueChanged ("V", V); + } + void hsvFromRGB(){ + Color c = curColor; + 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 + + v = max; + + if ( diff == 0 )//This is a gray, no chroma... + { + h = 0; + s = 0; + }else{//Chromatic data... + s = diff / max; + + double diffR = (((max - c.R) / 6.0) + (diff / 2.0)) / diff; + double diffG = (((max - c.G) / 6.0) + (diff / 2.0)) / diff; + double diffB = (((max - c.B) / 6.0) + (diff / 2.0)) / diff; + + if (c.R == max) + h = diffB - diffG; + else if (c.G == max) + h = (1.0 / 3.0) + diffR - diffB; + else if (c.B == max) + h = (2.0 / 3.0) + diffG - diffR; + + if (h < 0) + h += 1; + if (h > 1) + h -= 1; + + } + notifyHSVHasChanged (); + } + void rgbFromHSV(){ + Color c = Color.Black; + + if (s == 0) {//HSV from 0 to 1 + c.R = v; + c.G = v; + c.B = v; + }else{ + double var_h = h * 6.0; + + if (var_h == 6.0) + var_h = 0; //H must be < 1 + double var_i = Math.Floor( var_h ); //Or ... var_i = floor( var_h ) + double var_1 = v * ( 1.0 - s ); + double var_2 = v * (1.0 - s * (var_h - var_i)); + double var_3 = v * (1.0 - s * (1.0 - (var_h - var_i))); + + if (var_i == 0.0) { + c.R = v; + c.G = var_3; + c.B = var_1; + }else if ( var_i == 1.0 ) { c.R = var_2 ; c.G = v ; c.B = var_1; } + else if ( var_i == 2 ) { c.R = var_1 ; c.G = v ; c.B = var_3; } + else if ( var_i == 3 ) { c.R = var_1 ; c.G = var_2 ; c.B = v; } + else if ( var_i == 4 ) { c.R = var_3 ; c.G = var_1 ; c.B = v; } + else { c.R = v ; c.G = var_1 ; c.B = var_2; } + } + + curColor = c; + notifyCurColorHasChanged (); + notifyRGBAHasChanged (); + } + } +} + diff --git a/src/GraphicObjects/ColorSelector.cs b/src/GraphicObjects/ColorSelector.cs new file mode 100644 index 00000000..13a588ce --- /dev/null +++ b/src/GraphicObjects/ColorSelector.cs @@ -0,0 +1,89 @@ +// +// ColorPicker.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.Serialization; +using System.ComponentModel; + +namespace Crow +{ + public class ColorSelector : GraphicObject + { + public ColorSelector (): base() + { + } + + const double div = 255.0; + const double colDiv = 1.0 / div; + protected Point mousePos; + + public override void onMouseMove (object sender, MouseMoveEventArgs e) + { + base.onMouseMove (sender, e); + if (CurrentInterface.Mouse.LeftButton == ButtonState.Released) + return; + updateMouseLocalPos (e.Position); + } + public override void onMouseDown (object sender, MouseButtonEventArgs e) + { + base.onMouseDown (sender, e); + if (e.Button == MouseButton.Left) + updateMouseLocalPos (e.Position); + } + + protected virtual void updateMouseLocalPos(Point mPos){ + Rectangle r = ScreenCoordinates (Slot); + Rectangle cb = ClientRectangle; + mousePos = mPos - r.Position; + + mousePos.X = Math.Max(cb.X, mousePos.X); + mousePos.X = Math.Min(cb.Right-1, mousePos.X); + mousePos.Y = Math.Max(cb.Y, mousePos.Y); + mousePos.Y = Math.Min(cb.Bottom-1, mousePos.Y); + } +// virtual protected void updateColorFromPicking(bool redraw = true){ +// SelectedColor = new SolidColor(getPixelAt(mousePos.X, mousePos.Y)); +// +// updateHSV (); +// +// NotifyValueChanged ("R", R); +// NotifyValueChanged ("G", G); +// NotifyValueChanged ("B", B); +// NotifyValueChanged ("A", A); +// +// if (redraw) +// RegisterForRedraw (); +// } +// +// protected Color getPixelAt(int x, int y){ +// if (bmp == null) +// return Color.Transparent; +// +// int ptr = y * Slot.Width * 4 + x * 4; +// +// return new Color( +// (double)bmp[ptr + 2] * colDiv, +// (double)bmp[ptr + 1] * colDiv, +// (double)bmp[ptr] * colDiv, +// (double)bmp[ptr + 3] * colDiv); +// } + } +} + 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/FileDialog.cs b/src/GraphicObjects/FileDialog.cs index 16a60a4f..543b27cc 100644 --- a/src/GraphicObjects/FileDialog.cs +++ b/src/GraphicObjects/FileDialog.cs @@ -29,112 +29,103 @@ using System.Text.RegularExpressions; namespace Crow { - public class FileDialog: IValueChange + public class FileDialog: Window { - #region IValueChange implementation - public event EventHandler<ValueChangeEventArgs> ValueChanged; - public virtual void NotifyValueChanged(string MemberName, object _value) - { - ValueChanged.Raise(this, new ValueChangeEventArgs(MemberName, _value)); - } - #endregion - - Window window; - DirectoryInfo currentDir; - FileInfo selectedFile; - string searchPattern = "*"; + string searchPattern, curDirectory; #region CTOR - public FileDialog () + public FileDialog () : base() { - currentDir = new DirectoryInfo (Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)); - //window = Interface..Load("#Crow.Templates.FileDialog.goml") as Window; - window.DataSource = this; } #endregion - - public string CurrentPath { - get { return currentDir.FullName; } + //[DefaultValue(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments))] + [XmlAttributeAttribute][DefaultValue("/home")] + public virtual string CurrentDirectory { + get { return curDirectory; } set { - currentDir = new DirectoryInfo (value); - NotifyValueChanged ("CurrentPath", CurrentPath); + if (curDirectory == value) + return; + curDirectory = value; + NotifyValueChanged ("CurrentDirectory", curDirectory); + } - } - public string SearchPattern { + } + + [XmlAttributeAttribute()][DefaultValue("*")] + public virtual string SearchPattern { get { return searchPattern; } set { - searchPattern = value; - NotifyValueChanged ("Files", Files); - } - } + if (searchPattern == value) + return; + searchPattern = value; + NotifyValueChanged ("SearchPattern", searchPattern); - public DirectoryInfo[] Directories - { - get { - //currentDir.GetDirectories - List<DirectoryInfo> tmp = currentDir.GetDirectories ().Where(fi => !fi.Attributes.HasFlag(FileAttributes.Hidden)).ToList(); - if (currentDir.Parent != null) - tmp.Insert (0, currentDir.Parent); - return tmp.ToArray (); } - } - public FileInfo[] Files - { - get { - string[] exts = searchPattern.Replace("*","").Split ('|'); - //return currentDir.GetFiles (searchPattern).Where(fi => !fi.Attributes.HasFlag(FileAttributes.Hidden)).ToArray(); - return currentDir.GetFiles().Where(f => exts.Any - (x => f.Name.EndsWith (x, StringComparison.InvariantCultureIgnoreCase))).ToArray(); - } - } - - public void Show(){ - //.AddWidget(window); - } - - void OnSelectedItemChanged (object sender, SelectionChangeEventArgs e) - { - currentDir = e.NewValue as DirectoryInfo; - NotifyValueChanged ("CurrentPath", CurrentPath); - NotifyValueChanged ("Directories", Directories); - NotifyValueChanged ("Files", Files); - - } - void onFileListItemChanged (object sender, SelectionChangeEventArgs e) - { - selectedFile = e.NewValue as FileInfo; - } - void onFileSelect(object sender, MouseButtonEventArgs e){ - //OpenTKGameWindow.currentWindow.DeleteWidget(window); - } - } - public class DirContainer: IValueChange - { - #region IValueChange implementation - public event EventHandler<ValueChangeEventArgs> ValueChanged; - public void NotifyValueChanged(string name, object value) - { - ValueChanged.Raise (this, new ValueChangeEventArgs (name, value)); - } - #endregion + } - public DirectoryInfo CurDir; - public DirContainer(DirectoryInfo _dir){ - CurDir = _dir; - } - public string Name { - get { return CurDir.Name; } - } - - void onDirUp(object sender, MouseButtonEventArgs e) - { - - } - public void onMouseDown(object sender, MouseButtonEventArgs e) - { - Debug.WriteLine (sender.ToString ()); - } +// public DirectoryInfo[] Directories +// { +// get { +// //currentDir.GetDirectories +// List<DirectoryInfo> tmp = currentDir.GetDirectories ().Where(fi => !fi.Attributes.HasFlag(FileAttributes.Hidden)).ToList(); +// if (currentDir.Parent != null) +// tmp.Insert (0, currentDir.Parent); +// return tmp.ToArray (); +// } +// } +// public FileInfo[] Files +// { +// get { +// string[] exts = searchPattern.Replace("*","").Split ('|'); +// //return currentDir.GetFiles (searchPattern).Where(fi => !fi.Attributes.HasFlag(FileAttributes.Hidden)).ToArray(); +// return currentDir.GetFiles().Where(f => exts.Any +// (x => f.Name.EndsWith (x, StringComparison.InvariantCultureIgnoreCase))).ToArray(); +// } +// } +// void OnSelectedItemChanged (object sender, SelectionChangeEventArgs e) +// { +// currentDir = e.NewValue as DirectoryInfo; +// NotifyValueChanged ("CurrentPath", CurrentPath); +// NotifyValueChanged ("Directories", Directories); +// NotifyValueChanged ("Files", Files); +// +// } +// void onFileListItemChanged (object sender, SelectionChangeEventArgs e) +// { +// selectedFile = e.NewValue as FileInfo; +// } +// void onFileSelect(object sender, MouseButtonEventArgs e){ +// //OpenTKGameWindow.currentWindow.DeleteWidget(window); +// } } +// public class DirContainer: IValueChange +// { +// #region IValueChange implementation +// public event EventHandler<ValueChangeEventArgs> ValueChanged; +// public void NotifyValueChanged(string name, object value) +// { +// ValueChanged.Raise (this, new ValueChangeEventArgs (name, value)); +// } +// #endregion +// +// public DirectoryInfo CurDir; +// public DirContainer(DirectoryInfo _dir){ +// CurDir = _dir; +// } +// public string Name { +// get { return CurDir.Name; } +// } +// +// void onDirUp(object sender, MouseButtonEventArgs e) +// { +// +// } +// public void onMouseDown(object sender, MouseButtonEventArgs e) +// { +// Debug.WriteLine (sender.ToString ()); +// } +// +// } } diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index e69aaaca..e5313ea2 100644 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -12,22 +12,14 @@ 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; Interface currentInterface = null; - public Interface CurrentInterface { + [XmlIgnore]public Interface CurrentInterface { get { if (currentInterface == null) { currentInterface = Interface.CurrentInterface; @@ -47,6 +39,7 @@ namespace Crow public event EventHandler<ValueChangeEventArgs> ValueChanged; public virtual void NotifyValueChanged(string MemberName, object _value) { + //Debug.WriteLine ("Value changed: {0}->{1} = {2}", this, MemberName, _value); ValueChanged.Raise(this, new ValueChangeEventArgs(MemberName, _value)); } #endregion @@ -87,7 +80,7 @@ namespace Crow Size minimumSize = "0,0"; bool cacheEnabled = false; bool clipToClientRect = true; - object dataSource; + protected object dataSource; string style; #endregion @@ -132,11 +125,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 { @@ -177,6 +188,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 @@ -226,6 +240,7 @@ namespace Crow verticalAlignment = value; NotifyValueChanged("VerticalAlignment", verticalAlignment); + RegisterForLayouting (LayoutingType.Y); } } [XmlAttributeAttribute()][DefaultValue(HorizontalAlignment.Center)] @@ -237,6 +252,7 @@ namespace Crow horizontalAlignment = value; NotifyValueChanged("HorizontalAlignment", horizontalAlignment); + RegisterForLayouting (LayoutingType.X); } } [XmlAttributeAttribute()][DefaultValue(0)] @@ -373,6 +389,10 @@ namespace Crow return; hasFocus = value; + if (hasFocus) + onFocused (this, null); + else + onUnfocused (this, null); NotifyValueChanged ("HasFocus", hasFocus); } } @@ -402,6 +422,8 @@ namespace Crow set { if (background == value) return; + if (value == null) + return; background = value; NotifyValueChanged ("Background", background); RegisterForRedraw (); @@ -523,32 +545,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; } @@ -630,10 +665,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); @@ -643,7 +678,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)) { @@ -1030,9 +1084,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; @@ -1120,6 +1171,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) { @@ -1144,19 +1201,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); @@ -1191,20 +1242,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); @@ -1212,147 +1260,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) { @@ -1365,7 +1339,7 @@ namespace Crow if (reader.Name == "Style") continue; - affectMember (reader.Name, reader.Value); + //affectMember (reader.Name, reader.Value); } reader.MoveToElement (); }else @@ -1483,12 +1457,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/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/HueSelector.cs b/src/GraphicObjects/HueSelector.cs new file mode 100644 index 00000000..ec0a6f2c --- /dev/null +++ b/src/GraphicObjects/HueSelector.cs @@ -0,0 +1,143 @@ +// +// HueSelector.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.Serialization; +using System.ComponentModel; +using Cairo; + +namespace Crow +{ + public class HueSelector : ColorSelector + { + public HueSelector () : base() + { + } + + Orientation _orientation; + double hue; + + [XmlAttributeAttribute][DefaultValue(Orientation.Horizontal)] + public virtual Orientation Orientation + { + get { return _orientation; } + set { + if (_orientation == value) + return; + _orientation = value; + NotifyValueChanged ("Orientation", _orientation); + RegisterForGraphicUpdate (); + } + } + [XmlAttributeAttribute()] + public virtual double Hue { + get { return hue; } + set { + if (hue == value) + return; + hue = value; + + notifyHueChanged (); + updateMousePosFromHue (); + } + } + protected override void onDraw (Cairo.Context gr) + { + base.onDraw (gr); + + Rectangle r = ClientRectangle; + + Gradient.Type gt = Gradient.Type.Horizontal; + if (Orientation == Orientation.Vertical) + gt = Gradient.Type.Vertical; + + Crow.Gradient grad = new Gradient (gt); + + grad.Stops.Add (new Gradient.ColorStop (0, new Color (1, 0, 0, 1))); + grad.Stops.Add (new Gradient.ColorStop (0.167, new Color (1, 1, 0, 1))); + grad.Stops.Add (new Gradient.ColorStop (0.333, new Color (0, 1, 0, 1))); + grad.Stops.Add (new Gradient.ColorStop (0.5, new Color (0, 1, 1, 1))); + grad.Stops.Add (new Gradient.ColorStop (0.667, new Color (0, 0, 1, 1))); + grad.Stops.Add (new Gradient.ColorStop (0.833, new Color (1, 0, 1, 1))); + grad.Stops.Add (new Gradient.ColorStop (1, new Color (1, 0, 0, 1))); + + grad.SetAsSource (gr, r); + CairoHelpers.CairoRectangle (gr, r, CornerRadius); + gr.Fill(); + } + + public override void Paint (ref Context ctx) + { + base.Paint (ref ctx); + + Rectangle rb = Slot + Parent.ClientRectangle.Position; + ctx.Save (); + + ctx.Translate (rb.X, rb.Y); + + ctx.SetSourceColor (Color.White); + Rectangle r = ClientRectangle; + if (Orientation == Orientation.Horizontal) { + r.Width = 5; + r.X = mousePos.X; + } else { + r.Height = 5; + r.Y = mousePos.Y; + } + + CairoHelpers.CairoRectangle (ctx, r, 2); + ctx.SetSourceColor (Color.White); + ctx.LineWidth = 2.0; + ctx.Stroke(); + ctx.Restore (); + } + public override void OnLayoutChanges (LayoutingType layoutType) + { + base.OnLayoutChanges (layoutType); + + if (Orientation == Orientation.Horizontal) { + if (layoutType == LayoutingType.Width) + updateMousePosFromHue (); + } else if (layoutType == LayoutingType.Height) + updateMousePosFromHue (); + } + protected override void updateMouseLocalPos (Point mPos) + { + base.updateMouseLocalPos (mPos); + if (Orientation == Orientation.Horizontal) + hue = (double)mousePos.X / (double)ClientRectangle.Width; + else + hue = (double)mousePos.Y / (double)ClientRectangle.Height; + notifyHueChanged (); + } + void updateMousePosFromHue(){ + if (Orientation == Orientation.Horizontal) + mousePos.X = (int)Math.Floor(hue * (double)ClientRectangle.Width); + else + mousePos.Y = (int)Math.Floor(hue * (double)ClientRectangle.Height); + CurrentInterface.EnqueueForRepaint (this); + } + void notifyHueChanged(){ + NotifyValueChanged ("Hue", hue); + NotifyValueChanged ("HueColor", new SolidColor (Color.FromHSV (hue))); + } + } +} + 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..5726afdf 100644 --- a/src/GraphicObjects/Menu.cs +++ b/src/GraphicObjects/Menu.cs @@ -65,7 +65,7 @@ namespace Crow public override void onMouseLeave (object sender, MouseMoveEventArgs e) { base.onMouseLeave (sender, e); - IsOpened = false; + //IsOpened = false; } } } diff --git a/src/GraphicObjects/MenuItem.cs b/src/GraphicObjects/MenuItem.cs index 78162fbb..9fc0b226 100644 --- a/src/GraphicObjects/MenuItem.cs +++ b/src/GraphicObjects/MenuItem.cs @@ -50,8 +50,8 @@ namespace Crow if (isOpened) { onOpen (this, null); - if (MenuRoot != null) - MenuRoot.IsOpened = true; +// if (MenuRoot != null) +// MenuRoot.IsOpened = true; }else onClose (this, null); } @@ -102,7 +102,7 @@ namespace Crow Execute.Raise (this, null); } protected virtual void onOpen (object sender, EventArgs e){ - MenuRoot.IsOpened = true; + //MenuRoot.IsOpened = true; Open.Raise (this, null); } protected virtual void onClose (object sender, EventArgs e){ diff --git a/src/GraphicObjects/Popper.cs b/src/GraphicObjects/Popper.cs index 669b8d7f..1b8c0d95 100644 --- a/src/GraphicObjects/Popper.cs +++ b/src/GraphicObjects/Popper.cs @@ -175,21 +175,6 @@ 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) { @@ -201,13 +186,11 @@ namespace Crow { if (!_isPopped || _content == null) { base.onMouseLeave (sender, e); - System.Diagnostics.Debug.WriteLine ("NotPopped***popper mouse leave:"+ this.ToString()); return; } if (!_content.MouseIsIn (e.Position)) { base.onMouseLeave (sender, e); - System.Diagnostics.Debug.WriteLine ("***popper mouse leave:"+ this.ToString()); IsPopped = false; return; } 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/SaturationValueSelector.cs b/src/GraphicObjects/SaturationValueSelector.cs new file mode 100644 index 00000000..b71fa7ef --- /dev/null +++ b/src/GraphicObjects/SaturationValueSelector.cs @@ -0,0 +1,129 @@ +// +// SaturationValueSelector.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 Cairo; +using System.Xml.Serialization; + +namespace Crow +{ + public class SaturationValueSelector : ColorSelector + { + public SaturationValueSelector () : base() + { + } + + double v, s; + + [XmlAttributeAttribute()] + public virtual double V { + get { return v; } + set { + if (v == value) + return; + v = value; + NotifyValueChanged ("V", v); + mousePos.Y = (int)Math.Floor((1.0-v) * (double)ClientRectangle.Height); + + CurrentInterface.EnqueueForRepaint (this); + } + } + [XmlAttributeAttribute()] + public virtual double S { + get { return s; } + set { + if (s == value) + return; + s = value; + NotifyValueChanged ("S", s); + mousePos.X = (int)Math.Floor(s * (double)ClientRectangle.Width); + CurrentInterface.EnqueueForRepaint (this); + } + } + protected override void onDraw (Cairo.Context gr) + { + base.onDraw (gr); + + Rectangle r = ClientRectangle; + Rectangle rGrad = r; + rGrad.Inflate (-1); + + if (Foreground != null) {//TODO:test if null should be removed + Foreground.SetAsSource (gr, r); + CairoHelpers.CairoRectangle (gr, r, CornerRadius); + gr.Fill (); + } + + Crow.Gradient grad = new Gradient (Gradient.Type.Horizontal); + grad.Stops.Add (new Gradient.ColorStop (0, new Color (1, 1, 1, 1))); + grad.Stops.Add (new Gradient.ColorStop (1, new Color (1, 1, 1, 0))); + grad.SetAsSource (gr, rGrad); + CairoHelpers.CairoRectangle (gr, r, CornerRadius); + gr.Fill(); + grad = new Gradient (Gradient.Type.Vertical); + grad.Stops.Add (new Gradient.ColorStop (0, new Color (0, 0, 0, 0))); + grad.Stops.Add (new Gradient.ColorStop (1, new Color (0, 0, 0, 1))); + grad.SetAsSource (gr, rGrad); + CairoHelpers.CairoRectangle (gr, r, CornerRadius); + gr.Fill(); + } + + public override void Paint (ref Context ctx) + { + base.Paint (ref ctx); + + Rectangle rb = Slot + Parent.ClientRectangle.Position; + ctx.Save (); + + ctx.Translate (rb.X, rb.Y); + + ctx.SetSourceColor (Color.White); + ctx.Arc (mousePos.X, mousePos.Y, 3.0, 0, Math.PI * 2.0); + ctx.LineWidth = 1.0; + ctx.Stroke (); + + ctx.Restore (); + } + + protected override void updateMouseLocalPos (Point mPos) + { + base.updateMouseLocalPos (mPos); + + Rectangle cb = ClientRectangle; + s = (double)mousePos.X / (double)cb.Width; + v = 1.0 - (double)mousePos.Y / (double)cb.Height; + NotifyValueChanged ("S", s); + NotifyValueChanged ("V", v); + } + public override void OnLayoutChanges (LayoutingType layoutType) + { + base.OnLayoutChanges (layoutType); + switch (layoutType) { + case LayoutingType.Width: + mousePos.X = (int)Math.Floor(s * (double)ClientRectangle.Width); + break; + case LayoutingType.Height: + mousePos.Y = (int)Math.Floor((1.0-v) * (double)ClientRectangle.Height); + break; + } + } + } +} + diff --git a/src/GraphicObjects/TabItem.cs b/src/GraphicObjects/TabItem.cs index e14a7815..cd57b221 100644 --- a/src/GraphicObjects/TabItem.cs +++ b/src/GraphicObjects/TabItem.cs @@ -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); 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 500e5ea6..3c1fed9e 100644 --- a/src/GraphicObjects/TemplatedGroup.cs +++ b/src/GraphicObjects/TemplatedGroup.cs @@ -36,13 +36,12 @@ namespace Crow #endregion protected Group items; - string _itemTemplate; + string _itemTemplate, _dataTest; #region events public event EventHandler<SelectionChangeEventArgs> SelectedItemChanged; #endregion - Group _list; IList data; int _selectedIndex; Color selBackground, selForeground; @@ -56,6 +55,9 @@ namespace Crow //but then i should test if null in msil gen public Dictionary<string, ItemTemplate> ItemTemplates = new Dictionary<string, Crow.ItemTemplate>(); + /// <summary> + /// Default item template + /// </summary> [XmlAttributeAttribute][DefaultValue("#Crow.Templates.ItemTemplate.goml")] public string ItemTemplate { get { return _itemTemplate; } @@ -81,6 +83,24 @@ namespace Crow else NotifyValueChanged ("HasChildren", true); } + /// <summary> + /// Use to define condition on Data item for selecting among ItemTemplates. + /// Default value is 'TypeOf' for selecting Template depending on Type of Data. + /// Other possible values are properties of Data + /// </summary> + /// <value>The data property test.</value> + [XmlAttributeAttribute][DefaultValue("TypeOf")] + public string DataTest { + get { return _dataTest; } + set { + if (value == _dataTest) + return; + + _dataTest = value; + + NotifyValueChanged("DataTest", _dataTest); + } + } #endregion public virtual List<GraphicObject> Items{ get { return items.Children; }} @@ -100,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 { @@ -113,7 +135,7 @@ namespace Crow NotifyValueChanged ("Data", data); - lock (CurrentInterface.UpdateMutex) + //lock (CurrentInterface.UpdateMutex) ClearItems (); if (data == null) @@ -127,6 +149,7 @@ namespace Crow NotifyValueChanged ("SelectedIndex", _selectedIndex); NotifyValueChanged ("SelectedItem", SelectedItem); SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem)); + NotifyValueChanged ("HasItems", HasItems); } } @@ -160,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); @@ -281,6 +306,7 @@ namespace Crow if (cancelLoading) return; loadPage (i); + Thread.Sleep (1); } } void cancelLoadingThread(){ @@ -299,20 +325,25 @@ 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; - page.Name = "page" + pageNum; + }else + page = Activator.CreateInstance (items.GetType ()) as Group; -// //reset size to fit in the dir of the stacking -// //because items total size is forced to approx size -// if (_gsList.Orientation == Orientation.Horizontal) { -// page.Width = Measure.Fit; - page.BindMember ("Height", "../HeightPolicy"); - page.BindMember ("Width", "../WidthPolicy"); -// } else { -// page.Height = Measure.Fit; -// page.BindMember ("Width", "../WidthPolicy"); -// } + page.Name = "page" + pageNum; for (int i = (pageNum - 1) * itemPerPage; i < pageNum * itemPerPage; i++) { if (i >= data.Count) @@ -321,10 +352,10 @@ namespace Crow return; loadItem (i, page); - - //g.LogicalParent = this; } + if (page == items) + return; lock (CurrentInterface.LayoutMutex) items.AddChild (page); @@ -335,27 +366,41 @@ namespace Crow loadingTime.ElapsedMilliseconds, this.ToString()); #endif } + string getItempKey(Type dataType, object o){ + try { + return dataType.GetProperty (_dataTest).GetGetMethod ().Invoke (o, null).ToString(); + } catch (Exception ex) { + return dataType.FullName; + } + } protected void loadItem(int i, Group page){ GraphicObject g = null; ItemTemplate iTemp = null; Type dataType = data [i].GetType (); + string itempKey = dataType.FullName; - if (ItemTemplates.ContainsKey (dataType.FullName)) - iTemp = ItemTemplates [dataType.FullName]; + if (_dataTest != "TypeOf") + itempKey = getItempKey (dataType, data [i]); + + if (ItemTemplates.ContainsKey (itempKey)) + iTemp = ItemTemplates [itempKey]; else iTemp = ItemTemplates ["default"]; lock (CurrentInterface.LayoutMutex) { g = iTemp.CreateInstance(CurrentInterface); page.AddChild (g); - g.DataSource = data [i]; + registerItemClick (g); } - registerItemClick (g); - if (iTemp.Expand != null && g is Expandable) { (g as Expandable).Expand += iTemp.Expand; - } + (g as Expandable).GetIsExpandable = iTemp.HasSubItems; + } + //g.LogicalParent = this; + + + 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/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..402df1cc 100644 --- a/src/Instantiator.cs +++ b/src/Instantiator.cs @@ -23,85 +23,962 @@ 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 static Instantiator CreateFromImlFragment(string fragment){ + public Instantiator (Type _root, InstanciatorInvoker _loader) + { + RootType = _root; + loader = _loader; + } + 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); - #endif + + 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; } - public Instantiator (Type _root, InstanciatorInvoker _loader) + void emitLoader (XmlTextReader reader, Context ctx) { - RootType = _root; - loader = _loader; + 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 - public GraphicObject CreateInstance(Interface iface){ - GraphicObject tmp = (GraphicObject)Activator.CreateInstance(RootType); - loader (tmp, iface); - return tmp; + void readPropertyBinding (Context ctx, string sourceMember, string expression) + { + NodeAddress sourceNA = ctx.CurrentNodeAddress; + BindingDefinition bindingDef = splitBindingExp (sourceNA, sourceMember, expression); + + if (bindingDef.IsDataSourceBinding)//bind on data source + emitDataSourceBindings (ctx, bindingDef); + else + ctx.StorePropertyBinding (bindingDef); } - 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); + + /// <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) + { + //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 (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 + + #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); + } + } + 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..0cff9b87 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 @@ -100,10 +101,10 @@ namespace Crow // if (g.RegisteredLayoutings != LayoutingType.None) // return; ILayoutable l = g; - while (l.Parent != null) - l = l.Parent; - if (!(l is Interface)) - return; +// while (l.Parent != null) +// l = l.Parent; +// if (!(l is Interface)) +// return; lock (DrawingQueue) { if (g.IsQueueForRedraw) @@ -312,10 +313,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 @@ -411,15 +412,9 @@ namespace Crow 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 ()); - } + g.Parent.RegisterClip (g.LastPaintedSlot); + if (g.getSlot () != g.LastPaintedSlot) + g.Parent.RegisterClip (g.getSlot ()); } } #if MEASURE_TIME @@ -499,7 +494,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 +513,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 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 3b19efc9..2257ae8e 100644 --- a/src/LayoutingQueueItem.cs +++ b/src/LayoutingQueueItem.cs @@ -96,7 +96,9 @@ namespace Crow if (Layoutable.Parent == null) {//TODO:improve this //cancel layouting for object without parent, maybe some were in queue when //removed from a listbox + #if DEBUG_LAYOUTING Debug.WriteLine ("ERROR: processLayouting, no parent for: " + this.ToString ()); + #endif return; } #if DEBUG_LAYOUTING diff --git a/src/Rectangle.cs b/src/Rectangle.cs index 9a4ef396..3ceb396e 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; @@ -262,7 +263,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..9f3c3337 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(); } 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; } diff --git a/src/SolidColor.cs b/src/SolidColor.cs index 2b6f7e71..d214f592 100644 --- a/src/SolidColor.cs +++ b/src/SolidColor.cs @@ -12,7 +12,7 @@ namespace Crow { public class SolidColor : Fill { - Color color = Color.Transparent; + public Color color = Color.Transparent; #region CTOR public SolidColor(Color c) { |