From: Jean-Philippe Bruyère Date: Tue, 11 Feb 2020 09:49:09 +0000 (+0100) Subject: wip X-Git-Tag: v0.9.5-beta~131 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=701e6c825abbfb431bda3f6692e32a5f12e7307b;p=jp%2Fcrow.git wip --- diff --git a/Crow/Crow.csproj b/Crow/Crow.csproj index 27feb880..521683f3 100644 --- a/Crow/Crow.csproj +++ b/Crow/Crow.csproj @@ -3,7 +3,7 @@ netstandard2.0 - $(CrowVersion) + 0.8.0 $(CrowVersion) C# Rapid Open Widget Toolkit @@ -36,7 +36,7 @@ full - DEBUG;TRACE;MEASURE_TIME;_DEBUG_DISPOSE;_DEBUG_BINDING;DESIGN_MODE;_DEBUG_CLIP_RECTANGLE;_DEBUG_FOCUS;_DEBUG_DRAGNDROP;_DEBUG_LOG + DEBUG;TRACE;MEASURE_TIME;_DEBUG_DISPOSE;_DEBUG_BINDING;DESIGN_MODE;_DEBUG_CLIP_RECTANGLE;_DEBUG_FOCUS;DEBUG_DRAGNDROP;_DEBUG_LOG true diff --git a/Crow/src/BmpPicture.cs b/Crow/src/BmpPicture.cs deleted file mode 100644 index a67f66f1..00000000 --- a/Crow/src/BmpPicture.cs +++ /dev/null @@ -1,185 +0,0 @@ -// -// BmpPicture.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.IO; -using System.Runtime.InteropServices; -using Crow.Cairo; - -namespace Crow -{ - - /// - /// Derived from FILL for loading and drawing bitmaps in the interface - /// - public class BmpPicture : Picture - { - byte[] image = null; - - #region CTOR - /// - /// Initializes a new instance of BmpPicture. - /// - public BmpPicture () - {} - /// - /// Initializes a new instance of BmpPicture by loading the image pointed by the path argument - /// - /// image path, may be embedded - public BmpPicture (string path) : base(path) - { - Load (); - } - #endregion - /// - /// load the image for rendering from the path given as argument - /// - /// image path, may be embedded - void Load () - { - if (sharedResources.ContainsKey (Path)) { - sharedPicture sp = sharedResources [Path]; - image = (byte[])sp.Data; - Dimensions = sp.Dims; - return; - } - using (Stream stream = Interface.StaticGetStreamFromPath (Path)) { - using (StbImage stbi = new StbImage (stream)) { - image = new byte [stbi.Size]; - //rgba to argb for cairo. - for (int i = 0; i < stbi.Size; i+=4) { - image [i] = Marshal.ReadByte (stbi.Handle, i + 2); - image [i + 1] = Marshal.ReadByte (stbi.Handle, i + 1); - image [i + 2] = Marshal.ReadByte (stbi.Handle, i); - image [i + 3] = Marshal.ReadByte (stbi.Handle, i + 3); - } - Dimensions = new Size (stbi.Width, stbi.Height); - } - - //loadBitmap (new System.Drawing.Bitmap (stream)); - } - sharedResources [Path] = new sharedPicture (image, Dimensions); - } - - - - //load image via System.Drawing.Bitmap, cairo load png only - /*void loadBitmap (System.Drawing.Bitmap bitmap) - { - if (bitmap == null) - return; - - System.Drawing.Imaging.BitmapData data = bitmap.LockBits - (new System.Drawing.Rectangle (0, 0, bitmap.Width, bitmap.Height), - System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - - Dimensions = new Size (bitmap.Width, bitmap.Height); - - int stride = data.Stride; - int bitmapSize = Math.Abs (data.Stride) * bitmap.Height; - - image = new byte[bitmapSize]; - System.Runtime.InteropServices.Marshal.Copy (data.Scan0, image, 0, bitmapSize); - - bitmap.UnlockBits (data); - }*/ - - #region implemented abstract members of Fill - - public override void SetAsSource (Context ctx, Rectangle bounds = default(Rectangle)) - { - float widthRatio = 1f; - float heightRatio = 1f; - - if (Scaled){ - widthRatio = (float)bounds.Width / Dimensions.Width; - heightRatio = (float)bounds.Height / Dimensions.Height; - } - - if (KeepProportions) { - if (widthRatio < heightRatio) - heightRatio = widthRatio; - else - widthRatio = heightRatio; - } - - using (ImageSurface tmp = new ImageSurface (Format.Argb32, bounds.Width, bounds.Height)) { - using (Context gr = new Context (tmp)) { - gr.Translate (bounds.Left, bounds.Top); - gr.Scale (widthRatio, heightRatio); - gr.Translate ((bounds.Width/widthRatio - Dimensions.Width)/2, (bounds.Height/heightRatio - Dimensions.Height)/2); - - using (ImageSurface imgSurf = new ImageSurface (image, Format.Argb32, - Dimensions.Width, Dimensions.Height, 4 * Dimensions.Width)) { - gr.SetSourceSurface (imgSurf, 0,0); - gr.Paint (); - } - } - ctx.SetSource (tmp); - } - } - #endregion - - /// - /// paint the image in the rectangle given in arguments according - /// to the Scale and keepProportion parameters. - /// - /// drawing Backend context - /// bounds of the target surface to paint - /// used for svg only - public override void Paint (Context gr, Rectangle rect, string subPart = "") - { - float widthRatio = 1f; - float heightRatio = 1f; - - if (Scaled){ - widthRatio = (float)rect.Width / Dimensions.Width; - heightRatio = (float)rect.Height / Dimensions.Height; - } - - if (KeepProportions) { - if (widthRatio < heightRatio) - heightRatio = widthRatio; - else - widthRatio = heightRatio; - } - - gr.Save (); - - gr.Translate (rect.Left,rect.Top); - gr.Scale (widthRatio, heightRatio); - gr.Translate ((rect.Width/widthRatio - Dimensions.Width)/2, (rect.Height/heightRatio - Dimensions.Height)/2); - - using (ImageSurface imgSurf = new ImageSurface (image, Format.Argb32, - Dimensions.Width, Dimensions.Height, 4 * Dimensions.Width)) { - gr.SetSourceSurface (imgSurf, 0,0); - gr.Paint (); - } - gr.Restore (); - } - } -} - diff --git a/Crow/src/Colors.cs b/Crow/src/Colors.cs index 8cec2f4e..8af93517 100644 --- a/Crow/src/Colors.cs +++ b/Crow/src/Colors.cs @@ -1,38 +1,10 @@ -// -// Colors.cs +// Copyright (c) 2013-2020 Jean-Philippe Bruyère // -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Xml.Serialization; -using System.Reflection; -using System.Diagnostics; - - namespace Crow { diff --git a/Crow/src/CompilerServices/CompilerServices.cs b/Crow/src/CompilerServices/CompilerServices.cs deleted file mode 100644 index 86e7a7a9..00000000 --- a/Crow/src/CompilerServices/CompilerServices.cs +++ /dev/null @@ -1,1047 +0,0 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) - -using System; -using System.Reflection.Emit; -using System.Reflection; -using System.Diagnostics; -using System.Linq; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Xml; -using Crow.IML; -using System.Text; - -namespace Crow.IML -{ - public static class CompilerServices - { - /// - /// known types cache, prevent rewalking all the assemblies of the domain - /// the key is the type simple name - /// - internal static Dictionary knownTypes = new Dictionary (); - /// - /// known extension methods. - /// key is type dot memberName. - /// - internal static Dictionary knownExtMethods = new Dictionary (); - - internal static MethodInfo stringEquals = typeof (string).GetMethod("Equals", new Type [3] { typeof (string), typeof (string), typeof (StringComparison) }); - internal static MethodInfo miObjToString = typeof(object).GetMethod("ToString"); - internal static MethodInfo miGetType = typeof(object).GetMethod("GetType"); - internal static MethodInfo miParseEnum = typeof(Enum).GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, - Type.DefaultBinder, new Type [] {typeof (Type), typeof (string), typeof (bool)}, null); - - internal static MethodInfo miGetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle"); - internal static MethodInfo miGetEvent = typeof(Type).GetMethod("GetEvent", new Type[] {typeof(string)}); - - 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).GetMethod("get_Item", new Type[] { typeof(Int32) }); - - internal static MethodInfo miCompileDynEventHandler = typeof(CompilerServices).GetMethod ("compileDynEventHandler", BindingFlags.Static | BindingFlags.NonPublic); - internal static MethodInfo miRemEvtHdlByName = typeof(CompilerServices).GetMethod("removeEventHandlerByName", BindingFlags.Static | BindingFlags.NonPublic); - internal static MethodInfo miRemEvtHdlByTarget = typeof(CompilerServices).GetMethod("removeEventHandlerByTarget", BindingFlags.Static | BindingFlags.NonPublic); - internal static MethodInfo miGetMethInfoWithRefx = typeof(CompilerServices).GetMethod ("getMethodInfoWithReflexion", BindingFlags.Static | BindingFlags.NonPublic); - internal static MethodInfo miGetMembIinfoWithRefx = typeof(CompilerServices).GetMethod("getMemberInfoWithReflexion", BindingFlags.Static | BindingFlags.NonPublic); - internal static MethodInfo miSetValWithRefx = typeof(CompilerServices).GetMethod("setValueWithReflexion", BindingFlags.Static | BindingFlags.NonPublic); - internal static MethodInfo miGetValWithRefx = typeof(CompilerServices).GetMethod("getValueWithReflexion", BindingFlags.Static | BindingFlags.NonPublic); - internal static MethodInfo miCreateDel = typeof(CompilerServices).GetMethod ("createDel", BindingFlags.Static | BindingFlags.NonPublic); - internal static MethodInfo miGetImplOp = typeof(CompilerServices).GetMethod ("getImplicitOp", BindingFlags.Static | BindingFlags.NonPublic); - internal static MethodInfo miGetDataTypeAndFetch = typeof(CompilerServices).GetMethod("getDataTypeAndFetch", BindingFlags.Static | BindingFlags.NonPublic); - - - internal static MethodInfo miGoUpLevels = typeof(CompilerServices).GetMethod("goUpNbLevels", BindingFlags.Static | BindingFlags.NonPublic); - - 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(Widget).GetField ("IFace", BindingFlags.Public | BindingFlags.Instance); - internal static MethodInfo miFindByName = typeof (Widget).GetMethod ("FindByName"); - internal static MethodInfo miGetGObjItem = typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) }); - internal static MethodInfo miLoadDefaultVals = typeof (Widget).GetMethod ("loadDefaultValues"); - internal static PropertyInfo piStyle = typeof (Widget).GetProperty ("Style"); - internal static MethodInfo miGetLogicalParent = typeof(Widget).GetProperty("LogicalParent").GetGetMethod(); - internal static MethodInfo miGetDataSource = typeof(Widget).GetProperty("DataSource").GetGetMethod (); - internal static EventInfo eiLogicalParentChanged = typeof(Widget).GetEvent("LogicalParentChanged"); - - internal static MethodInfo miIFaceLoad = typeof(Interface).GetMethod ("CreateInstance", BindingFlags.Instance | BindingFlags.Public); - internal static MethodInfo miIFaceCreateTemplateInst = typeof (Interface).GetMethod ("CreateTemplateInstance", BindingFlags.Instance | BindingFlags.Public); - internal static MethodInfo miGetITemp = typeof(Interface).GetMethod ("GetItemTemplate", BindingFlags.Instance | BindingFlags.Public); - - internal static MethodInfo miAddITemp = typeof(Dictionary).GetMethod ("set_Item", new Type[] { typeof(string), typeof(ItemTemplate) }); - internal static MethodInfo miGetITempFromDic = typeof(Dictionary).GetMethod ("get_Item", new Type[] { typeof(string) }); - internal static FieldInfo fldItemTemplates = typeof(TemplatedGroup).GetField("ItemTemplates"); - internal static MethodInfo miLoadPage = typeof(TemplatedGroup).GetMethod ("loadPage", BindingFlags.Instance | BindingFlags.NonPublic| BindingFlags.Public); - internal static MethodInfo miIsAlreadyExpanded = typeof(TemplatedGroup).GetMethod("emitHelperIsAlreadyExpanded", BindingFlags.Instance | BindingFlags.NonPublic); - - internal static MethodInfo miCreateExpDel = typeof(ItemTemplate).GetMethod ("CreateExpandDelegate"); - internal static FieldInfo fiFetchMethodName = typeof(ItemTemplate).GetField("fetchMethodName", BindingFlags.Instance | BindingFlags.NonPublic); - - #if DESIGN_MODE - internal static MethodInfo miDicStrStrAdd = typeof(Dictionary).GetMethod ("set_Item", new Type[] { typeof(string), typeof(string) }); - #endif - - #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 EventInfo eiDSChange = typeof (Widget).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 - - /// - /// Loc0 is the current graphic object and arg2 of loader is the current interface - /// - /// Il. - public static void emitSetCurInterface(ILGenerator il){ - il.Emit (OpCodes.Ldloc_0); - il.Emit (OpCodes.Ldarg_1); - il.Emit (OpCodes.Stfld, miSetCurIface); - } - - public static void EmitSetValue(ILGenerator il, PropertyInfo pi, object val){ - il.Emit (OpCodes.Ldloc_0); - - if (val == null) { - il.Emit (OpCodes.Ldnull); - il.Emit (OpCodes.Callvirt, pi.GetSetMethod ()); - return; - } - Type dvType = val.GetType (); - - if (dvType.IsValueType) { - if (pi.PropertyType.IsValueType) { - if (pi.PropertyType.IsEnum) { - if (pi.PropertyType != dvType) - throw new Exception ("Enum mismatch in default values: " + pi.PropertyType.FullName); - il.Emit (OpCodes.Ldc_I4, Convert.ToInt32 (val)); - } else { - switch (Type.GetTypeCode (dvType)) { - case TypeCode.Boolean: - if ((bool)val == true) - il.Emit (OpCodes.Ldc_I4_1); - else - il.Emit (OpCodes.Ldc_I4_0); - break; -// case TypeCode.Empty: -// break; -// case TypeCode.Object: -// break; -// case TypeCode.DBNull: -// break; -// case TypeCode.SByte: -// break; -// case TypeCode.Decimal: -// break; -// case TypeCode.DateTime: -// break; - case TypeCode.Char: - il.Emit (OpCodes.Ldc_I4, Convert.ToChar (val)); - break; - case TypeCode.Byte: - case TypeCode.Int16: - case TypeCode.Int32: - il.Emit (OpCodes.Ldc_I4, Convert.ToInt32 (val)); - break; - case TypeCode.UInt16: - case TypeCode.UInt32: - il.Emit (OpCodes.Ldc_I4, Convert.ToUInt32 (val)); - break; - case TypeCode.Int64: - il.Emit (OpCodes.Ldc_I8, Convert.ToInt64 (val)); - break; - case TypeCode.UInt64: - il.Emit (OpCodes.Ldc_I8, Convert.ToUInt64 (val)); - break; - case TypeCode.Single: - il.Emit (OpCodes.Ldc_R4, Convert.ToSingle (val)); - break; - case TypeCode.Double: - il.Emit (OpCodes.Ldc_R8, Convert.ToDouble (val)); - break; - case TypeCode.String: - il.Emit (OpCodes.Ldstr, Convert.ToString (val)); - break; - default: - il.Emit (OpCodes.Pop); - return; - } - } - } else - throw new Exception ("Expecting valuetype in default values for: " + pi.Name); - }else{ - //surely a class or struct - if (dvType != typeof(string)) - throw new Exception ("Expecting String in default values for: " + pi.Name); - if (pi.PropertyType == typeof(string)) - il.Emit (OpCodes.Ldstr, Convert.ToString (val)); - else if (pi.PropertyType.IsEnum) { - //load type of enum - il.Emit(OpCodes.Ldtoken, pi.PropertyType); - il.Emit(OpCodes.Call, CompilerServices.miGetTypeFromHandle); - //load enum value name - il.Emit (OpCodes.Ldstr, Convert.ToString (val));//TODO:implement here string format? - //load false - il.Emit (OpCodes.Ldc_I4_0); - il.Emit (OpCodes.Call, CompilerServices.miParseEnum); - - if (CompilerServices.miParseEnum.ReturnType != pi.PropertyType) - il.Emit (OpCodes.Unbox_Any, pi.PropertyType); - } else { - MethodInfo miParse = pi.PropertyType.GetMethod - ("Parse", BindingFlags.Static | BindingFlags.Public, - Type.DefaultBinder, new Type [] {typeof (string)},null); - if (miParse == null) - throw new Exception ("no Parse method found for: " + pi.PropertyType.FullName); - - il.Emit (OpCodes.Ldstr, Convert.ToString (val));//TODO:is this convert required? - il.Emit (OpCodes.Call, miParse); - - if (miParse.ReturnType != pi.PropertyType) - il.Emit (OpCodes.Unbox_Any, pi.PropertyType); - } - } - il.Emit (OpCodes.Callvirt, pi.GetSetMethod ()); - } - - #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 (uint)) - name = "ToUInt32"; - else if (targetType == typeof (long)) - name = "ToInt64"; - else if (targetType == typeof (double)) - name = "ToDouble"; - else if (targetType == typeof (float)) - name = "ToSingle"; - else if (targetType == typeof (string)) - return typeof (object).GetMethod ("ToString", Type.EmptyTypes); - else //try to find implicit convertion - throw new NotImplementedException (string.Format ("Conversion to {0} is not implemented.", targetType.Name)); - - return typeof (Convert).GetMethod (name, BindingFlags.Static | BindingFlags.Public, null, new Type [] { typeof (object) }, null); - } - #endregion - - #region Reflexion helpers - static MemberInfo getMemberInfoWithReflexion(object instance, string member){ - Type t = instance.GetType(); -#if DEBUG_BINDING_FUNC_CALLS - Console.WriteLine ($"getMemberInfoWithReflexion ({instance},{member}); type:{t}"); -#endif - MemberInfo mi = t.GetMember (member)?.FirstOrDefault(); - if (mi == null) - mi = CompilerServices.SearchExtMethod (t, member); - return mi; - } - static MethodInfo getMethodInfoWithReflexion(object instance, string method){ -#if DEBUG_BINDING_FUNC_CALLS - Console.WriteLine ($"getMethodInfoWithReflexion ({instance},{method}); type:{instance.GetType ()}"); -#endif - return instance.GetType ().GetMethod (method, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); - } - /// - /// set value, convert if required - /// - /// Destination instance - /// Value - /// Destination member - static void setValueWithReflexion(object dest, object value, string destMember){ -#if DEBUG_BINDING_FUNC_CALLS - Console.WriteLine ($"setValueWithReflexion (dest:{dest},value:{value},member:{destMember});"); -#endif - Type destType = null; - Type origType = null; - object convertedVal = null; - - MemberInfo miDest = getMemberInfoWithReflexion (dest, destMember); - - if (miDest == null) { - Debug.WriteLine ("Reverse template binding error: " + destMember + " not found in " + dest); - return; - } - - destType = CompilerServices.GetMemberInfoType (miDest); - - try { - if (value != null) { - if (destType == typeof (object))//TODO: check that test of destType is not causing problems - convertedVal = value; - else { - origType = value.GetType (); - if (destType.IsAssignableFrom (origType)) - convertedVal = Convert.ChangeType (value, destType); - else if (origType == typeof(string) & destType.IsPrimitive) - 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); - } - } - } catch (Exception ex) { - Debug.WriteLine (ex.ToString ()); - return; - } - - if (miDest.MemberType == MemberTypes.Property) - (miDest as PropertyInfo).SetValue (dest, convertedVal); - else if (miDest.MemberType == MemberTypes.Field) - (miDest as FieldInfo).SetValue (dest, convertedVal); - } - /// - /// Gets value with reflexion, return empty string ("") for string and object and return - /// default value for valueType data. - /// - static object getValueWithReflexion(object instance, MemberInfo mi){ -#if DEBUG_BINDING_FUNC_CALLS - Console.WriteLine ($"getValueWithReflexion ({instance},{mi});"); -#endif - object tmp = null; - Type dstType = null; - if (mi == null) - return null; - try { - if (mi.MemberType == MemberTypes.Property) { - PropertyInfo pi = mi as PropertyInfo; - tmp = pi.GetValue (instance); - dstType = pi.PropertyType; - }else if (mi.MemberType == MemberTypes.Field) { - FieldInfo fi = mi as FieldInfo; - tmp = fi.GetValue (instance); - dstType = fi.FieldType; - }else if (mi.MemberType == MemberTypes.Method) { - MethodInfo gi = mi as MethodInfo; - if (gi.IsStatic) - tmp = gi.Invoke(null, new object[] {instance}); - else - tmp = gi.Invoke(instance, null); - dstType = gi.ReturnType; - } - if (tmp != null) - return tmp; - if (dstType == typeof(string) || dstType == typeof (object))//TODO:object should be allowed to return null and not "" - return ""; - if (dstType.IsValueType) - return Activator.CreateInstance (dstType); - } catch (Exception ex) { - Debug.WriteLine (ex.ToString ()); - return ""; - } - - return null; - } - internal static MethodInfo SearchExtMethod (Type t, string methodName) - { - string key = t.Name + "." + methodName; - if (knownExtMethods.ContainsKey (key)) - return knownExtMethods [key]; - - //Console.WriteLine ($"*** search extension method: {t};{methodName} => key={key}"); - - MethodInfo mi = null; - mi = GetExtensionMethods (Assembly.GetEntryAssembly (), t, methodName); - if (mi == null) - mi = GetExtensionMethods (t.Module.Assembly, t, methodName); - - //add key even if mi is null to prevent searching again and again for propertyless bindings - knownExtMethods.Add (key, mi); - return mi; - } - - public static MethodInfo GetExtensionMethods (Assembly assembly, Type extendedType, string methodName) - { - foreach (Type t in assembly.GetTypes ().Where - (ty => ty.IsDefined (typeof (ExtensionAttribute), false))) { - foreach (MethodInfo mi in t.GetMethods - (BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Where - (m=> m.Name == methodName && m.IsDefined (typeof (ExtensionAttribute), false) && - m.GetParameters ().Length == 1)) { - Type curType = extendedType; - while (curType != null) { - if (mi.GetParameters () [0].ParameterType == curType) - return mi; - curType = curType.BaseType; - } - } - - } - return null; - } - /// - /// retrieve event handler in class or ancestors - /// - 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; - } - /// - /// search for an implicit conversion method in origine or destination classes - /// - 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; - } -#endregion - - /// - /// Emits tree parsing command to fetch dest instance starting from orig node - /// - internal 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++; - } - } - /// - /// Emits tree parsing commands to get child starting at root node - /// - /// MSIL generator - /// Absolute Node Address of the instance to get - internal 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); - } - /// - /// Emits msil to fetch chil instance of current GraphicObject on the stack - /// - /// Il generator - /// Parent type - /// Index of child, -1 for template root - internal 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; - } - } - - /// - /// Emit MSIL for conversion from orig type to dest type - /// - internal static void emitConvert(ILGenerator il, Type origType, Type destType){ - if (destType == typeof(object)) - 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.IsEnum || origType == typeof (Enum)) && destType.IsEnum) { - il.Emit (OpCodes.Unbox_Any, destType); - return; - } else if (origType.IsValueType) { - if (destType != origType) { - MethodInfo miIO = getImplicitOp (origType, destType); - if (miIO != null) { - System.Reflection.Emit.Label emitCreateDefault = il.DefineLabel (); - System.Reflection.Emit.Label emitContinue = il.DefineLabel (); - LocalBuilder lbStruct = il.DeclareLocal (origType); - il.Emit (OpCodes.Dup); - il.Emit (OpCodes.Brfalse, emitCreateDefault); - il.Emit (OpCodes.Unbox_Any, origType); - il.Emit (OpCodes.Br, emitContinue); - il.MarkLabel (emitCreateDefault); - il.Emit (OpCodes.Pop);//pop null value - il.Emit (OpCodes.Ldloca, lbStruct); - il.Emit (OpCodes.Initobj, origType); - il.Emit (OpCodes.Ldloc, lbStruct); - il.MarkLabel (emitContinue); - il.Emit (OpCodes.Call, miIO); - } else { - MethodInfo miconv = CompilerServices.GetConvertMethod (destType); - if (miconv.IsStatic) - il.Emit (OpCodes.Call, miconv); - else - il.Emit (OpCodes.Callvirt, miconv); - } - } else - il.Emit (OpCodes.Unbox_Any, destType);//TODO:double check this - } else { - if (destType.IsAssignableFrom (origType)) - il.Emit (OpCodes.Castclass, destType); - else { - if (origType == typeof (string)) { - //search dest type for parse method - MethodInfo miParse = destType.GetMethod - ("Parse", BindingFlags.Static | BindingFlags.Public, - Type.DefaultBinder, new Type [] { typeof (string) }, null); - if (miParse == null) - throw new Exception ("no Parse method found for: " + destType.FullName); - il.Emit (OpCodes.Call, miParse); - } - //implicit conversion can't be defined from or to object base class, - //so we will check if object underlying type is one of the implicit converter of destType - else if (origType == typeof (object)) {//test all implicit converter to destType on obj - System.Reflection.Emit.Label emitTestNextImpOp; - System.Reflection.Emit.Label emitImpOpFound = il.DefineLabel (); - foreach (MethodInfo mi in destType.GetMethods (BindingFlags.Public | BindingFlags.Static)) { - if (mi.Name == "op_Implicit") { - if (mi.GetParameters () [0].ParameterType == destType) - continue; - emitTestNextImpOp = il.DefineLabel (); - il.Emit (OpCodes.Dup); - il.Emit (OpCodes.Isinst, mi.GetParameters () [0].ParameterType); - il.Emit (OpCodes.Brfalse, emitTestNextImpOp); - if (mi.GetParameters () [0].ParameterType.IsValueType) - il.Emit (OpCodes.Unbox_Any, mi.GetParameters () [0].ParameterType); - else - il.Emit (OpCodes.Isinst, mi.GetParameters () [0].ParameterType); - - il.Emit (OpCodes.Call, mi); - il.Emit (OpCodes.Br, emitImpOpFound); - - il.MarkLabel (emitTestNextImpOp); - } - } - //il.Emit (OpCodes.Br, emitImpOpNotFound); - il.MarkLabel (emitImpOpFound); - } else {//search both orig and dest types for implicit operators - MethodInfo miIO = getImplicitOp (origType, destType); - if (miIO != null) - il.Emit (OpCodes.Call, miIO); - } - } - } - } - /// - /// check type of current object on the stack and convert to dest type, - /// use loc_0 so store it as object!!! - /// - internal static void emitConvert(ILGenerator il, Type dstType){ - System.Reflection.Emit.Label endConvert = il.DefineLabel (); - System.Reflection.Emit.Label convert = il.DefineLabel (); - - 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); - - il.MarkLabel (convert); - - if (dstType == typeof(string)) { - System.Reflection.Emit.Label emitNullStr = il.DefineLabel (); - il.Emit (OpCodes.Dup); - il.Emit (OpCodes.Brfalse, emitNullStr); - il.Emit (OpCodes.Callvirt, CompilerServices.miObjToString); - il.Emit (OpCodes.Br, endConvert); - il.MarkLabel (emitNullStr); - il.Emit (OpCodes.Pop);//remove null string from stack - il.Emit (OpCodes.Ldstr, "");//replace with empty string - } else if (dstType.IsPrimitive) { - //il.Emit (OpCodes.Unbox_Any, dstType); - MethodInfo miconv = CompilerServices.GetConvertMethod(dstType); - if (miconv.IsStatic) - il.Emit(OpCodes.Call, miconv); - else - il.Emit(OpCodes.Callvirt, miconv); - } else if (dstType.IsValueType) { - il.Emit (OpCodes.Unbox_Any, dstType); - } else{ - il.Emit (OpCodes.Stloc_0); //save orig value in loc0 - //first check if not null - il.Emit (OpCodes.Ldloc_0); - il.Emit (OpCodes.Dup); - il.Emit (OpCodes.Brfalse, endConvert); - il.Emit (OpCodes.Callvirt, miGetType); - il.Emit (OpCodes.Ldtoken, dstType);//push destination property type for testing - il.Emit (OpCodes.Call, CompilerServices.miGetTypeFromHandle); - il.Emit (OpCodes.Call, miGetImplOp); - il.Emit (OpCodes.Dup); - convert = il.DefineLabel (); - il.Emit (OpCodes.Brtrue, convert); - il.Emit (OpCodes.Pop); - il.Emit (OpCodes.Ldloc_0); - il.Emit (OpCodes.Isinst, dstType); - il.Emit (OpCodes.Br, endConvert); - - il.MarkLabel (convert); - il.Emit (OpCodes.Ldnull);//null instance for invoke - il.Emit (OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Newarr, typeof (object)); - 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, typeof (object));//set the array element at index 0 - il.Emit (OpCodes.Callvirt, miMIInvoke); - } - - il.MarkLabel (endConvert); - } - - /// - /// Removes delegate from event handler by name - /// - static void removeEventHandlerByName(object instance, string eventName, string delegateName){ - Type t = instance.GetType (); - FieldInfo fiEvt = getEventHandlerField (t, eventName); - if (fiEvt == null) { -#if DEBUG_BINDING - Debug.WriteLine ("RemoveHandlerByName: Event '" + eventName + "' not found in " + instance); -#endif - 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 - } - } - } - } - /// - /// Removes delegate from event handler by searching for the object they are bond to - /// - static void removeEventHandlerByTarget(object instance, string eventName, object target){ - Type t = instance.GetType (); - FieldInfo fiEvt = 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 - } - } - } - } - /// - /// create delegate helper - /// - static Delegate createDel(object instance, Type eventType, string method){ - Type t = instance.GetType (); - MethodInfo mi = t.GetMethod (method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - if (mi == null) { - Debug.WriteLine ("Handler Method '{0}' not found in '{1}'", method, t); - return null; - } - return Delegate.CreateDelegate (eventType, instance, mi); - } - - internal static Delegate compileDynEventHandler(EventInfo sourceEvent, string expression, NodeAddress currentNode = null){ -#if DEBUG_BINDING - Debug.WriteLine ("\tCompile Event {0}: {1}", sourceEvent.Name, expression); -#endif - - Type lopType = null; - - if (currentNode == null) - lopType = sourceEvent.DeclaringType; - else - lopType = currentNode.NodeType; - -#region Retrieve EventHandler parameter type - MethodInfo evtInvoke = sourceEvent.EventHandlerType.GetMethod ("Invoke"); - ParameterInfo [] evtParams = evtInvoke.GetParameters (); - Type handlerArgsType = evtParams [1].ParameterType; -#endregion - - Type [] args = { typeof (object), handlerArgsType }; - DynamicMethod dm = new DynamicMethod ("dyn_eventHandler", - typeof(void), - args, true); - ILGenerator il = dm.GetILGenerator (256); - il.Emit (OpCodes.Nop); - - string [] srcLines = expression.Trim ().Split (new char [] { ';' }); - - foreach (string srcLine in srcLines) { - if (string.IsNullOrEmpty (srcLine)) - continue; - string [] operandes = srcLine.Trim ().Split (new char [] { '=' }); - if (operandes.Length != 2) //not an affectation - throw new NotSupportedException (); - - System.Reflection.Emit.Label cancel = il.DefineLabel (); - System.Reflection.Emit.Label cancelFinalSet = il.DefineLabel (); - System.Reflection.Emit.Label success = il.DefineLabel (); - - BindingMember lop = new BindingMember (operandes [0].Trim ()); - BindingMember rop = new BindingMember (operandes [1].Trim ()); - - il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node - -#region Left operande - PropertyInfo lopPI = null; - - //in dyn handler, no datasource binding, so single name in expression are also handled as current node property - if (lop.IsSingleName) - lopPI = lopType.GetProperty (lop.Tokens [0]); - else if (lop.IsCurrentNodeProperty) - lopPI = lopType.GetProperty (lop.Tokens [1]); - else - lop.emitGetTarget (il, cancel); -#endregion - -#region RIGHT OPERANDES - if (rop.IsStringConstant){ - il.Emit (OpCodes.Ldstr, rop.Tokens[0]); - lop.emitSetProperty (il); - }else if (rop.IsSingleName && rop.Tokens[0] == "this"){ - il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node - lop.emitSetProperty (il); - }else if (rop.LevelsUp ==0 && !string.IsNullOrEmpty(rop.Tokens[0])) {//parsable constant depending on lop type - //if left operand is member of current node, it's easy to fetch type, else we should use reflexion in msil - if (lopPI == null){//accept GraphicObj members, but it's restricive - //TODO: we should get the parse method by reflexion, or something else - lopPI = typeof(Widget).GetProperty (lop.Tokens [lop.Tokens.Length-1]); - if (lopPI == null) - throw new NotSupportedException (); - } - - MethodInfo lopParseMi = CompilerServices.miParseEnum; - if (lopPI.PropertyType.IsEnum){ - //load type of enum - il.Emit(OpCodes.Ldtoken, lopPI.PropertyType); - il.Emit(OpCodes.Call, CompilerServices.miGetTypeFromHandle); - //load enum value name - il.Emit (OpCodes.Ldstr, operandes [1].Trim ()); - //load false - il.Emit (OpCodes.Ldc_I4_0); - }else{ - lopParseMi = lopPI.PropertyType.GetMethod ("Parse"); - if (lopParseMi == null) - throw new Exception (string.Format - ("IML: no static 'Parse' method found in: {0}", lopPI.PropertyType.Name)); - - il.Emit (OpCodes.Ldstr, operandes [1].Trim ()); - } - if (lopParseMi.IsStatic) - il.Emit (OpCodes.Call, lopParseMi); - else - il.Emit(OpCodes.Callvirt, lopParseMi); - //il.Emit (OpCodes.Unbox_Any, lopPI.PropertyType); - //emit left operand assignment - il.Emit (OpCodes.Callvirt, lopPI.GetSetMethod()); - } else {//tree parsing and propert gets - il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node - - rop.emitGetTarget (il, cancelFinalSet); - rop.emitGetProperty (il, cancelFinalSet); - lop.emitSetProperty (il); - } -#endregion - - il.Emit (OpCodes.Br, success); - - il.MarkLabel (cancelFinalSet); - il.Emit (OpCodes.Pop); //pop null MemberInfo on the stack causing cancelation - il.MarkLabel (cancel); - il.Emit (OpCodes.Pop); //pop null instance on the stack causing cancelation - il.MarkLabel (success); - } - - il.Emit (OpCodes.Ret); - - return dm.CreateDelegate (sourceEvent.EventHandlerType); - } - - /// - /// MSIL helper, go n levels up - /// - /// true, if logical parents are not null - /// Start Instance - /// Levels to go upward - internal static ILayoutable goUpNbLevels(ILayoutable instance, int levelCount){ - ILayoutable tmp = instance; - int i = 0; - while (tmp != null && i < levelCount) { - tmp = tmp.LogicalParent; - i++; - } - return tmp; - } - /// - /// Splits expression on semicolon but ignore those between accolades - /// - internal static string[] splitOnSemiColumnOutsideAccolades (string expression){ - List exps = new List(); - int accCount = 0; - int expPtr = 0; - for (int c = 0; c < expression.Length; c++) { - switch (expression[c]){ - case '{': - accCount++; - break; - 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 (); - } - /// - /// Try to get the type named strDataType, search first in crow assembly then in - /// entry assembly. - /// - /// the corresponding type object if found - /// type name - internal static Type getTypeFromName (string strDataType){ - if (knownTypes.ContainsKey (strDataType)) - return knownTypes [strDataType]; - Type dataType = Type.GetType(strDataType); - if (dataType != null) { - knownTypes.Add (strDataType, dataType); - return dataType; - } - foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies ()) { - if (a.IsDynamic) - continue; - foreach (Type expT in a.GetExportedTypes ()) { - if (expT.Name != strDataType && expT.FullName != strDataType) - continue; - if (!knownTypes.ContainsKey(strDataType)) - knownTypes.Add (strDataType, expT); - return expT; - } - } - knownTypes.Add (strDataType, null); - return null; - } - - //get value from member of object - internal static object getDataTypeAndFetch (object data, string fetchMethod){ - Type dataType = data.GetType(); - //Console.WriteLine ($"get data type and fetch {data}.{fetchMethod}"); - MethodInfo miGetDatas = dataType.GetMethod (fetchMethod, new Type[] {}); - if (miGetDatas == null) - miGetDatas = CompilerServices.SearchExtMethod (dataType, fetchMethod); - - if (miGetDatas == null) {//in last resort, search among properties - PropertyInfo piDatas = dataType.GetProperty (fetchMethod); - if (piDatas == null) { - FieldInfo fiDatas = dataType.GetField (fetchMethod); - if (fiDatas == null)//and among fields - throw new Exception ("Fetch data member not found in ItemTemplate: " + fetchMethod); - return fiDatas.GetValue (data); - } - miGetDatas = piDatas.GetGetMethod (); - if (miGetDatas == null) - throw new Exception ("Read only property for fetching data in ItemTemplate: " + fetchMethod); - } - return miGetDatas.IsStatic ? - miGetDatas.Invoke (null, new object [] { data }) : miGetDatas.Invoke (data, null); - } - //TODO:memberinfo found here must be cached - internal static object getValue (Type dataType, object data, string member) - { - //Console.WriteLine ($"get value: {dataType} ; {data} ; {member}"); - - MethodInfo miGetDatas = dataType.GetMethod (member, new Type [] { }); - if (miGetDatas != null) - return miGetDatas.Invoke (data, null); - MemberInfo mbi = dataType.GetMember (member).FirstOrDefault (); - if (mbi == null) { - MethodInfo miExt = CompilerServices.SearchExtMethod (dataType, member); - if (miExt == null)//and among fields - throw new Exception ($"member {member} not found in {dataType}"); - return miExt.Invoke (null, new object [] { data }); - } - if (mbi.MemberType == MemberTypes.Property) { - miGetDatas = (mbi as PropertyInfo)?.GetGetMethod (); - if (miGetDatas == null) - throw new Exception ($"no getter found for property {member} in {dataType}"); - return miGetDatas.Invoke (data, null); - } - - FieldInfo fi = mbi as FieldInfo; - if (fi == null) - throw new Exception ($"member {member} not found in {dataType}"); - - return fi.GetValue (data); - } - internal static MemberInfo GetMemberInfo (Type dataType, string member, out Type returnType) - { - MethodInfo miGetDatas = dataType.GetMethod (member, new Type [] { }); - if (miGetDatas != null) { - returnType = miGetDatas.ReturnType; - return miGetDatas; - } - - MemberInfo mbi = dataType.GetMember (member).FirstOrDefault (); - if (mbi == null) - miGetDatas = CompilerServices.SearchExtMethod (dataType, member); - else { - if (mbi is FieldInfo) { - FieldInfo fi = mbi as FieldInfo; - returnType = fi.FieldType; - return mbi; - } - if (mbi.MemberType == MemberTypes.Property) - miGetDatas = (mbi as PropertyInfo).GetGetMethod (); - else - miGetDatas = mbi as MethodInfo; - } - returnType = miGetDatas?.ReturnType; - return miGetDatas; - - } - internal static void emitGetMemberValue (ILGenerator il, Type dataType, MemberInfo mi) - { - switch (mi.MemberType) { - case MemberTypes.Method: - MethodInfo mim = mi as MethodInfo; - if (mim.IsStatic) - il.Emit (OpCodes.Call, mim); - else - il.Emit (OpCodes.Callvirt, mim); - break; - case MemberTypes.Field: - il.Emit (OpCodes.Ldfld, mi as FieldInfo); - break; - } - } - //object is already on the stack - internal static void emitGetMemberValue (ILGenerator il, Type dataType, string member) - { - MethodInfo miGetDatas = dataType.GetMethod (member, new Type [] { }); - if (miGetDatas != null) { - il.Emit (OpCodes.Callvirt, miGetDatas); - return; - } - MemberInfo mbi = dataType.GetMember (member).FirstOrDefault (); - if (mbi == null) { - MethodInfo miExt = CompilerServices.SearchExtMethod (dataType, member); - if (miExt == null)//and among fields - throw new Exception ($"member {member} not found in {dataType}"); - il.Emit (OpCodes.Call, miExt); - return; - } - if (mbi.MemberType == MemberTypes.Property) { - miGetDatas = (mbi as PropertyInfo)?.GetGetMethod (); - if (miGetDatas == null) - throw new Exception ($"no getter found for property {member} in {dataType}"); - il.Emit (OpCodes.Callvirt, miGetDatas); - return; - } - - FieldInfo fi = mbi as FieldInfo; - if (fi == null) - throw new Exception ($"member {member} not found in {dataType}"); - - il.Emit (OpCodes.Ldfld, fi); - } - - //helper to get member info underlying type. - internal static Type GetMemberInfoType (MemberInfo mi) => - mi.MemberType == MemberTypes.Field ? (mi as FieldInfo).FieldType : - mi.MemberType == MemberTypes.Property ? (mi as PropertyInfo).PropertyType : - mi.MemberType == MemberTypes.Method ? (mi as MethodInfo).ReturnType : null; - } -} - diff --git a/Crow/src/ExtensionsMethods.cs b/Crow/src/ExtensionsMethods.cs index 00157371..69edfe8e 100644 --- a/Crow/src/ExtensionsMethods.cs +++ b/Crow/src/ExtensionsMethods.cs @@ -1,28 +1,6 @@ -// -// ExtensionsMethods.cs +// Copyright (c) 2013-2020 Jean-Philippe Bruyère // -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.Linq.Expressions; diff --git a/Crow/src/Fill/BmpPicture.cs b/Crow/src/Fill/BmpPicture.cs new file mode 100644 index 00000000..a67f66f1 --- /dev/null +++ b/Crow/src/Fill/BmpPicture.cs @@ -0,0 +1,185 @@ +// +// BmpPicture.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.IO; +using System.Runtime.InteropServices; +using Crow.Cairo; + +namespace Crow +{ + + /// + /// Derived from FILL for loading and drawing bitmaps in the interface + /// + public class BmpPicture : Picture + { + byte[] image = null; + + #region CTOR + /// + /// Initializes a new instance of BmpPicture. + /// + public BmpPicture () + {} + /// + /// Initializes a new instance of BmpPicture by loading the image pointed by the path argument + /// + /// image path, may be embedded + public BmpPicture (string path) : base(path) + { + Load (); + } + #endregion + /// + /// load the image for rendering from the path given as argument + /// + /// image path, may be embedded + void Load () + { + if (sharedResources.ContainsKey (Path)) { + sharedPicture sp = sharedResources [Path]; + image = (byte[])sp.Data; + Dimensions = sp.Dims; + return; + } + using (Stream stream = Interface.StaticGetStreamFromPath (Path)) { + using (StbImage stbi = new StbImage (stream)) { + image = new byte [stbi.Size]; + //rgba to argb for cairo. + for (int i = 0; i < stbi.Size; i+=4) { + image [i] = Marshal.ReadByte (stbi.Handle, i + 2); + image [i + 1] = Marshal.ReadByte (stbi.Handle, i + 1); + image [i + 2] = Marshal.ReadByte (stbi.Handle, i); + image [i + 3] = Marshal.ReadByte (stbi.Handle, i + 3); + } + Dimensions = new Size (stbi.Width, stbi.Height); + } + + //loadBitmap (new System.Drawing.Bitmap (stream)); + } + sharedResources [Path] = new sharedPicture (image, Dimensions); + } + + + + //load image via System.Drawing.Bitmap, cairo load png only + /*void loadBitmap (System.Drawing.Bitmap bitmap) + { + if (bitmap == null) + return; + + System.Drawing.Imaging.BitmapData data = bitmap.LockBits + (new System.Drawing.Rectangle (0, 0, bitmap.Width, bitmap.Height), + System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + + Dimensions = new Size (bitmap.Width, bitmap.Height); + + int stride = data.Stride; + int bitmapSize = Math.Abs (data.Stride) * bitmap.Height; + + image = new byte[bitmapSize]; + System.Runtime.InteropServices.Marshal.Copy (data.Scan0, image, 0, bitmapSize); + + bitmap.UnlockBits (data); + }*/ + + #region implemented abstract members of Fill + + public override void SetAsSource (Context ctx, Rectangle bounds = default(Rectangle)) + { + float widthRatio = 1f; + float heightRatio = 1f; + + if (Scaled){ + widthRatio = (float)bounds.Width / Dimensions.Width; + heightRatio = (float)bounds.Height / Dimensions.Height; + } + + if (KeepProportions) { + if (widthRatio < heightRatio) + heightRatio = widthRatio; + else + widthRatio = heightRatio; + } + + using (ImageSurface tmp = new ImageSurface (Format.Argb32, bounds.Width, bounds.Height)) { + using (Context gr = new Context (tmp)) { + gr.Translate (bounds.Left, bounds.Top); + gr.Scale (widthRatio, heightRatio); + gr.Translate ((bounds.Width/widthRatio - Dimensions.Width)/2, (bounds.Height/heightRatio - Dimensions.Height)/2); + + using (ImageSurface imgSurf = new ImageSurface (image, Format.Argb32, + Dimensions.Width, Dimensions.Height, 4 * Dimensions.Width)) { + gr.SetSourceSurface (imgSurf, 0,0); + gr.Paint (); + } + } + ctx.SetSource (tmp); + } + } + #endregion + + /// + /// paint the image in the rectangle given in arguments according + /// to the Scale and keepProportion parameters. + /// + /// drawing Backend context + /// bounds of the target surface to paint + /// used for svg only + public override void Paint (Context gr, Rectangle rect, string subPart = "") + { + float widthRatio = 1f; + float heightRatio = 1f; + + if (Scaled){ + widthRatio = (float)rect.Width / Dimensions.Width; + heightRatio = (float)rect.Height / Dimensions.Height; + } + + if (KeepProportions) { + if (widthRatio < heightRatio) + heightRatio = widthRatio; + else + widthRatio = heightRatio; + } + + gr.Save (); + + gr.Translate (rect.Left,rect.Top); + gr.Scale (widthRatio, heightRatio); + gr.Translate ((rect.Width/widthRatio - Dimensions.Width)/2, (rect.Height/heightRatio - Dimensions.Height)/2); + + using (ImageSurface imgSurf = new ImageSurface (image, Format.Argb32, + Dimensions.Width, Dimensions.Height, 4 * Dimensions.Width)) { + gr.SetSourceSurface (imgSurf, 0,0); + gr.Paint (); + } + gr.Restore (); + } + } +} + diff --git a/Crow/src/Fill/Fill.cs b/Crow/src/Fill/Fill.cs index 719002d2..6ff2bb7c 100644 --- a/Crow/src/Fill/Fill.cs +++ b/Crow/src/Fill/Fill.cs @@ -31,16 +31,15 @@ namespace Crow s.EndsWith (".gif", true, System.Globalization.CultureInfo.InvariantCulture)) return Picture.Parse (s); - return (SolidColor)SolidColor.Parse (s); + return new SolidColor((Color)Color.Parse (s)); } public static implicit operator Color(Fill c){ SolidColor sc = c as SolidColor; - return sc == null ? default(Color) : sc.color; + return sc == null ? default : sc.color; } public static implicit operator Fill(Color c){ return new SolidColor (c); } - } } diff --git a/Crow/src/Fill/Picture.cs b/Crow/src/Fill/Picture.cs new file mode 100644 index 00000000..2a71d6b5 --- /dev/null +++ b/Crow/src/Fill/Picture.cs @@ -0,0 +1,152 @@ +// +// Picture.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.IO; +using Crow.Cairo; +using System.Collections.Generic; + +namespace Crow +{ + /// + /// store data and dimensions for resource sharing + /// + internal class sharedPicture { + //TODO: restructure this whith clever conceptual classes + public object Data; + public Size Dims; + public sharedPicture (object _data, Size _dims){ + Data = _data; + Dims = _dims; + } + } + /// + /// virtual class for loading and drawing picture in the interface + /// + /// Every loaded resources are stored in a dictonary with their path as key and shared + /// among interface elements + /// + public abstract class Picture : Fill + { + /// + /// share a single store for picture resources among usage in different controls + /// + internal static Dictionary sharedResources = new Dictionary(); + + /// + /// path of the picture + /// + public string Path; + /// + /// unscaled dimensions fetched on loading + /// + public Size Dimensions; + /// + /// if true and image has to be scalled, it will be scaled in both direction + /// equaly + /// + public bool KeepProportions = false; + /// + /// allow or not the picture to be scalled on request by the painter + /// + public bool Scaled = true; + + #region CTOR + /// + /// Initializes a new instance of Picture. + /// + public Picture () + { + } + /// + /// Initializes a new instance of Picture by loading the image pointed by the path argument + /// + /// image path, may be embedded + public Picture (string path) + { + Path = path; + } + #endregion + + #region Image Loading + /// + /// load the image for rendering from the stream given as argument + /// + /// picture stream + //public abstract void Load(Interface iface, string path); + #endregion + + /// + /// abstract method to paint the image in the rectangle given in arguments according + /// to the Scale and keepProportion parameters. + /// + /// drawing Backend context + /// bounds of the target surface to paint + /// used for svg only + public abstract void Paint(Context ctx, Rectangle rect, string subPart = ""); + + #region Operators + public static implicit operator Picture(string path) + { + if (string.IsNullOrEmpty (path)) + return null; + + Picture _pic = null; + + if (path.EndsWith (".svg", true, System.Globalization.CultureInfo.InvariantCulture)) + _pic = new SvgPicture (path); + else + _pic = new BmpPicture (path); + + return _pic; + } + public static implicit operator string(Picture _pic) + { + return _pic == null ? null : _pic.Path; + } + #endregion + + public static new object Parse(string path) + { + if (string.IsNullOrEmpty (path)) + return null; + + Picture _pic = null; + + if (path.EndsWith (".svg", true, System.Globalization.CultureInfo.InvariantCulture)) + _pic = new SvgPicture (path); + else + _pic = new BmpPicture (path); + + return _pic; + } + public override string ToString () + { + return Path; + } + } +} + diff --git a/Crow/src/Fill/SolidColor.cs b/Crow/src/Fill/SolidColor.cs new file mode 100644 index 00000000..f3e37d2b --- /dev/null +++ b/Crow/src/Fill/SolidColor.cs @@ -0,0 +1,115 @@ +// +// SolidColor.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Serialization; +using System.Reflection; +using System.Diagnostics; +using Crow.Cairo; + + + +namespace Crow +{ + public class SolidColor : Fill + { + public Color color = Color.Transparent; + #region CTOR + public SolidColor(Color c) + { + color = c; + } + #endregion + + #region implemented abstract members of Fill + public override void SetAsSource (Context ctx, Rectangle bounds = default) + { + ctx.SetSourceRGBA (color.R, color.G, color.B, color.A); + } + public static new object Parse(string s) + { + return new SolidColor((Color)s); + } + #endregion + + #region Operators + public static implicit operator Color(SolidColor c) => c.color; + public static implicit operator SolidColor(Color c) => new SolidColor (c); + + public static bool operator ==(SolidColor left, SolidColor right) => left?.color == right?.color; + public static bool operator !=(SolidColor left, SolidColor right) => left?.color != right?.color; + + public override int GetHashCode () + { + return color.GetHashCode(); + } + public override bool Equals (object obj) + { + if (obj is Color) + return color == (Color)obj; + if (obj is SolidColor) + return color == (obj as SolidColor).color; + return false; + } +// public static bool operator ==(SolidColor c, string n) +// { +// return c.color.Name == n ? true : false; +// } +// public static bool operator !=(SolidColor c, string n) +// { +// return c.color.Name == n ? false : true; +// } +// public static bool operator ==(string n, SolidColor c) +// { +// return c.color.Name == n ? true : false; +// } +// public static bool operator !=(string n, SolidColor c) +// { +// return c.color.Name == n ? false : true; +// } + public static SolidColor operator *(SolidColor c, Double f) + { + return new SolidColor(new Color(c.color.R,c.color.G,c.color.B,c.color.A * f)); + } + public static SolidColor operator +(SolidColor c1, SolidColor c2) + { + return new SolidColor(new Color(c1.color.R + c2.color.R,c1.color.G + c2.color.G,c1.color.B + c2.color.B,c1.color.A + c2.color.A)); + } + public static SolidColor operator -(SolidColor c1, SolidColor c2) + { + return new SolidColor(new Color(c1.color.R - c2.color.R,c1.color.G - c2.color.G,c1.color.B - c2.color.B,c1.color.A - c2.color.A)); + } + #endregion + + public override string ToString() + { + return color.ToString (); + } + } +} diff --git a/Crow/src/Fill/SvgPicture.cs b/Crow/src/Fill/SvgPicture.cs new file mode 100644 index 00000000..33f68955 --- /dev/null +++ b/Crow/src/Fill/SvgPicture.cs @@ -0,0 +1,152 @@ +// +// SvgPicture.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2013-2017 Jean-Philippe Bruyère +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.IO; +using Crow.Cairo; + +namespace Crow +{ + /// + /// Derived from FILL for loading and drawing SVG images in the interface + /// + public class SvgPicture : Picture + { + Rsvg.Handle hSVG; + + #region CTOR + /// + /// Initializes a new instance of SvgPicture. + /// + public SvgPicture () + {} + /// + /// Initializes a new instance of SvgPicture by loading the SVG file pointed by the path argument + /// + /// image path, may be embedded + public SvgPicture (string path) : base(path) + { + Load (); + } + #endregion + + void Load () + { + if (sharedResources.ContainsKey (Path)) { + sharedPicture sp = sharedResources [Path]; + hSVG = (Rsvg.Handle)sp.Data; + Dimensions = sp.Dims; + return; + } + using (Stream stream = Interface.StaticGetStreamFromPath (Path)) { + using (MemoryStream ms = new MemoryStream ()) { + stream.CopyTo (ms); + + hSVG = new Rsvg.Handle (ms.ToArray ()); + Dimensions = new Size (hSVG.Dimensions.Width, hSVG.Dimensions.Height); + } + } + sharedResources [Path] = new sharedPicture (hSVG, Dimensions); + } + + public void LoadSvgFragment (string fragment) { + hSVG = new Rsvg.Handle (System.Text.Encoding.Unicode.GetBytes(fragment)); + Dimensions = new Size (hSVG.Dimensions.Width, hSVG.Dimensions.Height); + } + + #region implemented abstract members of Fill + + public override void SetAsSource (Context ctx, Rectangle bounds = default(Rectangle)) + { + float widthRatio = 1f; + float heightRatio = 1f; + + if (Scaled){ + widthRatio = (float)bounds.Width / Dimensions.Width; + heightRatio = (float)bounds.Height / Dimensions.Height; + } + + if (KeepProportions) { + if (widthRatio < heightRatio) + heightRatio = widthRatio; + else + widthRatio = heightRatio; + } + + using (ImageSurface tmp = new ImageSurface (Format.Argb32, bounds.Width, bounds.Height)) { + using (Context gr = new Context (tmp)) { + gr.Translate (bounds.Left, bounds.Top); + gr.Scale (widthRatio, heightRatio); + gr.Translate ((bounds.Width/widthRatio - Dimensions.Width)/2, (bounds.Height/heightRatio - Dimensions.Height)/2); + + hSVG.RenderCairo (gr); + } + ctx.SetSource (tmp); + } + } + #endregion + + /// + /// paint the image in the rectangle given in arguments according + /// to the Scale and keepProportion parameters. + /// + /// drawing Backend context + /// bounds of the target surface to paint + /// limit rendering to svg part named 'subPart' + public override void Paint (Context gr, Rectangle rect, string subPart = "") + { + if (hSVG == null) + return; + float widthRatio = 1f; + float heightRatio = 1f; + + if (Scaled) { + widthRatio = (float)rect.Width / Dimensions.Width; + heightRatio = (float)rect.Height / Dimensions.Height; + } + if (KeepProportions) { + if (widthRatio < heightRatio) + heightRatio = widthRatio; + else + widthRatio = heightRatio; + } + + gr.Save (); + + gr.Translate (rect.Left,rect.Top); + gr.Scale (widthRatio, heightRatio); + gr.Translate (((float)rect.Width/widthRatio - Dimensions.Width)/2f, ((float)rect.Height/heightRatio - Dimensions.Height)/2f); + + if (string.IsNullOrEmpty (subPart)) + hSVG.RenderCairo (gr); + else + hSVG.RenderCairoSub (gr, "#" + subPart); + + gr.Restore (); + } + } +} + diff --git a/Crow/src/IML/CompilerServices.cs b/Crow/src/IML/CompilerServices.cs new file mode 100644 index 00000000..6ea3c957 --- /dev/null +++ b/Crow/src/IML/CompilerServices.cs @@ -0,0 +1,1102 @@ +// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Reflection.Emit; +using System.Reflection; +using System.Diagnostics; +using System.Linq; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Xml; +using Crow.IML; +using System.Text; + +namespace Crow.IML +{ + public static class CompilerServices + { + /// + /// known types cache, prevent rewalking all the assemblies of the domain + /// the key is the type simple name + /// + internal static Dictionary knownTypes = new Dictionary (); + /// + /// known extension methods. + /// key is type dot memberName. + /// + internal static Dictionary knownExtMethods = new Dictionary (); + + internal static MethodInfo stringEquals = typeof (string).GetMethod("Equals", new Type [3] { typeof (string), typeof (string), typeof (StringComparison) }); + internal static MethodInfo miObjToString = typeof(object).GetMethod("ToString"); + internal static MethodInfo miGetType = typeof(object).GetMethod("GetType"); + internal static MethodInfo miParseEnum = typeof(Enum).GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, + Type.DefaultBinder, new Type [] {typeof (Type), typeof (string), typeof (bool)}, null); + + internal static MethodInfo miGetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle"); + internal static MethodInfo miGetEvent = typeof(Type).GetMethod("GetEvent", new Type[] {typeof(string)}); + + 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).GetMethod("get_Item", new Type[] { typeof(Int32) }); + + internal static MethodInfo miCompileDynEventHandler = typeof(CompilerServices).GetMethod ("compileDynEventHandler", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miRemEvtHdlByName = typeof(CompilerServices).GetMethod("removeEventHandlerByName", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miRemEvtHdlByTarget = typeof(CompilerServices).GetMethod("removeEventHandlerByTarget", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miGetMethInfoWithRefx = typeof(CompilerServices).GetMethod ("getMethodInfoWithReflexion", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miGetMembIinfoWithRefx = typeof(CompilerServices).GetMethod("getMemberInfoWithReflexion", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miSetValWithRefx = typeof(CompilerServices).GetMethod("setValueWithReflexion", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miGetValWithRefx = typeof(CompilerServices).GetMethod("getValueWithReflexion", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miCreateDel = typeof(CompilerServices).GetMethod ("createDel", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miGetImplOp = typeof(CompilerServices).GetMethod ("getImplicitOp", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miGetDataTypeAndFetch = typeof(CompilerServices).GetMethod("getDataTypeAndFetch", BindingFlags.Static | BindingFlags.NonPublic); + + + internal static MethodInfo miGoUpLevels = typeof(CompilerServices).GetMethod("goUpNbLevels", BindingFlags.Static | BindingFlags.NonPublic); + + 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(Widget).GetField ("IFace", BindingFlags.Public | BindingFlags.Instance); + internal static MethodInfo miFindByName = typeof (Widget).GetMethod ("FindByName"); + internal static MethodInfo miGetGObjItem = typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) }); + internal static MethodInfo miLoadDefaultVals = typeof (Widget).GetMethod ("loadDefaultValues"); + internal static PropertyInfo piStyle = typeof (Widget).GetProperty ("Style"); + internal static MethodInfo miGetLogicalParent = typeof(Widget).GetProperty("LogicalParent").GetGetMethod(); + internal static MethodInfo miGetDataSource = typeof(Widget).GetProperty("DataSource").GetGetMethod (); + internal static EventInfo eiLogicalParentChanged = typeof(Widget).GetEvent("LogicalParentChanged"); + + internal static MethodInfo miIFaceLoad = typeof(Interface).GetMethod ("CreateInstance", BindingFlags.Instance | BindingFlags.Public); + internal static MethodInfo miIFaceCreateTemplateInst = typeof (Interface).GetMethod ("CreateTemplateInstance", BindingFlags.Instance | BindingFlags.Public); + internal static MethodInfo miGetITemp = typeof(Interface).GetMethod ("GetItemTemplate", BindingFlags.Instance | BindingFlags.Public); + + internal static MethodInfo miAddITemp = typeof(Dictionary).GetMethod ("set_Item", new Type[] { typeof(string), typeof(ItemTemplate) }); + internal static MethodInfo miGetITempFromDic = typeof(Dictionary).GetMethod ("get_Item", new Type[] { typeof(string) }); + internal static FieldInfo fldItemTemplates = typeof(TemplatedGroup).GetField("ItemTemplates"); + internal static MethodInfo miLoadPage = typeof(TemplatedGroup).GetMethod ("loadPage", BindingFlags.Instance | BindingFlags.NonPublic| BindingFlags.Public); + internal static MethodInfo miIsAlreadyExpanded = typeof(TemplatedGroup).GetMethod("emitHelperIsAlreadyExpanded", BindingFlags.Instance | BindingFlags.NonPublic); + + internal static MethodInfo miCreateExpDel = typeof(ItemTemplate).GetMethod ("CreateExpandDelegate"); + internal static FieldInfo fiFetchMethodName = typeof(ItemTemplate).GetField("fetchMethodName", BindingFlags.Instance | BindingFlags.NonPublic); + + #if DESIGN_MODE + internal static MethodInfo miDicStrStrAdd = typeof(Dictionary).GetMethod ("set_Item", new Type[] { typeof(string), typeof(string) }); + #endif + + #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 EventInfo eiDSChange = typeof (Widget).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 + + /// + /// Loc0 is the current graphic object and arg2 of loader is the current interface + /// + /// Il. + public static void emitSetCurInterface(ILGenerator il){ + il.Emit (OpCodes.Ldloc_0); + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Stfld, miSetCurIface); + } + + public static void EmitSetValue(ILGenerator il, PropertyInfo pi, object val){ + il.Emit (OpCodes.Ldloc_0); + + if (val == null) { + il.Emit (OpCodes.Ldnull); + il.Emit (OpCodes.Callvirt, pi.GetSetMethod ()); + return; + } + Type dvType = val.GetType (); + + if (dvType.IsValueType) { + if (pi.PropertyType.IsValueType) { + if (pi.PropertyType.IsEnum) { + if (pi.PropertyType != dvType) + throw new Exception ("Enum mismatch in default values: " + pi.PropertyType.FullName); + il.Emit (OpCodes.Ldc_I4, Convert.ToInt32 (val)); + } else { + switch (Type.GetTypeCode (dvType)) { + case TypeCode.Boolean: + if ((bool)val == true) + il.Emit (OpCodes.Ldc_I4_1); + else + il.Emit (OpCodes.Ldc_I4_0); + break; +// case TypeCode.Empty: +// break; +// case TypeCode.Object: +// break; +// case TypeCode.DBNull: +// break; +// case TypeCode.SByte: +// break; +// case TypeCode.Decimal: +// break; +// case TypeCode.DateTime: +// break; + case TypeCode.Char: + il.Emit (OpCodes.Ldc_I4, Convert.ToChar (val)); + break; + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.Int32: + il.Emit (OpCodes.Ldc_I4, Convert.ToInt32 (val)); + break; + case TypeCode.UInt16: + case TypeCode.UInt32: + il.Emit (OpCodes.Ldc_I4, Convert.ToUInt32 (val)); + break; + case TypeCode.Int64: + il.Emit (OpCodes.Ldc_I8, Convert.ToInt64 (val)); + break; + case TypeCode.UInt64: + il.Emit (OpCodes.Ldc_I8, Convert.ToUInt64 (val)); + break; + case TypeCode.Single: + il.Emit (OpCodes.Ldc_R4, Convert.ToSingle (val)); + break; + case TypeCode.Double: + il.Emit (OpCodes.Ldc_R8, Convert.ToDouble (val)); + break; + case TypeCode.String: + il.Emit (OpCodes.Ldstr, Convert.ToString (val)); + break; + default: + il.Emit (OpCodes.Pop); + return; + } + } + } else + throw new Exception ("Expecting valuetype in default values for: " + pi.Name); + }else{ + //surely a class or struct + if (dvType != typeof(string)) + throw new Exception ("Expecting String in default values for: " + pi.Name); + if (pi.PropertyType == typeof(string)) + il.Emit (OpCodes.Ldstr, Convert.ToString (val)); + else if (pi.PropertyType.IsEnum) { + //load type of enum + il.Emit(OpCodes.Ldtoken, pi.PropertyType); + il.Emit(OpCodes.Call, CompilerServices.miGetTypeFromHandle); + //load enum value name + il.Emit (OpCodes.Ldstr, Convert.ToString (val));//TODO:implement here string format? + //load false + il.Emit (OpCodes.Ldc_I4_0); + il.Emit (OpCodes.Call, CompilerServices.miParseEnum); + + if (CompilerServices.miParseEnum.ReturnType != pi.PropertyType) + il.Emit (OpCodes.Unbox_Any, pi.PropertyType); + } else { + MethodInfo miParse = pi.PropertyType.GetMethod + ("Parse", BindingFlags.Static | BindingFlags.Public, + Type.DefaultBinder, new Type [] {typeof (string)},null); + if (miParse == null) + throw new Exception ("no Parse method found for: " + pi.PropertyType.FullName); + + il.Emit (OpCodes.Ldstr, Convert.ToString (val));//TODO:is this convert required? + il.Emit (OpCodes.Call, miParse); + + if (miParse.ReturnType != pi.PropertyType) + il.Emit (OpCodes.Unbox_Any, pi.PropertyType); + } + } + il.Emit (OpCodes.Callvirt, pi.GetSetMethod ()); + } + + #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 (uint)) + name = "ToUInt32"; + else if (targetType == typeof (long)) + name = "ToInt64"; + else if (targetType == typeof (double)) + name = "ToDouble"; + else if (targetType == typeof (float)) + name = "ToSingle"; + else if (targetType == typeof (string)) + return typeof (object).GetMethod ("ToString", Type.EmptyTypes); + else //try to find implicit convertion + throw new NotImplementedException (string.Format ("Conversion to {0} is not implemented.", targetType.Name)); + + return typeof (Convert).GetMethod (name, BindingFlags.Static | BindingFlags.Public, null, new Type [] { typeof (object) }, null); + } + #endregion + + #region Reflexion helpers + static MemberInfo getMemberInfoWithReflexion(object instance, string member){ + Type t = instance.GetType(); +#if DEBUG_BINDING_FUNC_CALLS + Console.WriteLine ($"getMemberInfoWithReflexion ({instance},{member}); type:{t}"); +#endif + MemberInfo mi = t.GetMember (member)?.FirstOrDefault(); + if (mi == null) + mi = CompilerServices.SearchExtMethod (t, member); + return mi; + } + static MethodInfo getMethodInfoWithReflexion(object instance, string method){ +#if DEBUG_BINDING_FUNC_CALLS + Console.WriteLine ($"getMethodInfoWithReflexion ({instance},{method}); type:{instance.GetType ()}"); +#endif + return instance.GetType ().GetMethod (method, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + } + /// + /// set value, convert if required + /// + /// Destination instance + /// Value + /// Destination member + static void setValueWithReflexion(object dest, object value, string destMember){ +#if DEBUG_BINDING_FUNC_CALLS + Console.WriteLine ($"setValueWithReflexion (dest:{dest},value:{value},member:{destMember});"); +#endif + Type destType = null; + Type origType = null; + object convertedVal = null; + + MemberInfo miDest = getMemberInfoWithReflexion (dest, destMember); + + if (miDest == null) { + Debug.WriteLine ("Reverse template binding error: " + destMember + " not found in " + dest); + return; + } + + destType = CompilerServices.GetMemberInfoType (miDest); + + try { + if (value != null) { + if (destType == typeof (object))//TODO: check that test of destType is not causing problems + convertedVal = value; + else { + origType = value.GetType (); + if (destType.IsAssignableFrom (origType)) + convertedVal = Convert.ChangeType (value, destType); + else if (origType == typeof(string) & destType.IsPrimitive) + 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); + } + } + } catch (Exception ex) { + Debug.WriteLine (ex.ToString ()); + return; + } + + if (miDest.MemberType == MemberTypes.Property) + (miDest as PropertyInfo).SetValue (dest, convertedVal); + else if (miDest.MemberType == MemberTypes.Field) + (miDest as FieldInfo).SetValue (dest, convertedVal); + } + /// + /// Gets value with reflexion, return empty string ("") for string and object and return + /// default value for valueType data. + /// + static object getValueWithReflexion(object instance, MemberInfo mi){ +#if DEBUG_BINDING_FUNC_CALLS + Console.WriteLine ($"getValueWithReflexion ({instance},{mi});"); +#endif + object tmp = null; + Type dstType = null; + if (mi == null) + return null; + try { + if (mi.MemberType == MemberTypes.Property) { + PropertyInfo pi = mi as PropertyInfo; + tmp = pi.GetValue (instance); + dstType = pi.PropertyType; + }else if (mi.MemberType == MemberTypes.Field) { + FieldInfo fi = mi as FieldInfo; + tmp = fi.GetValue (instance); + dstType = fi.FieldType; + }else if (mi.MemberType == MemberTypes.Method) { + MethodInfo gi = mi as MethodInfo; + if (gi.IsStatic) + tmp = gi.Invoke(null, new object[] {instance}); + else + tmp = gi.Invoke(instance, null); + dstType = gi.ReturnType; + } + if (tmp != null) + return tmp; + if (dstType == typeof(string) || dstType == typeof (object))//TODO:object should be allowed to return null and not "" + return ""; + if (dstType.IsValueType) + return Activator.CreateInstance (dstType); + } catch (Exception ex) { + Debug.WriteLine (ex.ToString ()); + return ""; + } + + return null; + } + internal static MethodInfo SearchExtMethod (Type t, string methodName) + { + string key = t.Name + "." + methodName; + if (knownExtMethods.ContainsKey (key)) + return knownExtMethods [key]; + + //Console.WriteLine ($"*** search extension method: {t};{methodName} => key={key}"); + + MethodInfo mi = null; + mi = GetExtensionMethods (Assembly.GetEntryAssembly (), t, methodName); + if (mi == null) + mi = GetExtensionMethods (t.Module.Assembly, t, methodName); + + //add key even if mi is null to prevent searching again and again for propertyless bindings + knownExtMethods.Add (key, mi); + return mi; + } + + public static MethodInfo GetExtensionMethods (Assembly assembly, Type extendedType, string methodName) + { + foreach (Type t in assembly.GetTypes ().Where + (ty => ty.IsDefined (typeof (ExtensionAttribute), false))) { + foreach (MethodInfo mi in t.GetMethods + (BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Where + (m=> m.Name == methodName && m.IsDefined (typeof (ExtensionAttribute), false) && + m.GetParameters ().Length == 1)) { + Type curType = extendedType; + while (curType != null) { + if (mi.GetParameters () [0].ParameterType == curType) + return mi; + curType = curType.BaseType; + } + } + + } + return null; + } + /// + /// retrieve event handler in class or ancestors + /// + 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; + } + /// + /// search for an implicit conversion method in origine or destination classes + /// + 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; + } + #endregion + + /// + /// Emits tree parsing command to fetch dest instance starting from orig node + /// + internal 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++; + } + } + /// + /// Emits tree parsing commands to get child starting at root node + /// + /// MSIL generator + /// Absolute Node Address of the instance to get + internal 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); + } + /// + /// Emits msil to fetch chil instance of current GraphicObject on the stack + /// + /// Il generator + /// Parent type + /// Index of child, -1 for template root + internal 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; + } + } + + /// + /// Emit MSIL for conversion from orig type to dest type + /// + internal static void emitConvert(ILGenerator il, Type origType, Type destType){ + if (destType == typeof(object)) + 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.IsEnum || origType == typeof (Enum)) && destType.IsEnum) {//TODO:double check this (multiple IsEnum test, no check of enum underlying type + il.Emit (OpCodes.Unbox_Any, destType); + return; + } else if (origType.IsValueType) { + if (destType != origType) { + MethodInfo miIO = getImplicitOp (origType, destType); + if (miIO != null) { + System.Reflection.Emit.Label emitCreateDefault = il.DefineLabel (); + System.Reflection.Emit.Label emitContinue = il.DefineLabel (); + LocalBuilder lbStruct = il.DeclareLocal (origType); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, emitCreateDefault); + il.Emit (OpCodes.Unbox_Any, origType); + il.Emit (OpCodes.Br, emitContinue); + il.MarkLabel (emitCreateDefault); + il.Emit (OpCodes.Pop);//pop null value + il.Emit (OpCodes.Ldloca, lbStruct); + il.Emit (OpCodes.Initobj, origType); + il.Emit (OpCodes.Ldloc, lbStruct); + il.MarkLabel (emitContinue); + il.Emit (OpCodes.Call, miIO); + } else { + MethodInfo miconv = CompilerServices.GetConvertMethod (destType); + if (miconv.IsStatic) + il.Emit (OpCodes.Call, miconv); + else + il.Emit (OpCodes.Callvirt, miconv); + } + } else + il.Emit (OpCodes.Unbox_Any, destType);//TODO:double check this + } else { + if (destType.IsAssignableFrom (origType)) + il.Emit (OpCodes.Castclass, destType); + else { + if (origType == typeof (string)) { + //search dest type for parse method + MethodInfo miParse = destType.GetMethod + ("Parse", BindingFlags.Static | BindingFlags.Public, + Type.DefaultBinder, new Type [] { typeof (string) }, null); + if (miParse == null) + throw new Exception ("no Parse method found for: " + destType.FullName); + il.Emit (OpCodes.Call, miParse); + } + //implicit conversion can't be defined from or to object base class, + //so we will check if object underlying type is one of the implicit converter of destType + else if (origType == typeof (object)) {//test all implicit converter to destType on obj + System.Reflection.Emit.Label emitTestNextImpOp; + System.Reflection.Emit.Label emitImpOpFound = il.DefineLabel (); + foreach (MethodInfo mi in destType.GetMethods (BindingFlags.Public | BindingFlags.Static)) { + if (mi.Name == "op_Implicit") { + if (mi.GetParameters () [0].ParameterType == destType) + continue; + emitTestNextImpOp = il.DefineLabel (); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Isinst, mi.GetParameters () [0].ParameterType); + il.Emit (OpCodes.Brfalse, emitTestNextImpOp); + if (mi.GetParameters () [0].ParameterType.IsValueType) + il.Emit (OpCodes.Unbox_Any, mi.GetParameters () [0].ParameterType); + else + il.Emit (OpCodes.Isinst, mi.GetParameters () [0].ParameterType); + + il.Emit (OpCodes.Call, mi); + il.Emit (OpCodes.Br, emitImpOpFound); + + il.MarkLabel (emitTestNextImpOp); + } + } + //il.Emit (OpCodes.Br, emitImpOpNotFound); + il.MarkLabel (emitImpOpFound); + } else {//search both orig and dest types for implicit operators + MethodInfo miIO = getImplicitOp (origType, destType); + if (miIO != null) + il.Emit (OpCodes.Call, miIO); + } + } + } + } + internal static bool isValueType (object obj) => obj.GetType ().IsValueType; + /// + /// check type of current object on the stack and convert to dest type, + /// use loc_0 so store it as object!!! + /// + internal static void emitConvert(ILGenerator il, Type dstType){ + System.Reflection.Emit.Label endConvert = il.DefineLabel (); + System.Reflection.Emit.Label convert = il.DefineLabel (); + + 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); + + il.MarkLabel (convert); + + if (dstType == typeof (string)) { + System.Reflection.Emit.Label emitNullStr = il.DefineLabel (); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, emitNullStr); + il.Emit (OpCodes.Callvirt, CompilerServices.miObjToString); + il.Emit (OpCodes.Br, endConvert); + il.MarkLabel (emitNullStr); + il.Emit (OpCodes.Pop);//remove null string from stack + il.Emit (OpCodes.Ldstr, "");//replace with empty string + } else if (dstType.IsPrimitive) { + //il.Emit (OpCodes.Unbox_Any, dstType); + MethodInfo miconv = CompilerServices.GetConvertMethod (dstType); + if (miconv.IsStatic) + il.Emit (OpCodes.Call, miconv); + else + il.Emit (OpCodes.Callvirt, miconv); + }else if (dstType.IsValueType) { + System.Reflection.Emit.Label endConvert2 = il.DefineLabel (); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Call, typeof (CompilerServices).GetMethod ("isValueType", BindingFlags.Static | BindingFlags.NonPublic)); + System.Reflection.Emit.Label convertToValueType = il.DefineLabel (); + il.Emit (OpCodes.Brfalse, convertToValueType); + + il.Emit (OpCodes.Unbox_Any, dstType); + il.Emit (OpCodes.Br, endConvert); + + il.MarkLabel (convertToValueType); + //il.EmitWriteLine ($"convertToValueType:{dstType}"); + + //check if null + il.Emit (OpCodes.Dup); + LocalBuilder lbOrig = il.DeclareLocal (typeof (object)); + LocalBuilder lbMI = il.DeclareLocal (typeof (MethodInfo)); + il.Emit (OpCodes.Stloc, lbOrig); + il.Emit (OpCodes.Ldloc, lbOrig); + + //il.Emit (OpCodes.Ldloc, lbOrig); + //il.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type [] { typeof (object) })); + il.Emit (OpCodes.Brfalse, endConvert2); + //il.EmitWriteLine ($"search for implOp:{dstType}"); + il.Emit (OpCodes.Ldloc, lbOrig); + 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.Stloc, lbMI); + il.Emit (OpCodes.Ldloc, lbMI); + convert = il.DefineLabel (); + il.Emit (OpCodes.Brtrue, convert); + //il.Emit (OpCodes.Ldloc, lbOrig); + il.Emit (OpCodes.Isinst, dstType); + il.Emit (OpCodes.Br, endConvert2); + + il.MarkLabel (convert); + //il.EmitWriteLine ($"implOp found, calling:{dstType}"); + il.Emit (OpCodes.Pop); + il.Emit (OpCodes.Ldloc, lbMI); + il.Emit (OpCodes.Ldnull);//null instance for invoke + il.Emit (OpCodes.Ldc_I4_1); + il.Emit (OpCodes.Newarr, typeof (object)); + il.Emit (OpCodes.Dup);//duplicate the array ref + il.Emit (OpCodes.Ldc_I4_0);//push the index 0 + il.Emit (OpCodes.Ldloc, lbOrig);//push the orig value to convert + il.Emit (OpCodes.Stelem, typeof (object));//set the array element at index 0 + il.Emit (OpCodes.Callvirt, miMIInvoke); + il.MarkLabel (endConvert2); + il.Emit (OpCodes.Unbox_Any, dstType); + } else{ + LocalBuilder lbOrig = il.DeclareLocal (typeof (object)); + il.Emit (OpCodes.Stloc, lbOrig); //save orig value in loc0 + //first check if not null + il.Emit (OpCodes.Ldloc, lbOrig); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, endConvert); + il.Emit (OpCodes.Callvirt, miGetType); + il.Emit (OpCodes.Ldtoken, dstType);//push destination property type for testing + il.Emit (OpCodes.Call, CompilerServices.miGetTypeFromHandle); + il.Emit (OpCodes.Call, miGetImplOp); + il.Emit (OpCodes.Dup); + convert = il.DefineLabel (); + il.Emit (OpCodes.Brtrue, convert); + il.Emit (OpCodes.Pop); + il.Emit (OpCodes.Ldloc, lbOrig); + il.Emit (OpCodes.Isinst, dstType); + il.Emit (OpCodes.Br, endConvert); + + il.MarkLabel (convert); + il.Emit (OpCodes.Ldnull);//null instance for invoke + il.Emit (OpCodes.Ldc_I4_1); + il.Emit(OpCodes.Newarr, typeof (object)); + il.Emit (OpCodes.Dup);//duplicate the array ref + il.Emit (OpCodes.Ldc_I4_0);//push the index 0 + il.Emit (OpCodes.Ldloc, lbOrig);//push the orig value to convert + il.Emit (OpCodes.Stelem, typeof (object));//set the array element at index 0 + il.Emit (OpCodes.Callvirt, miMIInvoke); + + //if (dstType.IsValueType) + // il.Emit (OpCodes.Unbox_Any, dstType); + + } + + il.MarkLabel (endConvert); + } + + /// + /// Removes delegate from event handler by name + /// + static void removeEventHandlerByName(object instance, string eventName, string delegateName){ + Type t = instance.GetType (); + FieldInfo fiEvt = getEventHandlerField (t, eventName); + if (fiEvt == null) { +#if DEBUG_BINDING + Debug.WriteLine ("RemoveHandlerByName: Event '" + eventName + "' not found in " + instance); +#endif + 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 + } + } + } + } + /// + /// Removes delegate from event handler by searching for the object they are bond to + /// + static void removeEventHandlerByTarget(object instance, string eventName, object target){ + Type t = instance.GetType (); + FieldInfo fiEvt = 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 + } + } + } + } + /// + /// create delegate helper + /// + static Delegate createDel(object instance, Type eventType, string method){ + Type t = instance.GetType (); + MethodInfo mi = t.GetMethod (method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (mi == null) { + Debug.WriteLine ("Handler Method '{0}' not found in '{1}'", method, t); + return null; + } + return Delegate.CreateDelegate (eventType, instance, mi); + } + + internal static Delegate compileDynEventHandler(EventInfo sourceEvent, string expression, NodeAddress currentNode = null){ +#if DEBUG_BINDING + Debug.WriteLine ("\tCompile Event {0}: {1}", sourceEvent.Name, expression); +#endif + + Type lopType = null; + + if (currentNode == null) + lopType = sourceEvent.DeclaringType; + else + lopType = currentNode.NodeType; + +#region Retrieve EventHandler parameter type + MethodInfo evtInvoke = sourceEvent.EventHandlerType.GetMethod ("Invoke"); + ParameterInfo [] evtParams = evtInvoke.GetParameters (); + Type handlerArgsType = evtParams [1].ParameterType; +#endregion + + Type [] args = { typeof (object), handlerArgsType }; + DynamicMethod dm = new DynamicMethod ("dyn_eventHandler", + typeof(void), + args, true); + ILGenerator il = dm.GetILGenerator (256); + il.Emit (OpCodes.Nop); + + string [] srcLines = expression.Trim ().Split (new char [] { ';' }); + + foreach (string srcLine in srcLines) { + if (string.IsNullOrEmpty (srcLine)) + continue; + string [] operandes = srcLine.Trim ().Split (new char [] { '=' }); + if (operandes.Length != 2) //not an affectation + throw new NotSupportedException (); + + System.Reflection.Emit.Label cancel = il.DefineLabel (); + System.Reflection.Emit.Label cancelFinalSet = il.DefineLabel (); + System.Reflection.Emit.Label success = il.DefineLabel (); + + BindingMember lop = new BindingMember (operandes [0].Trim ()); + BindingMember rop = new BindingMember (operandes [1].Trim ()); + + il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node + +#region Left operande + PropertyInfo lopPI = null; + + //in dyn handler, no datasource binding, so single name in expression are also handled as current node property + if (lop.IsSingleName) + lopPI = lopType.GetProperty (lop.Tokens [0]); + else if (lop.IsCurrentNodeProperty) + lopPI = lopType.GetProperty (lop.Tokens [1]); + else + lop.emitGetTarget (il, cancel); +#endregion + +#region RIGHT OPERANDES + if (rop.IsStringConstant){ + il.Emit (OpCodes.Ldstr, rop.Tokens[0]); + lop.emitSetProperty (il); + }else if (rop.IsSingleName && rop.Tokens[0] == "this"){ + il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node + lop.emitSetProperty (il); + }else if (rop.LevelsUp ==0 && !string.IsNullOrEmpty(rop.Tokens[0])) {//parsable constant depending on lop type + //if left operand is member of current node, it's easy to fetch type, else we should use reflexion in msil + if (lopPI == null){//accept GraphicObj members, but it's restricive + //TODO: we should get the parse method by reflexion, or something else + lopPI = typeof(Widget).GetProperty (lop.Tokens [lop.Tokens.Length-1]); + if (lopPI == null) + throw new NotSupportedException (); + } + + MethodInfo lopParseMi = CompilerServices.miParseEnum; + if (lopPI.PropertyType.IsEnum){ + //load type of enum + il.Emit(OpCodes.Ldtoken, lopPI.PropertyType); + il.Emit(OpCodes.Call, CompilerServices.miGetTypeFromHandle); + //load enum value name + il.Emit (OpCodes.Ldstr, operandes [1].Trim ()); + //load false + il.Emit (OpCodes.Ldc_I4_0); + }else{ + lopParseMi = lopPI.PropertyType.GetMethod ("Parse"); + if (lopParseMi == null) + throw new Exception (string.Format + ("IML: no static 'Parse' method found in: {0}", lopPI.PropertyType.Name)); + + il.Emit (OpCodes.Ldstr, operandes [1].Trim ()); + } + if (lopParseMi.IsStatic) + il.Emit (OpCodes.Call, lopParseMi); + else + il.Emit(OpCodes.Callvirt, lopParseMi); + //il.Emit (OpCodes.Unbox_Any, lopPI.PropertyType); + //emit left operand assignment + il.Emit (OpCodes.Callvirt, lopPI.GetSetMethod()); + } else {//tree parsing and propert gets + il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node + + rop.emitGetTarget (il, cancelFinalSet); + rop.emitGetProperty (il, cancelFinalSet); + lop.emitSetProperty (il); + } +#endregion + + il.Emit (OpCodes.Br, success); + + il.MarkLabel (cancelFinalSet); + il.Emit (OpCodes.Pop); //pop null MemberInfo on the stack causing cancelation + il.MarkLabel (cancel); + il.Emit (OpCodes.Pop); //pop null instance on the stack causing cancelation + il.MarkLabel (success); + } + + il.Emit (OpCodes.Ret); + + return dm.CreateDelegate (sourceEvent.EventHandlerType); + } + + /// + /// MSIL helper, go n levels up + /// + /// true, if logical parents are not null + /// Start Instance + /// Levels to go upward + internal static ILayoutable goUpNbLevels(ILayoutable instance, int levelCount){ + ILayoutable tmp = instance; + int i = 0; + while (tmp != null && i < levelCount) { + tmp = tmp.LogicalParent; + i++; + } + return tmp; + } + /// + /// Splits expression on semicolon but ignore those between accolades + /// + internal static string[] splitOnSemiColumnOutsideAccolades (string expression){ + List exps = new List(); + int accCount = 0; + int expPtr = 0; + for (int c = 0; c < expression.Length; c++) { + switch (expression[c]){ + case '{': + accCount++; + break; + 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 (); + } + /// + /// Try to get the type named strDataType, search first in crow assembly then in + /// entry assembly. + /// + /// the corresponding type object if found + /// type name + internal static Type getTypeFromName (string strDataType){ + if (knownTypes.ContainsKey (strDataType)) + return knownTypes [strDataType]; + Type dataType = Type.GetType(strDataType); + if (dataType != null) { + knownTypes.Add (strDataType, dataType); + return dataType; + } + foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies ()) { + if (a.IsDynamic) + continue; + foreach (Type expT in a.GetExportedTypes ()) { + if (expT.Name != strDataType && expT.FullName != strDataType) + continue; + if (!knownTypes.ContainsKey(strDataType)) + knownTypes.Add (strDataType, expT); + return expT; + } + } + knownTypes.Add (strDataType, null); + return null; + } + + //get value from member of object + internal static object getDataTypeAndFetch (object data, string fetchMethod){ + Type dataType = data.GetType(); + //Console.WriteLine ($"get data type and fetch {data}.{fetchMethod}"); + MethodInfo miGetDatas = dataType.GetMethod (fetchMethod, new Type[] {}); + if (miGetDatas == null) + miGetDatas = CompilerServices.SearchExtMethod (dataType, fetchMethod); + + if (miGetDatas == null) {//in last resort, search among properties + PropertyInfo piDatas = dataType.GetProperty (fetchMethod); + if (piDatas == null) { + FieldInfo fiDatas = dataType.GetField (fetchMethod); + if (fiDatas == null)//and among fields + throw new Exception ("Fetch data member not found in ItemTemplate: " + fetchMethod); + return fiDatas.GetValue (data); + } + miGetDatas = piDatas.GetGetMethod (); + if (miGetDatas == null) + throw new Exception ("Read only property for fetching data in ItemTemplate: " + fetchMethod); + } + return miGetDatas.IsStatic ? + miGetDatas.Invoke (null, new object [] { data }) : miGetDatas.Invoke (data, null); + } + //TODO:memberinfo found here must be cached + internal static object getValue (Type dataType, object data, string member) + { + //Console.WriteLine ($"get value: {dataType} ; {data} ; {member}"); + + MethodInfo miGetDatas = dataType.GetMethod (member, new Type [] { }); + if (miGetDatas != null) + return miGetDatas.Invoke (data, null); + MemberInfo mbi = dataType.GetMember (member).FirstOrDefault (); + if (mbi == null) { + MethodInfo miExt = CompilerServices.SearchExtMethod (dataType, member); + if (miExt == null)//and among fields + throw new Exception ($"member {member} not found in {dataType}"); + return miExt.Invoke (null, new object [] { data }); + } + if (mbi.MemberType == MemberTypes.Property) { + miGetDatas = (mbi as PropertyInfo)?.GetGetMethod (); + if (miGetDatas == null) + throw new Exception ($"no getter found for property {member} in {dataType}"); + return miGetDatas.Invoke (data, null); + } + + FieldInfo fi = mbi as FieldInfo; + if (fi == null) + throw new Exception ($"member {member} not found in {dataType}"); + + return fi.GetValue (data); + } + internal static MemberInfo GetMemberInfo (Type dataType, string member, out Type returnType) + { + MethodInfo miGetDatas = dataType.GetMethod (member, new Type [] { }); + if (miGetDatas != null) { + returnType = miGetDatas.ReturnType; + return miGetDatas; + } + + MemberInfo mbi = dataType.GetMember (member).FirstOrDefault (); + if (mbi == null) + miGetDatas = CompilerServices.SearchExtMethod (dataType, member); + else { + if (mbi is FieldInfo) { + FieldInfo fi = mbi as FieldInfo; + returnType = fi.FieldType; + return mbi; + } + if (mbi.MemberType == MemberTypes.Property) + miGetDatas = (mbi as PropertyInfo).GetGetMethod (); + else + miGetDatas = mbi as MethodInfo; + } + returnType = miGetDatas?.ReturnType; + return miGetDatas; + + } + internal static void emitGetMemberValue (ILGenerator il, Type dataType, MemberInfo mi) + { + switch (mi.MemberType) { + case MemberTypes.Method: + MethodInfo mim = mi as MethodInfo; + if (mim.IsStatic) + il.Emit (OpCodes.Call, mim); + else + il.Emit (OpCodes.Callvirt, mim); + break; + case MemberTypes.Field: + il.Emit (OpCodes.Ldfld, mi as FieldInfo); + break; + } + } + //object is already on the stack + internal static void emitGetMemberValue (ILGenerator il, Type dataType, string member) + { + MethodInfo miGetDatas = dataType.GetMethod (member, new Type [] { }); + if (miGetDatas != null) { + il.Emit (OpCodes.Callvirt, miGetDatas); + return; + } + MemberInfo mbi = dataType.GetMember (member).FirstOrDefault (); + if (mbi == null) { + MethodInfo miExt = CompilerServices.SearchExtMethod (dataType, member); + if (miExt == null)//and among fields + throw new Exception ($"member {member} not found in {dataType}"); + il.Emit (OpCodes.Call, miExt); + return; + } + if (mbi.MemberType == MemberTypes.Property) { + miGetDatas = (mbi as PropertyInfo)?.GetGetMethod (); + if (miGetDatas == null) + throw new Exception ($"no getter found for property {member} in {dataType}"); + il.Emit (OpCodes.Callvirt, miGetDatas); + return; + } + + FieldInfo fi = mbi as FieldInfo; + if (fi == null) + throw new Exception ($"member {member} not found in {dataType}"); + + il.Emit (OpCodes.Ldfld, fi); + } + + //helper to get member info underlying type. + internal static Type GetMemberInfoType (MemberInfo mi) => + mi.MemberType == MemberTypes.Field ? (mi as FieldInfo).FieldType : + mi.MemberType == MemberTypes.Property ? (mi as PropertyInfo).PropertyType : + mi.MemberType == MemberTypes.Method ? (mi as MethodInfo).ReturnType : null; + } +} + diff --git a/Crow/src/Instantiator.cs b/Crow/src/Instantiator.cs index 20ca1ff8..760b5b1a 100644 --- a/Crow/src/Instantiator.cs +++ b/Crow/src/Instantiator.cs @@ -174,6 +174,19 @@ namespace Crow.IML { /// Delegate templateBinding; +#if DESIGN_MODE + public List DsValueChangedDynMeths =>dsValueChangedDynMeths; + public List CachedDelegates => cachedDelegates; + /// + /// store indices of template delegate to be handled by root parentChanged event + /// + public List TemplateCachedDelegateIndices => templateCachedDelegateIndices; + /// + /// Store template bindings in the instantiator + /// + public Delegate TemplateBinding => templateBinding; + +#endif #region IML parsing /// /// Parses IML and build a dynamic method that will be used to instantiate one or multiple occurences of the IML file or fragment @@ -446,7 +459,7 @@ namespace Crow.IML { ctx.SetDataSourceTypeForCurrentNode(CompilerServices.getTypeFromName (dataSourceType)); } ctx.il.Emit (OpCodes.Ldloc_0); - ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miLoadDefaultVals); + ctx.il.Emit (OpCodes.Call, CompilerServices.miLoadDefaultVals); #endregion @@ -811,12 +824,12 @@ namespace Crow.IML { } void emitTemplateBindings(IMLContext ctx, Dictionary> bindings){ //value changed dyn method - DynamicMethod dm = new DynamicMethod ("dyn_tmpValueChanged", + DynamicMethod dm = new DynamicMethod ("dyn_tmpValueChanged" + NewId, 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", + DynamicMethod dmPC = new DynamicMethod ("dyn_InitAndLogicalParentChanged" + NewId, typeof (void), CompilerServices.argsBoundDSChange, true); ILGenerator ilPC = dmPC.GetILGenerator (256); @@ -1108,6 +1121,10 @@ namespace Crow.IML { //store dschange delegate in instatiator instance for access while instancing graphic object int delDSIndex = cachedDelegates.Count; + + //Int32 fiLength = (Int32)il.GetType ().GetField ("code_len", BindingFlags.Instance | BindingFlags.NonPublic).GetValue (il); + //byte [] bytes = (byte[])il.GetType ().GetField ("code", BindingFlags.Instance | BindingFlags.NonPublic).GetValue (il); + cachedDelegates.Add (dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); #endregion @@ -1199,7 +1216,7 @@ namespace Crow.IML { #endregion } -#region emit dataSourceChanged event handler + #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. @@ -1413,7 +1430,7 @@ namespace Crow.IML { EventHandler tmp = (EventHandler)dm.CreateDelegate (typeof (EventHandler), dest); orig.ValueChanged += tmp; } -#endregion + #endregion /// /// search for graphic object type in crow assembly, if not found, diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index a71e4825..5fc2c2fb 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -495,11 +495,11 @@ namespace Crow /// path of the iml file to load public virtual Widget CreateInstance (string path) { - try { + //try { return GetInstantiator (path).CreateInstance (); - } catch (Exception ex) { - throw new Exception ("Error loading <" + path + ">:", ex); - } + //} catch (Exception ex) { + // throw new Exception ("Error loading <" + path + ">:", ex); + //} } /// /// Create an instance of a GraphicObject linked to this interface but not added to the GraphicTree @@ -508,13 +508,13 @@ namespace Crow /// path of the iml file to load public virtual Widget CreateTemplateInstance (string path, Type declaringType) { - try { +// try { if (!Templates.ContainsKey (path)) Templates [path] = new Instantiator (this, GetTemplateStreamFromPath(path, declaringType), path); return Templates [path].CreateInstance (); - } catch (Exception ex) { - throw new Exception ("Error loading Template <" + path + ">:", ex); - } + //} catch (Exception ex) { + // throw new Exception ("Error loading Template <" + path + ">:", ex); + //} } /// /// Fetch instantiator from cache or create it. diff --git a/Crow/src/Measure.cs b/Crow/src/Measure.cs index c40c6703..c77d66bd 100644 --- a/Crow/src/Measure.cs +++ b/Crow/src/Measure.cs @@ -1,28 +1,6 @@ -// -// Measure.cs +// Copyright (c) 2013-2020 Jean-Philippe Bruyère // -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; diff --git a/Crow/src/Picture.cs b/Crow/src/Picture.cs deleted file mode 100644 index 2a71d6b5..00000000 --- a/Crow/src/Picture.cs +++ /dev/null @@ -1,152 +0,0 @@ -// -// Picture.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.IO; -using Crow.Cairo; -using System.Collections.Generic; - -namespace Crow -{ - /// - /// store data and dimensions for resource sharing - /// - internal class sharedPicture { - //TODO: restructure this whith clever conceptual classes - public object Data; - public Size Dims; - public sharedPicture (object _data, Size _dims){ - Data = _data; - Dims = _dims; - } - } - /// - /// virtual class for loading and drawing picture in the interface - /// - /// Every loaded resources are stored in a dictonary with their path as key and shared - /// among interface elements - /// - public abstract class Picture : Fill - { - /// - /// share a single store for picture resources among usage in different controls - /// - internal static Dictionary sharedResources = new Dictionary(); - - /// - /// path of the picture - /// - public string Path; - /// - /// unscaled dimensions fetched on loading - /// - public Size Dimensions; - /// - /// if true and image has to be scalled, it will be scaled in both direction - /// equaly - /// - public bool KeepProportions = false; - /// - /// allow or not the picture to be scalled on request by the painter - /// - public bool Scaled = true; - - #region CTOR - /// - /// Initializes a new instance of Picture. - /// - public Picture () - { - } - /// - /// Initializes a new instance of Picture by loading the image pointed by the path argument - /// - /// image path, may be embedded - public Picture (string path) - { - Path = path; - } - #endregion - - #region Image Loading - /// - /// load the image for rendering from the stream given as argument - /// - /// picture stream - //public abstract void Load(Interface iface, string path); - #endregion - - /// - /// abstract method to paint the image in the rectangle given in arguments according - /// to the Scale and keepProportion parameters. - /// - /// drawing Backend context - /// bounds of the target surface to paint - /// used for svg only - public abstract void Paint(Context ctx, Rectangle rect, string subPart = ""); - - #region Operators - public static implicit operator Picture(string path) - { - if (string.IsNullOrEmpty (path)) - return null; - - Picture _pic = null; - - if (path.EndsWith (".svg", true, System.Globalization.CultureInfo.InvariantCulture)) - _pic = new SvgPicture (path); - else - _pic = new BmpPicture (path); - - return _pic; - } - public static implicit operator string(Picture _pic) - { - return _pic == null ? null : _pic.Path; - } - #endregion - - public static new object Parse(string path) - { - if (string.IsNullOrEmpty (path)) - return null; - - Picture _pic = null; - - if (path.EndsWith (".svg", true, System.Globalization.CultureInfo.InvariantCulture)) - _pic = new SvgPicture (path); - else - _pic = new BmpPicture (path); - - return _pic; - } - public override string ToString () - { - return Path; - } - } -} - diff --git a/Crow/src/Point.cs b/Crow/src/Point.cs index 83ae7651..02f12e2f 100644 --- a/Crow/src/Point.cs +++ b/Crow/src/Point.cs @@ -53,7 +53,7 @@ namespace Crow public static bool operator <= (Point p1, Point p2) => p1.X <= p2.X && p1.Y <= p2.Y; public static bool operator <= (Point s, int i) => s.X <= i && s.Y <= i; - public override string ToString () => string.Format ("({0},{1})", X, Y); + public override string ToString () => string.Format ("{0},{1}", X, Y); public override bool Equals (object obj) => obj is Point ? this == (Point)obj : obj is Point && (Point)this == (Point)obj; public static Point Parse (string s) diff --git a/Crow/src/PointD.cs b/Crow/src/PointD.cs index 9db0a184..d70e5b82 100644 --- a/Crow/src/PointD.cs +++ b/Crow/src/PointD.cs @@ -46,7 +46,7 @@ namespace Crow { public static bool operator <= (PointD p1, PointD p2) => p1.X <= p2.X && p1.Y <= p2.Y; public static bool operator <= (PointD s, double i) => s.X <= i && s.Y <= i; - public override string ToString () => string.Format ("({0},{1})", X, Y); + public override string ToString () => string.Format ("{0},{1}", X, Y); public override bool Equals (object obj) => obj is PointD ? this == (PointD)obj : obj is Point && (Point)this == (Point)obj; public static PointD Parse (string s) diff --git a/Crow/src/SolidColor.cs b/Crow/src/SolidColor.cs deleted file mode 100644 index 5c8ebb72..00000000 --- a/Crow/src/SolidColor.cs +++ /dev/null @@ -1,129 +0,0 @@ -// -// SolidColor.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml.Serialization; -using System.Reflection; -using System.Diagnostics; -using Crow.Cairo; - - - -namespace Crow -{ - public class SolidColor : Fill - { - public Color color = Color.Transparent; - #region CTOR - public SolidColor(Color c) - { - color = c; - } - #endregion - - #region implemented abstract members of Fill - - public override void SetAsSource (Context ctx, Rectangle bounds = default(Rectangle)) - { - ctx.SetSourceRGBA (color.R, color.G, color.B, color.A); - } - public static new object Parse(string s) - { - return new SolidColor((Color)s); - } - #endregion - - #region Operators - public static implicit operator Color(SolidColor c) - { - return c.color; - } - public static implicit operator SolidColor(Color c) - { - return new SolidColor (c); - } - public static bool operator ==(SolidColor left, SolidColor right) - { - return left is SolidColor ? right is SolidColor ? true : false : - left.color == right.color ? true : false; - } - public static bool operator !=(SolidColor left, SolidColor right) - { - return left is SolidColor ? right is SolidColor ? false : true : - left.color == right.color ? false : true; - - } - public override int GetHashCode () - { - return color.GetHashCode(); - } - public override bool Equals (object obj) - { - if (obj is Crow.Color) - return color == (Color)obj; - if (obj is SolidColor) - return color == (obj as SolidColor).color; - return false; - } -// public static bool operator ==(SolidColor c, string n) -// { -// return c.color.Name == n ? true : false; -// } -// public static bool operator !=(SolidColor c, string n) -// { -// return c.color.Name == n ? false : true; -// } -// public static bool operator ==(string n, SolidColor c) -// { -// return c.color.Name == n ? true : false; -// } -// public static bool operator !=(string n, SolidColor c) -// { -// return c.color.Name == n ? false : true; -// } - public static SolidColor operator *(SolidColor c, Double f) - { - return new SolidColor(new Color(c.color.R,c.color.G,c.color.B,c.color.A * f)); - } - public static SolidColor operator +(SolidColor c1, SolidColor c2) - { - return new SolidColor(new Color(c1.color.R + c2.color.R,c1.color.G + c2.color.G,c1.color.B + c2.color.B,c1.color.A + c2.color.A)); - } - public static SolidColor operator -(SolidColor c1, SolidColor c2) - { - return new SolidColor(new Color(c1.color.R - c2.color.R,c1.color.G - c2.color.G,c1.color.B - c2.color.B,c1.color.A - c2.color.A)); - } - #endregion - - public override string ToString() - { - return color.ToString (); - } - } -} diff --git a/Crow/src/SvgPicture.cs b/Crow/src/SvgPicture.cs deleted file mode 100644 index 33f68955..00000000 --- a/Crow/src/SvgPicture.cs +++ /dev/null @@ -1,152 +0,0 @@ -// -// SvgPicture.cs -// -// Author: -// Jean-Philippe Bruyère -// -// Copyright (c) 2013-2017 Jean-Philippe Bruyère -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.IO; -using Crow.Cairo; - -namespace Crow -{ - /// - /// Derived from FILL for loading and drawing SVG images in the interface - /// - public class SvgPicture : Picture - { - Rsvg.Handle hSVG; - - #region CTOR - /// - /// Initializes a new instance of SvgPicture. - /// - public SvgPicture () - {} - /// - /// Initializes a new instance of SvgPicture by loading the SVG file pointed by the path argument - /// - /// image path, may be embedded - public SvgPicture (string path) : base(path) - { - Load (); - } - #endregion - - void Load () - { - if (sharedResources.ContainsKey (Path)) { - sharedPicture sp = sharedResources [Path]; - hSVG = (Rsvg.Handle)sp.Data; - Dimensions = sp.Dims; - return; - } - using (Stream stream = Interface.StaticGetStreamFromPath (Path)) { - using (MemoryStream ms = new MemoryStream ()) { - stream.CopyTo (ms); - - hSVG = new Rsvg.Handle (ms.ToArray ()); - Dimensions = new Size (hSVG.Dimensions.Width, hSVG.Dimensions.Height); - } - } - sharedResources [Path] = new sharedPicture (hSVG, Dimensions); - } - - public void LoadSvgFragment (string fragment) { - hSVG = new Rsvg.Handle (System.Text.Encoding.Unicode.GetBytes(fragment)); - Dimensions = new Size (hSVG.Dimensions.Width, hSVG.Dimensions.Height); - } - - #region implemented abstract members of Fill - - public override void SetAsSource (Context ctx, Rectangle bounds = default(Rectangle)) - { - float widthRatio = 1f; - float heightRatio = 1f; - - if (Scaled){ - widthRatio = (float)bounds.Width / Dimensions.Width; - heightRatio = (float)bounds.Height / Dimensions.Height; - } - - if (KeepProportions) { - if (widthRatio < heightRatio) - heightRatio = widthRatio; - else - widthRatio = heightRatio; - } - - using (ImageSurface tmp = new ImageSurface (Format.Argb32, bounds.Width, bounds.Height)) { - using (Context gr = new Context (tmp)) { - gr.Translate (bounds.Left, bounds.Top); - gr.Scale (widthRatio, heightRatio); - gr.Translate ((bounds.Width/widthRatio - Dimensions.Width)/2, (bounds.Height/heightRatio - Dimensions.Height)/2); - - hSVG.RenderCairo (gr); - } - ctx.SetSource (tmp); - } - } - #endregion - - /// - /// paint the image in the rectangle given in arguments according - /// to the Scale and keepProportion parameters. - /// - /// drawing Backend context - /// bounds of the target surface to paint - /// limit rendering to svg part named 'subPart' - public override void Paint (Context gr, Rectangle rect, string subPart = "") - { - if (hSVG == null) - return; - float widthRatio = 1f; - float heightRatio = 1f; - - if (Scaled) { - widthRatio = (float)rect.Width / Dimensions.Width; - heightRatio = (float)rect.Height / Dimensions.Height; - } - if (KeepProportions) { - if (widthRatio < heightRatio) - heightRatio = widthRatio; - else - widthRatio = heightRatio; - } - - gr.Save (); - - gr.Translate (rect.Left,rect.Top); - gr.Scale (widthRatio, heightRatio); - gr.Translate (((float)rect.Width/widthRatio - Dimensions.Width)/2f, ((float)rect.Height/heightRatio - Dimensions.Height)/2f); - - if (string.IsNullOrEmpty (subPart)) - hSVG.RenderCairo (gr); - else - hSVG.RenderCairoSub (gr, "#" + subPart); - - gr.Restore (); - } - } -} - diff --git a/Crow/src/Widgets/Group.cs b/Crow/src/Widgets/Group.cs index 6db541d6..b2f6f418 100644 --- a/Crow/src/Widgets/Group.cs +++ b/Crow/src/Widgets/Group.cs @@ -97,6 +97,7 @@ namespace Crow Children.Remove(child); child.Parent = null; + child.LogicalParent = null; childrenRWLock.ExitWriteLock (); diff --git a/Crow/src/Widgets/TemplatedGroup.cs b/Crow/src/Widgets/TemplatedGroup.cs index 54e3e760..da2c1f87 100644 --- a/Crow/src/Widgets/TemplatedGroup.cs +++ b/Crow/src/Widgets/TemplatedGroup.cs @@ -339,13 +339,13 @@ namespace Crow { /// Items loading thread /// void loading(){ - try { + //try { loadPage (data, items, dataTest); - } catch { + /*} catch (Exception ex) { if (Monitor.IsEntered (IFace.LayoutMutex)) Monitor.Exit (IFace.LayoutMutex); - Console.WriteLine ("loading thread aborted"); - } + Console.WriteLine ("loading thread aborted: " + ex.ToString()); + }*/ } // //if (!ItemTemplates.ContainsKey ("default")) @@ -363,10 +363,13 @@ namespace Crow { void cancelLoadingThread(){ if (loadingThread == null) return; - bool updateMx = Monitor.IsEntered (IFace.UpdateMutex); bool layoutMx = Monitor.IsEntered (IFace.LayoutMutex); +#if DEBUG_LOG + DebugLog.AddEvent (DbgEvtType.TGCancelLoadingThread, this); +#endif + if (layoutMx) Monitor.Exit (IFace.LayoutMutex); if (updateMx) diff --git a/Crow/src/Widgets/Widget.cs b/Crow/src/Widgets/Widget.cs index 0ca6598b..8596a86f 100644 --- a/Crow/src/Widgets/Widget.cs +++ b/Crow/src/Widgets/Widget.cs @@ -951,7 +951,7 @@ namespace Crow rootDataLevel = true; #if DEBUG_LOG - DbgEvent dbgEvt = DebugLog.AddEvent(DbgEvtType.GOLockLayouting, this); + DbgEvent dbgEvt = DebugLog.AddEvent(DbgEvtType.GOLockUpdate, this); #endif lock (IFace.UpdateMutex) { OnDataSourceChanged (this, dse); @@ -1156,12 +1156,12 @@ namespace Crow il.Emit(OpCodes.Ret); #endregion - try { + //try { IFace.DefaultValuesLoader[styleKey] = (Interface.LoaderInvoker)dm.CreateDelegate(typeof(Interface.LoaderInvoker)); IFace.DefaultValuesLoader[styleKey] (this); - } catch (Exception ex) { + /*} catch (Exception ex) { throw new Exception ("Error applying style <" + styleKey + ">:", ex); - } + }*/ #if DEBUG_LOG dbgEvt.end = DebugLog.chrono.ElapsedTicks; @@ -1457,6 +1457,9 @@ namespace Crow #endif if (Parent == null) return; +#if DEBUG_LOG + DbgEvent dbgEvt = DebugLog.AddEvent (DbgEvtType.GOLockLayouting, this); +#endif lock (IFace.LayoutMutex) { //prevent queueing same LayoutingType for this layoutType &= (~RegisteredLayoutings); @@ -1494,6 +1497,9 @@ namespace Crow if (layoutType.HasFlag (LayoutingType.ArrangeChildren)) IFace.LayoutingQueue.Enqueue (new LayoutingQueueItem (LayoutingType.ArrangeChildren, this)); } +#if DEBUG_LOG + dbgEvt.end = DebugLog.chrono.ElapsedTicks; +#endif } /// trigger dependant sizing component update @@ -1594,7 +1600,7 @@ namespace Crow else if (Width == Measure.Stretched) Slot.Width = Parent.ClientRectangle.Width; else - Slot.Width = (int)Math.Round ((double)(Parent.ClientRectangle.Width * Width) / 100.0); + Slot.Width = (int)Math.Round ((double)(Parent.ClientRectangle.Width * Width) / 100.0); if (Slot.Width < 0) return false; diff --git a/Crow/src/debug/DbgLogViewer.cs b/Crow/src/debug/DbgLogViewer.cs index 4ff6082e..dad155c7 100644 --- a/Crow/src/debug/DbgLogViewer.cs +++ b/Crow/src/debug/DbgLogViewer.cs @@ -130,7 +130,7 @@ namespace Crow long currentTick = 0, selStart = -1, selEnd = -1, minTicks = 0, maxTicks = 0, visibleTicks = 0; int currentLine = -1; - int visibleLines; + int visibleLines = 1; public string LogFile { get { return logFile; } @@ -141,7 +141,6 @@ namespace Crow loadDebugFile (); - NotifyValueChanged ("LogFile", logFile); RegisterForGraphicUpdate (); } @@ -230,8 +229,6 @@ namespace Crow leftMargin = 2.5 + maxNameWidth; - fe = gr.FontExtents; - topMargin = 2.0 * fe.Height; updateVisibleLines (); @@ -241,7 +238,7 @@ namespace Crow } void updateVisibleLines(){ - visibleLines = (int)Math.Floor (((double)ClientRectangle.Height - topMargin) / fe.Height); + visibleLines = fe.Height < 1 ? 1 : (int)Math.Floor (((double)ClientRectangle.Height - topMargin) / fe.Height); NotifyValueChanged ("VisibleLines", visibleLines); updateMaxScrollY (); } @@ -280,10 +277,15 @@ namespace Crow get { return base.Font; } set { base.Font = value; + using (Context gr = new Context (IFace.surf)) { + gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + gr.SetFontSize (Font.Size); + + fe = gr.FontExtents; + } loadDebugFile (); } } - public override int ScrollY { get { return base.ScrollY; @@ -359,7 +361,7 @@ namespace Crow Color c = Color.Black; - if (evt.type == DbgEvtType.GOProcessLayouting) { + if (evt.type == DbgEvtType.GOProcessLayouting) { switch (evt.data.result) { case LayoutingQueueItem.Result.Success: c = Crow.Color.Green; @@ -374,11 +376,13 @@ namespace Crow c = Crow.Color.Orange; break; } - } else if (colors.ContainsKey (evt.type)) + } else if (evt.type.HasFlag (DbgEvtType.GOLock)) + c = Color.BlueViolet; + else if (colors.ContainsKey (evt.type)) c = colors [evt.type]; - else - System.Diagnostics.Debugger.Break (); - + //else + // System.Diagnostics.Debugger.Break (); + c = c.AdjustAlpha (0.2); gr.SetSourceColor (c); gr.Rectangle (x, penY, w, fe.Height); diff --git a/Crow/src/debug/DebugLogger.cs b/Crow/src/debug/DebugLogger.cs index bfe1948f..37842e22 100644 --- a/Crow/src/debug/DebugLogger.cs +++ b/Crow/src/debug/DebugLogger.cs @@ -49,27 +49,34 @@ namespace Crow IFaceEndDrawing = 0x0106, IFaceUpdate = 0x0107, //10 nth bit set for graphic obj - GraphicObject = 0x0200, - GOClassCreation = 0x0201, - GOInitialization = 0x0202, - GOClippingRegistration = 0x0203, - GORegisterClip = 0x0204, - GORegisterForGraphicUpdate = 0x0205, - GOEnqueueForRepaint = 0x0206, - GOLayouting = 0x0400, - GORegisterLayouting = 0x0607, - GOProcessLayoutingWithNoParent = 0x0608, - GOProcessLayouting = 0x0609, - GODraw = 0x020a, - GORecreateCache = 0x020b, - GOUpdateCacheAndPaintOnCTX = 0x020c, - GOPaint = 0x020d, - GONewDataSource = 0x020e, + GraphicObject = 0x0100, + Warning = 0x4000, + Error = 0x8000, + GOClassCreation = GraphicObject | 0x01, + GOInitialization = GraphicObject | 0x02, + GOClippingRegistration = GraphicObject | 0x03, + GORegisterClip = GraphicObject | 0x04, + GORegisterForGraphicUpdate = GraphicObject | 0x05, + GOEnqueueForRepaint = GraphicObject | 0x06, + GONewDataSource = GraphicObject | 0x07, + GOLayouting = 0x0200, + Drawing = 0x0400, GOLock = 0x0800, - GOLockUpdate = 0x0a01, - GOLockClipping = 0x0a02, - GOLockRender = 0x0a03, - GOLockLayouting = 0x0a04, + TemplatedGroup = 0x1000, + GORegisterLayouting = GraphicObject | GOLayouting | 0x01, + GOProcessLayouting = GraphicObject | GOLayouting | 0x02, + GOProcessLayoutingWithNoParent = Warning | GraphicObject | GOLayouting | 0x01, + GODraw = GraphicObject | Drawing | 0x01, + GORecreateCache = GraphicObject | Drawing | 0x02, + GOUpdateCacheAndPaintOnCTX = GraphicObject | Drawing | 0x03, + GOPaint = GraphicObject | Drawing | 0x04, + + GOLockUpdate = GraphicObject | GOLock | 0x01, + GOLockClipping = GraphicObject | GOLock | 0x02, + GOLockRender = GraphicObject | GOLock | 0x03, + GOLockLayouting = GraphicObject | GOLock | 0x04, + + TGCancelLoadingThread = GraphicObject | TemplatedGroup | 0x01, } /// diff --git a/Samples/HelloWorld/HelloWorld.csproj b/Samples/HelloWorld/HelloWorld.csproj index 448fb435..908af2fc 100644 --- a/Samples/HelloWorld/HelloWorld.csproj +++ b/Samples/HelloWorld/HelloWorld.csproj @@ -7,7 +7,8 @@ - TRACE;DEBUG;NETFRAMEWORK;NET471 + full + TRACE;DEBUG @@ -22,4 +23,7 @@ HelloWorld.%(Filename)%(Extension) + + + diff --git a/Samples/HelloWorld/ILView.cs b/Samples/HelloWorld/ILView.cs new file mode 100644 index 00000000..9bac2299 --- /dev/null +++ b/Samples/HelloWorld/ILView.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using Crow; + +namespace HelloWorld +{ + public class ILView : Widget + { + public ILView () + { + } + } +} diff --git a/Samples/HelloWorld/main.cs b/Samples/HelloWorld/main.cs index 14a1a2ad..c407b04e 100644 --- a/Samples/HelloWorld/main.cs +++ b/Samples/HelloWorld/main.cs @@ -1,5 +1,9 @@ using System; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; using Crow; +using Crow.IML; namespace HelloWorld { @@ -10,6 +14,7 @@ namespace HelloWorld vke.Run (); } } + protected override void Startup () { CMDQuit = new Command (new Action (() => running = false)) { Caption = "Quit", Icon = new SvgPicture ("#Crow.Icons.exit-symbol.svg") }; @@ -19,6 +24,17 @@ namespace HelloWorld w.DataSource = this; } + public SolidColor testColor = Color.Red; + public SolidColor TestColor { + get => testColor; + set { + if (testColor == value) + return; + testColor = value; + NotifyValueChanged ("TestColor", testColor); + } + } + Color [] colors = { Color.Blue, Color.DarkGoldenRod, Color.Red, Color.Azure, Color.Brown, Color.Black, Color.White, Color.Pink }; int ptr = 0; @@ -37,6 +53,48 @@ namespace HelloWorld if (ptr == colors.Length) ptr = 0; } + public override bool OnKeyDown (Key key) + { + switch (key) { + case Key.d: + dump (); + break; + case Key.space: + TestColor = Color.Green; + break; + default: + return base.OnKeyDown (key); + } + return true; + } + + void dump () + { + Instantiator inst = Instantiators ["#HelloWorld.helloworld.crow"]; + + foreach (Delegate cd in inst.CachedDelegates) { + FieldInfo mb = typeof(Delegate).GetField("original_method_info", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField); + + DynamicMethod dynMethod = mb.GetValue (cd) as DynamicMethod; + /*Console.WriteLine (typeof (ILGenerator).GetFields (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField)); + + var ilgen = dynMethod.GetILGenerator (); + + + +*/ + + MethodBody body = dynMethod.GetMethodBody (); + + byte [] il = body.GetILAsByteArray (); + foreach (byte b in il) { + Console.Write ($"{b:x2} "); + //Console.WriteLine (b); + } + + } + + } } } diff --git a/Samples/HelloWorld/ui/helloworld.crow b/Samples/HelloWorld/ui/helloworld.crow index d150136e..1bc9601f 100644 --- a/Samples/HelloWorld/ui/helloworld.crow +++ b/Samples/HelloWorld/ui/helloworld.crow @@ -1,9 +1,6 @@  - - - - - + + + + + \ No newline at end of file diff --git a/Samples/common/ui/Interfaces/Divers/0.crow b/Samples/common/ui/Interfaces/Divers/0.crow index d083946c..4b6c50fb 100644 --- a/Samples/common/ui/Interfaces/Divers/0.crow +++ b/Samples/common/ui/Interfaces/Divers/0.crow @@ -1,162 +1,108 @@ - - - + + + - + - - - - - - - - - + - + - - - - - - + + - + - - + - - - + - - + \ No newline at end of file