aboutsummaryrefslogtreecommitdiffstatsci
path: root/src/GraphicObjects/Base/GraphicObject.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/GraphicObjects/Base/GraphicObject.cs')
-rw-r--r--src/GraphicObjects/Base/GraphicObject.cs1555
1 files changed, 1555 insertions, 0 deletions
diff --git a/src/GraphicObjects/Base/GraphicObject.cs b/src/GraphicObjects/Base/GraphicObject.cs
new file mode 100644
index 00000000..2afbe336
--- /dev/null
+++ b/src/GraphicObjects/Base/GraphicObject.cs
@@ -0,0 +1,1555 @@
+//
+// GraphicObject.cs
+//
+// Author:
+// Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
+//
+// 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.ComponentModel;
+using System.Xml.Serialization;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Runtime.CompilerServices;
+using Cairo;
+using System.Linq;
+using System.Diagnostics;
+using System.IO;
+
+namespace Crow
+{
+ public class GraphicObject : IXmlSerializable, ILayoutable, IValueChange, ICloneable
+ {
+ internal static ulong currentUid = 0;
+ internal ulong uid = 0;
+
+ Interface currentInterface = null;
+
+ [XmlIgnore]public Interface CurrentInterface {
+ get {
+ if (currentInterface == null) {
+ currentInterface = Interface.CurrentInterface;
+ Initialize ();
+ }
+ return currentInterface;
+ }
+ set {
+ currentInterface = value;
+ }
+ }
+
+ Rectangles clipping = new Rectangles();
+ public Rectangles Clipping { get { return clipping; }}
+
+ #region IValueChange implementation
+ public event EventHandler<ValueChangeEventArgs> ValueChanged;
+ public virtual void NotifyValueChanged(string MemberName, object _value)
+ {
+ //Debug.WriteLine ("Value changed: {0}->{1} = {2}", this, MemberName, _value);
+ ValueChanged.Raise(this, new ValueChangeEventArgs(MemberName, _value));
+ }
+ #endregion
+
+ #region CTOR
+ public GraphicObject ()
+ {
+ #if DEBUG
+ uid = currentUid;
+ currentUid++;
+ #endif
+ }
+ #endregion
+
+ /// <summary>
+ /// Initialize this Graphic object instance by setting style and default values and loading template if required
+ /// </summary>
+ public virtual void Initialize(){
+ if (currentInterface == null)
+ currentInterface = Interface.CurrentInterface;
+ loadDefaultValues ();
+ }
+ #region private fields
+ LayoutingType registeredLayoutings = LayoutingType.All;
+ ILayoutable logicalParent;
+ ILayoutable parent;
+ string name;
+ Fill background = Color.Transparent;
+ Fill foreground = Color.White;
+ Font font = "droid, 10";
+ Measure width, height;
+ int left, top;
+ double cornerRadius = 0;
+ int margin = 0;
+ bool focusable = false;
+ bool hasFocus = false;
+ bool isActive = false;
+ bool mouseRepeat;
+ protected bool isVisible = true;
+ bool isEnabled = true;
+ VerticalAlignment verticalAlignment = VerticalAlignment.Center;
+ HorizontalAlignment horizontalAlignment = HorizontalAlignment.Center;
+ Size maximumSize = "0,0";
+ Size minimumSize = "0,0";
+ bool cacheEnabled = false;
+ bool clipToClientRect = true;
+ protected object dataSource;
+ string style;
+ object tag;
+ #endregion
+
+ #region public fields
+ /// <summary>
+ /// Current size and position computed during layouting pass
+ /// </summary>
+ public Rectangle Slot = new Rectangle ();
+ /// <summary>
+ /// keep last slot components for each layouting pass to track
+ /// changes and trigger update of other component accordingly
+ /// </summary>
+ public Rectangle LastSlots;
+ /// <summary>
+ /// keep last slot painted on screen to clear traces if moved or resized
+ /// TODO: we should ensure the whole parsed widget tree is the last painted
+ /// version to clear effective oldslot if parents have been moved or resized.
+ /// IDEA is to add a ScreenCoordinates function that use only lastPaintedSlots
+ /// </summary>
+ public Rectangle LastPaintedSlot;
+ /// <summary>Prevent requeuing multiple times the same widget</summary>
+ public bool IsQueueForRedraw = false;
+ /// <summary>drawing Cache, if null, a redraw is done, cached or not</summary>
+ public byte[] bmp;
+ public bool IsDirty = true;
+ /// <summary>
+ /// This size is computed on each child' layout changes.
+ /// In stacking widget, it is used to compute the remaining space for the stretched
+ /// widget inside the stack, which is never added to the contentSize, instead, its size
+ /// is deducted from (parent.ClientRectangle - contentSize)
+ /// </summary>
+ internal Size contentSize;
+ #endregion
+
+ #region ILayoutable
+ [XmlIgnore]public LayoutingType RegisteredLayoutings { get { return registeredLayoutings; } set { registeredLayoutings = value; } }
+ //TODO: it would save the recurent cost of a cast in event bubbling if parent type was GraphicObject
+ // or we could add to the interface the mouse events
+ /// <summary>
+ /// Parent in the graphic tree, used for rendering and layouting
+ /// </summary>
+ [XmlIgnore]public virtual ILayoutable Parent {
+ get { return parent; }
+ set {
+ if (parent == value)
+ return;
+ DataSourceChangeEventArgs e = new DataSourceChangeEventArgs (parent, value);
+ lock (this)
+ parent = value;
+
+ onParentChanged (this, e);
+ }
+ }
+ [XmlIgnore]public ILayoutable LogicalParent {
+ get { return logicalParent == null ? Parent : logicalParent; }
+ set {
+ if (logicalParent == value)
+ return;
+ if (logicalParent != null)
+ (logicalParent as GraphicObject).DataSourceChanged -= onLogicalParentDataSourceChanged;
+ DataSourceChangeEventArgs dsce = new DataSourceChangeEventArgs (LogicalParent, null);
+ logicalParent = value;
+ dsce.NewDataSource = LogicalParent;
+ if (logicalParent != null)
+ (logicalParent as GraphicObject).DataSourceChanged += onLogicalParentDataSourceChanged;
+ onLogicalParentChanged (this, dsce);
+ }
+ }
+ [XmlIgnore]public virtual Rectangle ClientRectangle {
+ get {
+ Rectangle cb = Slot.Size;
+ cb.Inflate ( - Margin);
+ return cb;
+ }
+ }
+ public virtual Rectangle ContextCoordinates(Rectangle r){
+ GraphicObject go = Parent as GraphicObject;
+ if (go == null)
+ return r + Parent.ClientRectangle.Position;
+ return go.CacheEnabled ?
+ r + Parent.ClientRectangle.Position :
+ Parent.ContextCoordinates (r);
+ }
+ public virtual Rectangle ScreenCoordinates (Rectangle r){
+ return
+ Parent.ScreenCoordinates(r) + Parent.getSlot().Position + Parent.ClientRectangle.Position;
+ }
+ public virtual Rectangle getSlot () { return Slot;}
+ #endregion
+
+ #region EVENT HANDLERS
+ public event EventHandler<MouseWheelEventArgs> MouseWheelChanged;
+ public event EventHandler<MouseButtonEventArgs> MouseUp;
+ public event EventHandler<MouseButtonEventArgs> MouseDown;
+ public event EventHandler<MouseButtonEventArgs> MouseClick;
+ public event EventHandler<MouseButtonEventArgs> MouseDoubleClick;
+ public event EventHandler<MouseMoveEventArgs> MouseMove;
+ public event EventHandler<MouseMoveEventArgs> MouseEnter;
+ public event EventHandler<MouseMoveEventArgs> MouseLeave;
+ public event EventHandler<KeyboardKeyEventArgs> KeyDown;
+ public event EventHandler<KeyboardKeyEventArgs> KeyUp;
+ public event EventHandler<KeyPressEventArgs> KeyPress;
+ public event EventHandler Focused;
+ public event EventHandler Unfocused;
+ public event EventHandler Enabled;
+ public event EventHandler Disabled;
+ public event EventHandler<LayoutingEventArgs> LayoutChanged;
+ public event EventHandler<DataSourceChangeEventArgs> DataSourceChanged;
+ public event EventHandler<DataSourceChangeEventArgs> ParentChanged;
+ public event EventHandler<DataSourceChangeEventArgs> LogicalParentChanged;
+ #endregion
+
+ #region public properties
+ /// <summary>Random value placeholder</summary>
+ [XmlAttributeAttribute]
+ public object Tag {
+ get { return tag; }
+ set {
+ if (tag == value)
+ return;
+ tag = value;
+ NotifyValueChanged ("Tag", tag);
+ }
+ }
+ /// <summary>
+ /// If enabled, resulting bitmap of graphic object is cached in an byte array
+ /// speeding up rendering of complex object. Default is enabled.
+ /// </summary>
+ [XmlAttributeAttribute][DefaultValue(true)]
+ public virtual bool CacheEnabled {
+ get { return cacheEnabled; }
+ set {
+ if (cacheEnabled == value)
+ return;
+ cacheEnabled = value;
+ NotifyValueChanged ("CacheEnabled", cacheEnabled);
+ }
+ }
+ /// <summary>
+ /// If true, rendering of GraphicObject is clipped inside client rectangle
+ /// </summary>
+ [XmlAttributeAttribute][DefaultValue(true)]
+ public virtual bool ClipToClientRect {
+ get { return clipToClientRect; }
+ set {
+ if (clipToClientRect == value)
+ return;
+ clipToClientRect = value;
+ NotifyValueChanged ("ClipToClientRect", clipToClientRect);
+ this.RegisterForRedraw ();
+ }
+ }
+ /// <summary>
+ /// Name is used in binding to reference other GraphicObjects inside the graphic tree
+ /// </summary>
+ [XmlAttributeAttribute][DefaultValue(null)]
+ public virtual string Name {
+ get {
+ #if DEBUG
+ return string.IsNullOrEmpty(name) ? this.GetType().Name + uid.ToString () : name;
+ #else
+ return name;
+ #endif
+ }
+ set {
+ if (name == value)
+ return;
+ name = value;
+ NotifyValueChanged("Name", name);
+ }
+ }
+ [XmlAttributeAttribute ()][DefaultValue(VerticalAlignment.Center)]
+ public virtual VerticalAlignment VerticalAlignment {
+ get { return verticalAlignment; }
+ set {
+ if (verticalAlignment == value)
+ return;
+
+ verticalAlignment = value;
+ NotifyValueChanged("VerticalAlignment", verticalAlignment);
+ RegisterForLayouting (LayoutingType.Y);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(HorizontalAlignment.Center)]
+ public virtual HorizontalAlignment HorizontalAlignment {
+ get { return horizontalAlignment; }
+ set {
+ if (horizontalAlignment == value)
+ return;
+ horizontalAlignment = value;
+ NotifyValueChanged("HorizontalAlignment", horizontalAlignment);
+ RegisterForLayouting (LayoutingType.X);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(0)]
+ public virtual int Left {
+ get { return left; }
+ set {
+ if (left == value)
+ return;
+ left = value;
+ NotifyValueChanged ("Left", left);
+ this.RegisterForLayouting (LayoutingType.X);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(0)]
+ public virtual int Top {
+ get { return top; }
+ set {
+ if (top == value)
+ return;
+ top = value;
+ NotifyValueChanged ("Top", top);
+ this.RegisterForLayouting (LayoutingType.Y);
+ }
+ }
+ /// <summary>
+ /// When set to True, the <see cref="T:Crow.GraphicObject"/>'s width and height will be set to Fit.
+ /// </summary>
+ [XmlAttributeAttribute()][DefaultValue(false)]
+ public virtual bool Fit {
+ get { return Width == Measure.Fit && Height == Measure.Fit ? true : false; }
+ set {
+ if (value == Fit)
+ return;
+
+ Width = Height = Measure.Fit;
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue("Inherit")]
+ public virtual Measure Width {
+ get {
+ return width.Units == Unit.Inherit ?
+ Parent is GraphicObject ? (Parent as GraphicObject).WidthPolicy :
+ Measure.Stretched : width;
+ }
+ set {
+ if (width == value)
+ return;
+ if (value.IsFixed) {
+ if (value < MinimumSize.Width || (value > MaximumSize.Width && MaximumSize.Width > 0))
+ return;
+ }
+ Measure lastWP = WidthPolicy;
+ width = value;
+ NotifyValueChanged ("Width", width);
+ if (WidthPolicy != lastWP) {
+ NotifyValueChanged ("WidthPolicy", WidthPolicy);
+ //contentSize in Stacks are only update on childLayoutChange, and the single stretched
+ //child of the stack is not counted in contentSize, so when changing size policy of a child
+ //we should adapt contentSize
+ //TODO:check case when child become stretched, and another stretched item already exists.
+ if (parent is GenericStack) {//TODO:check if I should test Group instead
+ if ((parent as GenericStack).Orientation == Orientation.Horizontal) {
+ if (lastWP == Measure.Fit)
+ (parent as GenericStack).contentSize.Width -= this.LastSlots.Width;
+ else
+ (parent as GenericStack).contentSize.Width += this.LastSlots.Width;
+ }
+ }
+ }
+
+ this.RegisterForLayouting (LayoutingType.Width);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue("Inherit")]
+ public virtual Measure Height {
+ get {
+ return height.Units == Unit.Inherit ?
+ Parent is GraphicObject ? (Parent as GraphicObject).HeightPolicy :
+ Measure.Stretched : height;
+ }
+ set {
+ if (height == value)
+ return;
+ if (value.IsFixed) {
+ if (value < MinimumSize.Height || (value > MaximumSize.Height && MaximumSize.Height > 0))
+ return;
+ }
+ Measure lastHP = HeightPolicy;
+ height = value;
+ NotifyValueChanged ("Height", height);
+ if (HeightPolicy != lastHP) {
+ NotifyValueChanged ("HeightPolicy", HeightPolicy);
+ if (parent is GenericStack) {
+ if ((parent as GenericStack).Orientation == Orientation.Vertical) {
+ if (lastHP == Measure.Fit)
+ (parent as GenericStack).contentSize.Height -= this.LastSlots.Height;
+ else
+ (parent as GenericStack).contentSize.Height += this.LastSlots.Height;
+ }
+ }
+ }
+
+ this.RegisterForLayouting (LayoutingType.Height);
+ }
+ }
+ /// <summary>
+ /// Used for binding on dimensions, this property will never hold fixed size, but instead only
+ /// Fit or Stretched
+ /// </summary>
+ [XmlIgnore]public virtual Measure WidthPolicy { get {
+ return Width.IsFit ? Measure.Fit : Measure.Stretched; } }
+ /// <summary>
+ /// Used for binding on dimensions, this property will never hold fixed size, but instead only
+ /// Fit or Stretched
+ /// </summary>
+ [XmlIgnore]public virtual Measure HeightPolicy { get {
+ return Height.IsFit ? Measure.Fit : Measure.Stretched; } }
+ [XmlAttributeAttribute()][DefaultValue(false)]
+ public virtual bool Focusable {
+ get { return focusable; }
+ set {
+ if (focusable == value)
+ return;
+ focusable = value;
+ NotifyValueChanged ("Focusable", focusable);
+ }
+ }
+ [XmlIgnore]public virtual bool HasFocus {
+ get { return hasFocus; }
+ set {
+ if (value == hasFocus)
+ return;
+
+ hasFocus = value;
+ if (hasFocus)
+ onFocused (this, null);
+ else
+ onUnfocused (this, null);
+ NotifyValueChanged ("HasFocus", hasFocus);
+ }
+ }
+ [XmlIgnore]public virtual bool IsActive {
+ get { return isActive; }
+ set {
+ if (value == isActive)
+ return;
+
+ isActive = value;
+ NotifyValueChanged ("IsActive", isActive);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(false)]
+ public virtual bool MouseRepeat {
+ get { return mouseRepeat; }
+ set {
+ if (mouseRepeat == value)
+ return;
+ mouseRepeat = value;
+ NotifyValueChanged ("MouseRepeat", mouseRepeat);
+ }
+ }
+ bool clearBackground = false;
+ [XmlAttributeAttribute()][DefaultValue("Transparent")]
+ public virtual Fill Background {
+ get { return background; }
+ set {
+ if (background == value)
+ return;
+ clearBackground = false;
+ if (value == null)
+ return;
+ background = value;
+ NotifyValueChanged ("Background", background);
+ RegisterForRedraw ();
+ if (background is SolidColor) {
+ if ((Background as SolidColor).Equals (Color.Clear))
+ clearBackground = true;
+ }
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue("White")]
+ public virtual Fill Foreground {
+ get { return foreground; }
+ set {
+ if (foreground == value)
+ return;
+ foreground = value;
+ NotifyValueChanged ("Foreground", foreground);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue("sans,10")]
+ public virtual Font Font {
+ get { return font; }
+ set {
+ if (value == font)
+ return;
+ font = value;
+ NotifyValueChanged ("Font", font);
+ RegisterForGraphicUpdate ();
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(0.0)]
+ public virtual double CornerRadius {
+ get { return cornerRadius; }
+ set {
+ if (value == cornerRadius)
+ return;
+ cornerRadius = value;
+ NotifyValueChanged ("CornerRadius", cornerRadius);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue(0)]
+ public virtual int Margin {
+ get { return margin; }
+ set {
+ if (value == margin)
+ return;
+ margin = value;
+ NotifyValueChanged ("Margin", margin);
+ RegisterForGraphicUpdate ();
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(true)]
+ public virtual bool Visible {
+ get { return isVisible; }
+ set {
+ if (value == isVisible)
+ return;
+
+ isVisible = value;
+
+ if (isVisible)
+ RegisterForLayouting (LayoutingType.Sizing);
+ else {
+ lock (CurrentInterface.UpdateMutex) {
+ Slot.Width = 0;
+ LayoutChanged.Raise (this, new LayoutingEventArgs (LayoutingType.Width));
+ Slot.Height = 0;
+ LayoutChanged.Raise (this, new LayoutingEventArgs (LayoutingType.Height));
+ if (this.parent != null)
+ CurrentInterface.EnqueueForRepaint (this);
+ LastSlots.Width = LastSlots.Height = 0;
+ }
+ }
+
+ //trigger a mouse to handle possible hover changes
+ CurrentInterface.ProcessMouseMove (CurrentInterface.Mouse.X, CurrentInterface.Mouse.Y);
+
+ NotifyValueChanged ("Visible", isVisible);
+ }
+ }
+ [XmlAttributeAttribute][DefaultValue(true)]
+ public virtual bool IsEnabled {
+ get { return isEnabled; }
+ set {
+ if (value == isEnabled)
+ return;
+
+ isEnabled = value;
+
+ if (isEnabled)
+ onEnable (this, null);
+ else
+ onDisable (this, null);
+
+ NotifyValueChanged ("IsEnabled", isEnabled);
+ RegisterForRedraw ();
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue("1,1")]
+ public virtual Size MinimumSize {
+ get { return minimumSize; }
+ set {
+ if (value == minimumSize)
+ return;
+
+ minimumSize = value;
+
+ NotifyValueChanged ("MinimumSize", minimumSize);
+ RegisterForLayouting (LayoutingType.Sizing);
+ }
+ }
+ [XmlAttributeAttribute()][DefaultValue("0,0")]
+ public virtual Size MaximumSize {
+ get { return maximumSize; }
+ set {
+ if (value == maximumSize)
+ return;
+
+ maximumSize = value;
+
+ NotifyValueChanged ("MaximumSize", maximumSize);
+ RegisterForLayouting (LayoutingType.Sizing);
+ }
+ }
+ /// <summary>
+ /// Seek first logical tree upward if logicalParent is set, or seek graphic tree for
+ /// a not null dataSource that will be active for all descendants having dataSource=null
+ /// </summary>
+ [XmlAttributeAttribute][DefaultValue(null)]
+ public virtual object DataSource {
+ set {
+ if (DataSource == value)
+ return;
+
+ DataSourceChangeEventArgs dse = new DataSourceChangeEventArgs (DataSource, null);
+ dataSource = value;
+ dse.NewDataSource = DataSource;
+
+ OnDataSourceChanged (this, dse);
+
+ NotifyValueChanged ("DataSource", DataSource);
+ }
+ get {
+ return dataSource == null ?
+ LogicalParent == null ? null :
+ LogicalParent is GraphicObject ? (LogicalParent as GraphicObject).DataSource : null :
+ dataSource;
+ }
+ }
+ protected virtual void onLogicalParentDataSourceChanged(object sender, DataSourceChangeEventArgs e){
+ if (localDataSourceIsNull)
+ OnDataSourceChanged (this, e);
+ }
+ internal bool localDataSourceIsNull { get { return dataSource == null; } }
+ internal bool localLogicalParentIsNull { get { return logicalParent == null; } }
+
+ public virtual void OnDataSourceChanged(object sender, DataSourceChangeEventArgs e){
+ DataSourceChanged.Raise (this, e);
+ #if DEBUG_BINDING
+ Debug.WriteLine("New DataSource for => {0} \n\t{1}=>{2}", this.ToString(),e.OldDataSource,e.NewDataSource);
+ #endif
+ }
+
+ [XmlAttributeAttribute]
+ public virtual string Style {
+ get { return style; }
+ set {
+ if (value == style)
+ return;
+
+ style = value;
+
+ NotifyValueChanged ("Style", style);
+ }
+ }
+ #endregion
+
+ #region Default and Style Values loading
+ /// <summary> Loads the default values from XML attributes default </summary>
+ public void loadDefaultValues()
+ {
+ #if DEBUG_LOAD
+ Debug.WriteLine ("LoadDefValues for " + this.ToString ());
+ #endif
+
+ Type thisType = this.GetType ();
+
+ if (!string.IsNullOrEmpty (Style)) {
+ if (Interface.DefaultValuesLoader.ContainsKey (Style)) {
+ Interface.DefaultValuesLoader [Style] (this);
+ return;
+ }
+ } else {
+ if (Interface.DefaultValuesLoader.ContainsKey (thisType.FullName)) {
+ Interface.DefaultValuesLoader [thisType.FullName] (this);
+ return;
+ } else if (!Interface.Styling.ContainsKey (thisType.FullName)) {
+ if (Interface.DefaultValuesLoader.ContainsKey (thisType.Name)) {
+ Interface.DefaultValuesLoader [thisType.Name] (this);
+ return;
+ }
+ }
+ }
+
+ List<Style> styling = new List<Style>();
+
+ //Search for a style matching :
+ //1: Full class name, with full namespace
+ //2: class name
+ //3: style may have been registered with their ressource ID minus .style extention
+ // those files being placed in a Styles folder
+ string styleKey = Style;
+ if (!string.IsNullOrEmpty (Style)) {
+ if (Interface.Styling.ContainsKey (Style)) {
+ styling.Add (Interface.Styling [Style]);
+ }
+ }
+ if (Interface.Styling.ContainsKey (thisType.FullName)) {
+ styling.Add (Interface.Styling [thisType.FullName]);
+ if (string.IsNullOrEmpty (styleKey))
+ styleKey = thisType.FullName;
+ }
+ if (Interface.Styling.ContainsKey (thisType.Name)) {
+ styling.Add (Interface.Styling [thisType.Name]);
+ if (string.IsNullOrEmpty (styleKey))
+ styleKey = thisType.Name;
+ }
+
+ if (string.IsNullOrEmpty (styleKey))
+ styleKey = thisType.FullName;
+
+
+ //Reflexion being very slow compared to dyn method or delegates,
+ //I compile the initial values coded in the CustomAttribs of the class,
+ //all other instance of this type would not longer use reflexion to init properly
+ //but will fetch the dynamic initialisation method compiled for this precise type
+ //TODO:measure speed gain.
+ #region Delfault values Loading dynamic compilation
+ DynamicMethod dm = null;
+ ILGenerator il = null;
+
+ dm = new DynamicMethod("dyn_loadDefValues",
+ MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot,
+ CallingConventions.Standard,
+ typeof(void),new Type[] {CompilerServices.TObject},thisType,true);
+
+ il = dm.GetILGenerator(256);
+ il.DeclareLocal(CompilerServices.TObject);
+ il.Emit(OpCodes.Nop);
+ //set local GraphicObject to root object passed as 1st argument
+ il.Emit (OpCodes.Ldarg_0);
+ il.Emit (OpCodes.Stloc_0);
+
+ foreach (EventInfo ei in thisType.GetEvents(BindingFlags.Public | BindingFlags.Instance)) {
+ string expression;
+ if (!getDefaultEvent(ei, styling, out expression))
+ continue;
+ //TODO:dynEventHandler could be cached somewhere, maybe a style instanciator class holding the styling delegate and bound to it.
+ foreach (string exp in CompilerServices.splitOnSemiColumnOutsideAccolades(expression)) {
+ string trimed = exp.Trim();
+ if (trimed.StartsWith ("{", StringComparison.OrdinalIgnoreCase)){
+ il.Emit (OpCodes.Ldloc_0);//load this as 1st arg of event Add
+
+ //push eventInfo as 1st arg of compile
+ il.Emit (OpCodes.Ldloc_0);
+ il.Emit (OpCodes.Call, CompilerServices.miGetType);
+ il.Emit (OpCodes.Ldstr, ei.Name);//push event name
+ il.Emit (OpCodes.Call, CompilerServices.miGetEvent);
+ //push expression as 2nd arg of compile
+ il.Emit (OpCodes.Ldstr, trimed.Substring (1, trimed.Length - 2));
+ //push null as 3rd arg, currentNode, not known when instanciing
+ il.Emit (OpCodes.Ldnull);
+ il.Emit (OpCodes.Callvirt, CompilerServices.miCompileDynEventHandler);
+ il.Emit (OpCodes.Castclass, ei.EventHandlerType);
+ il.Emit (OpCodes.Callvirt, ei.AddMethod);
+ }else
+ Debug.WriteLine("error in styling, event not handled : " + trimed);
+ }
+ }
+
+ foreach (PropertyInfo pi in thisType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
+ if (pi.GetSetMethod () == null)
+ continue;
+ object defaultValue;
+ if (!getDefaultValue (pi, styling, out defaultValue))
+ continue;
+
+ CompilerServices.EmitSetValue (il, pi, defaultValue);
+ }
+ il.Emit(OpCodes.Ret);
+ #endregion
+
+ try {
+ Interface.DefaultValuesLoader[styleKey] = (Interface.LoaderInvoker)dm.CreateDelegate(typeof(Interface.LoaderInvoker));
+ Interface.DefaultValuesLoader[styleKey] (this);
+ } catch (Exception ex) {
+ throw new Exception ("Error applying style <" + styleKey + ">:", ex);
+ }
+ }
+ bool getDefaultEvent(EventInfo ei, List<Style> styling,
+ out string expression){
+ expression = "";
+ if (styling.Count > 0){
+ for (int i = 0; i < styling.Count; i++) {
+ if (styling[i].ContainsKey (ei.Name)){
+ expression = (string)styling[i] [ei.Name];
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ bool getDefaultValue(PropertyInfo pi, List<Style> styling,
+ out object defaultValue){
+ defaultValue = null;
+ string name = "";
+
+ XmlIgnoreAttribute xia = (XmlIgnoreAttribute)pi.GetCustomAttribute (typeof(XmlIgnoreAttribute));
+ if (xia != null)
+ return false;
+ XmlAttributeAttribute xaa = (XmlAttributeAttribute)pi.GetCustomAttribute (typeof(XmlAttributeAttribute));
+ if (xaa != null) {
+ if (string.IsNullOrEmpty (xaa.AttributeName))
+ name = pi.Name;
+ else
+ name = xaa.AttributeName;
+ }
+
+ int styleIndex = -1;
+ if (styling.Count > 0){
+ for (int i = 0; i < styling.Count; i++) {
+ if (styling[i].ContainsKey (name)){
+ styleIndex = i;
+ break;
+ }
+ }
+ }
+ if (styleIndex >= 0){
+ if (pi.PropertyType.IsEnum)//maybe should be in parser..
+ defaultValue = Enum.Parse(pi.PropertyType, (string)styling[styleIndex] [name], true);
+ else
+ defaultValue = styling[styleIndex] [name];
+ }else {
+ DefaultValueAttribute dv = (DefaultValueAttribute)pi.GetCustomAttribute (typeof (DefaultValueAttribute));
+ if (dv == null)
+ return false;
+ defaultValue = dv.Value;
+ }
+ return true;
+ }
+ #endregion
+
+ public virtual GraphicObject FindByName(string nameToFind){
+ return string.Equals(nameToFind, name, StringComparison.Ordinal) ? this : null;
+ }
+ public virtual bool Contains(GraphicObject goToFind){
+ return false;
+ }
+
+ #region Queuing
+ /// <summary>
+ /// Register old and new slot for clipping
+ /// </summary>
+ public virtual void ClippingRegistration(){
+ IsQueueForRedraw = false;
+ if (Parent == null)
+ return;
+ Parent.RegisterClip (LastPaintedSlot);
+ Parent.RegisterClip (Slot);
+ }
+ /// <summary>
+ /// Add clip rectangle to this.clipping and propagate up to root
+ /// </summary>
+ /// <param name="clip">Clip rectangle</param>
+ public virtual void RegisterClip(Rectangle clip){
+ if (CacheEnabled && !IsDirty)
+ Clipping.AddRectangle (clip + ClientRectangle.Position);
+ if (Parent == null)
+ return;
+ GraphicObject p = Parent as GraphicObject;
+ if (p?.IsDirty == true && p?.CacheEnabled == true)
+ return;
+ Parent.RegisterClip (clip + Slot.Position + ClientRectangle.Position);
+ }
+ /// <summary> Full update, taking care of sizing policy </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void RegisterForGraphicUpdate ()
+ {
+ IsDirty = true;
+ if (Width.IsFit || Height.IsFit)
+ RegisterForLayouting (LayoutingType.Sizing);
+ else if (RegisteredLayoutings == LayoutingType.None)
+ CurrentInterface.EnqueueForRepaint (this);
+ }
+ /// <summary> query an update of the content, a redraw </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void RegisterForRedraw ()
+ {
+ IsDirty = true;
+ if (RegisteredLayoutings == LayoutingType.None)
+ CurrentInterface.EnqueueForRepaint (this);
+ }
+ #endregion
+
+ #region Layouting
+
+ /// <summary> return size of content + margins </summary>
+ protected virtual int measureRawSize (LayoutingType lt) {
+ return lt == LayoutingType.Width ?
+ contentSize.Width + 2 * Margin: contentSize.Height + 2 * Margin;
+ }
+ /// <summary> By default in groups, LayoutingType.ArrangeChildren is reset </summary>
+ public virtual void ChildrenLayoutingConstraints(ref LayoutingType layoutType){
+ }
+ public virtual bool ArrangeChildren { get { return false; } }
+ public virtual void RegisterForLayouting(LayoutingType layoutType){
+ if (Parent == null)
+ return;
+ lock (CurrentInterface.LayoutMutex) {
+ //prevent queueing same LayoutingType for this
+ layoutType &= (~RegisteredLayoutings);
+
+ if (layoutType == LayoutingType.None)
+ return;
+ //dont set position for stretched item
+ if (Width == Measure.Stretched)
+ layoutType &= (~LayoutingType.X);
+ if (Height == Measure.Stretched)
+ layoutType &= (~LayoutingType.Y);
+
+ if (!ArrangeChildren)
+ layoutType &= (~LayoutingType.ArrangeChildren);
+
+ //apply constraints depending on parent type
+ if (Parent is GraphicObject)
+ (Parent as GraphicObject).ChildrenLayoutingConstraints (ref layoutType);
+
+// //prevent queueing same LayoutingType for this
+// layoutType &= (~RegisteredLayoutings);
+
+ if (layoutType == LayoutingType.None)
+ return;
+
+ //enqueue LQI LayoutingTypes separately
+ if (layoutType.HasFlag (LayoutingType.Width))
+ CurrentInterface.LayoutingQueue.Enqueue (new LayoutingQueueItem (LayoutingType.Width, this));
+ if (layoutType.HasFlag (LayoutingType.Height))
+ CurrentInterface.LayoutingQueue.Enqueue (new LayoutingQueueItem (LayoutingType.Height, this));
+ if (layoutType.HasFlag (LayoutingType.X))
+ CurrentInterface.LayoutingQueue.Enqueue (new LayoutingQueueItem (LayoutingType.X, this));
+ if (layoutType.HasFlag (LayoutingType.Y))
+ CurrentInterface.LayoutingQueue.Enqueue (new LayoutingQueueItem (LayoutingType.Y, this));
+ if (layoutType.HasFlag (LayoutingType.ArrangeChildren))
+ CurrentInterface.LayoutingQueue.Enqueue (new LayoutingQueueItem (LayoutingType.ArrangeChildren, this));
+ }
+ }
+
+ /// <summary> trigger dependant sizing component update </summary>
+ public virtual void OnLayoutChanges(LayoutingType layoutType)
+ {
+ #if DEBUG_LAYOUTING
+ CurrentInterface.currentLQI.Slot = LastSlots;
+ CurrentInterface.currentLQI.NewSlot = Slot;
+ Debug.WriteLine ("\t\t{0} => {1}",LastSlots,Slot);
+ #endif
+
+ switch (layoutType) {
+ case LayoutingType.Width:
+ RegisterForLayouting (LayoutingType.X);
+ break;
+ case LayoutingType.Height:
+ RegisterForLayouting (LayoutingType.Y);
+ break;
+ }
+ LayoutChanged.Raise (this, new LayoutingEventArgs (layoutType));
+ }
+ internal protected void raiseLayoutChanged(LayoutingEventArgs e){
+ LayoutChanged.Raise (this, e);
+ }
+ /// <summary> Update layout component only one at a time, this is where the computation of alignement
+ /// and size take place.
+ /// The redrawing will only be triggered if final slot size has changed </summary>
+ /// <returns><c>true</c>, if layouting was possible, <c>false</c> if conditions were not
+ /// met and LQI has to be re-queued</returns>
+ public virtual bool UpdateLayout (LayoutingType layoutType)
+ {
+ //unset bit, it would be reset if LQI is re-queued
+ registeredLayoutings &= (~layoutType);
+
+ switch (layoutType) {
+ case LayoutingType.X:
+ if (Left == 0) {
+
+ if (Parent.RegisteredLayoutings.HasFlag (LayoutingType.Width) ||
+ RegisteredLayoutings.HasFlag (LayoutingType.Width))
+ return false;
+
+ switch (HorizontalAlignment) {
+ case HorizontalAlignment.Left:
+ Slot.X = 0;
+ break;
+ case HorizontalAlignment.Right:
+ Slot.X = Parent.ClientRectangle.Width - Slot.Width;
+ break;
+ case HorizontalAlignment.Center:
+ Slot.X = Parent.ClientRectangle.Width / 2 - Slot.Width / 2;
+ break;
+ }
+ } else
+ Slot.X = Left;
+
+ if (LastSlots.X == Slot.X)
+ break;
+
+ IsDirty = true;
+
+ OnLayoutChanges (layoutType);
+
+ LastSlots.X = Slot.X;
+ break;
+ case LayoutingType.Y:
+ if (Top == 0) {
+
+ if (Parent.RegisteredLayoutings.HasFlag (LayoutingType.Height) ||
+ RegisteredLayoutings.HasFlag (LayoutingType.Height))
+ return false;
+
+ switch (VerticalAlignment) {
+ case VerticalAlignment.Top://this could be processed even if parent Height is not known
+ Slot.Y = 0;
+ break;
+ case VerticalAlignment.Bottom:
+ Slot.Y = Parent.ClientRectangle.Height - Slot.Height;
+ break;
+ case VerticalAlignment.Center:
+ Slot.Y = Parent.ClientRectangle.Height / 2 - Slot.Height / 2;
+ break;
+ }
+ } else
+ Slot.Y = Top;
+
+ if (LastSlots.Y == Slot.Y)
+ break;
+
+ IsDirty = true;
+
+ OnLayoutChanges (layoutType);
+
+ LastSlots.Y = Slot.Y;
+ break;
+ case LayoutingType.Width:
+ if (Visible) {
+ if (Width.IsFixed)
+ Slot.Width = Width;
+ else if (Width == Measure.Fit) {
+ Slot.Width = measureRawSize (LayoutingType.Width);
+ } else if (Parent.RegisteredLayoutings.HasFlag (LayoutingType.Width))
+ return false;
+ else if (Width == Measure.Stretched)
+ Slot.Width = Parent.ClientRectangle.Width;
+ else
+ Slot.Width = (int)Math.Round ((double)(Parent.ClientRectangle.Width * Width) / 100.0);
+
+ if (Slot.Width < 0)
+ return false;
+
+ //size constrain
+ if (Slot.Width < MinimumSize.Width) {
+ Slot.Width = MinimumSize.Width;
+ //NotifyValueChanged ("WidthPolicy", Measure.Stretched);
+ } else if (Slot.Width > MaximumSize.Width && MaximumSize.Width > 0) {
+ Slot.Width = MaximumSize.Width;
+ //NotifyValueChanged ("WidthPolicy", Measure.Stretched);
+ }
+ } else
+ Slot.Width = 0;
+
+ if (LastSlots.Width == Slot.Width)
+ break;
+
+ IsDirty = true;
+
+ OnLayoutChanges (layoutType);
+
+ LastSlots.Width = Slot.Width;
+ break;
+ case LayoutingType.Height:
+ if (Visible) {
+ if (Height.IsFixed)
+ Slot.Height = Height;
+ else if (Height == Measure.Fit) {
+ Slot.Height = measureRawSize (LayoutingType.Height);
+ } else if (Parent.RegisteredLayoutings.HasFlag (LayoutingType.Height))
+ return false;
+ else if (Height == Measure.Stretched)
+ Slot.Height = Parent.ClientRectangle.Height;
+ else
+ Slot.Height = (int)Math.Round ((double)(Parent.ClientRectangle.Height * Height) / 100.0);
+
+ if (Slot.Height < 0)
+ return false;
+
+ //size constrain
+ if (Slot.Height < MinimumSize.Height) {
+ Slot.Height = MinimumSize.Height;
+ //NotifyValueChanged ("HeightPolicy", Measure.Stretched);
+ } else if (Slot.Height > MaximumSize.Height && MaximumSize.Height > 0) {
+ Slot.Height = MaximumSize.Height;
+ //NotifyValueChanged ("HeightPolicy", Measure.Stretched);
+ }
+ } else
+ Slot.Height = 0;
+
+ if (LastSlots.Height == Slot.Height)
+ break;
+
+ IsDirty = true;
+
+ OnLayoutChanges (layoutType);
+
+ LastSlots.Height = Slot.Height;
+ break;
+ }
+
+ //if no layouting remains in queue for item, registre for redraw
+ if (this.registeredLayoutings == LayoutingType.None && IsDirty)
+ CurrentInterface.EnqueueForRepaint (this);
+
+ return true;
+ }
+ #endregion
+
+ #region Rendering
+ /// <summary> This is the common overridable drawing routine to create new widget </summary>
+ protected virtual void onDraw(Context gr)
+ {
+ Rectangle rBack = new Rectangle (Slot.Size);
+
+ Background.SetAsSource (gr, rBack);
+ CairoHelpers.CairoRectangle(gr,rBack,cornerRadius);
+ gr.Fill ();
+ }
+
+ /// <summary>
+ /// Internal drawing context creation on a cached surface limited to slot size
+ /// this trigger the effective drawing routine </summary>
+ protected virtual void RecreateCache ()
+ {
+ int stride = 4 * Slot.Width;
+
+ int bmpSize = Math.Abs (stride) * Slot.Height;
+ bmp = new byte[bmpSize];
+ IsDirty = false;
+ using (ImageSurface draw =
+ new ImageSurface(bmp, Format.Argb32, Slot.Width, Slot.Height, stride)) {
+ using (Context gr = new Context (draw)) {
+ gr.Antialias = Interface.Antialias;
+ onDraw (gr);
+ }
+ draw.Flush ();
+ }
+ }
+ protected virtual void UpdateCache(Context ctx){
+ Rectangle rb = Slot + Parent.ClientRectangle.Position;
+ using (ImageSurface cache = new ImageSurface (bmp, Format.Argb32, Slot.Width, Slot.Height, 4 * Slot.Width)) {
+ if (clearBackground) {
+ ctx.Save ();
+ ctx.Operator = Operator.Clear;
+ ctx.Rectangle (rb);
+ ctx.Fill ();
+ ctx.Restore ();
+ }
+ ctx.SetSourceSurface (cache, rb.X, rb.Y);
+ ctx.Paint ();
+ }
+ //Clipping.clearAndClip (ctx);
+ Clipping.Reset();
+ }
+ /// <summary> Chained painting routine on the parent context of the actual cached version
+ /// of the widget </summary>
+ public virtual void Paint (ref Context ctx)
+ {
+ //TODO:this test should not be necessary
+ if (Slot.Height < 0 || Slot.Width < 0 || parent == null)
+ return;
+ lock (this) {
+ if (cacheEnabled) {
+ if (Slot.Width > Interface.MaxCacheSize || Slot.Height > Interface.MaxCacheSize)
+ cacheEnabled = false;
+ }
+
+ if (cacheEnabled) {
+ if (IsDirty)
+ RecreateCache ();
+
+ UpdateCache (ctx);
+ if (!isEnabled)
+ paintDisabled (ctx, Slot + Parent.ClientRectangle.Position);
+ } else {
+ Rectangle rb = Slot + Parent.ClientRectangle.Position;
+ ctx.Save ();
+
+ ctx.Translate (rb.X, rb.Y);
+
+ onDraw (ctx);
+ if (!isEnabled)
+ paintDisabled (ctx, Slot);
+
+ ctx.Restore ();
+ }
+ LastPaintedSlot = Slot;
+ }
+ }
+ void paintDisabled(Context gr, Rectangle rb){
+ gr.Operator = Operator.Xor;
+ gr.SetSourceRGBA (0.6, 0.6, 0.6, 0.3);
+ gr.Rectangle (rb);
+ gr.Fill ();
+ gr.Operator = Operator.Over;
+ }
+ #endregion
+
+ #region Keyboard handling
+ public virtual void onKeyDown(object sender, KeyboardKeyEventArgs e){
+ KeyDown.Raise (sender, e);
+ }
+ public virtual void onKeyUp(object sender, KeyboardKeyEventArgs e){
+ KeyUp.Raise (sender, e);
+ }
+ public virtual void onKeyPress(object sender, KeyPressEventArgs e){
+ KeyPress.Raise (sender, e);
+ }
+ #endregion
+
+ #region Mouse handling
+ public virtual bool MouseIsIn(Point m)
+ {
+ try {
+ if (!(Visible & isEnabled))
+ return false;
+ if (ScreenCoordinates (Slot).ContainsOrIsEqual (m)) {
+ Scroller scr = Parent as Scroller;
+ if (scr == null) {
+ if (Parent is GraphicObject)
+ return (Parent as GraphicObject).MouseIsIn (m);
+ else return true;
+ }
+ return scr.MouseIsIn (scr.savedMousePos);
+ }
+ } catch (Exception ex) {
+ return false;
+ }
+ return false;
+ }
+ public virtual void checkHoverWidget(MouseMoveEventArgs e)
+ {
+ if (CurrentInterface.HoverWidget != this) {
+ CurrentInterface.HoverWidget = this;
+ onMouseEnter (this, e);
+ }
+
+ this.onMouseMove (this, e);//without this, window border doesn't work, should be removed
+ }
+ public virtual void onMouseMove(object sender, MouseMoveEventArgs e)
+ {
+ //bubble event to the top
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseMove(sender,e);
+
+ MouseMove.Raise (this, e);
+ }
+ public virtual void onMouseDown(object sender, MouseButtonEventArgs e){
+ if (CurrentInterface.eligibleForDoubleClick == this && CurrentInterface.clickTimer.ElapsedMilliseconds < Interface.DoubleClick)
+ onMouseDoubleClick (this, e);
+ else
+ currentInterface.clickTimer.Restart();
+ CurrentInterface.eligibleForDoubleClick = null;
+
+ if (CurrentInterface.ActiveWidget == null)
+ CurrentInterface.ActiveWidget = this;
+ if (this.Focusable && !Interface.FocusOnHover) {
+ BubblingMouseButtonEventArg be = e as BubblingMouseButtonEventArg;
+ if (be.Focused == null) {
+ be.Focused = this;
+ CurrentInterface.FocusedWidget = this;
+ }
+ }
+ //bubble event to the top
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseDown(sender,e);
+
+ MouseDown.Raise (this, e);
+ }
+ public virtual void onMouseUp(object sender, MouseButtonEventArgs e){
+ //bubble event to the top
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseUp(sender,e);
+
+ MouseUp.Raise (this, e);
+
+ if (MouseIsIn (e.Position) && IsActive) {
+ if (CurrentInterface.clickTimer.ElapsedMilliseconds < Interface.DoubleClick)
+ CurrentInterface.eligibleForDoubleClick = this;
+ onMouseClick (this, e);
+ }
+ }
+ public virtual void onMouseClick(object sender, MouseButtonEventArgs e){
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseClick(sender,e);
+ MouseClick.Raise (this, e);
+ }
+ public virtual void onMouseDoubleClick(object sender, MouseButtonEventArgs e){
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseDoubleClick(sender,e);
+ MouseDoubleClick.Raise (this, e);
+ }
+ public virtual void onMouseWheel(object sender, MouseWheelEventArgs e){
+ GraphicObject p = Parent as GraphicObject;
+ if (p != null)
+ p.onMouseWheel(sender,e);
+
+ MouseWheelChanged.Raise (this, e);
+ }
+ public virtual void onMouseEnter(object sender, MouseMoveEventArgs e)
+ {
+ #if DEBUG_FOCUS
+ Debug.WriteLine("MouseEnter => " + this.ToString());
+ #endif
+ MouseEnter.Raise (this, e);
+ }
+ public virtual void onMouseLeave(object sender, MouseMoveEventArgs e)
+ {
+ #if DEBUG_FOCUS
+ Debug.WriteLine("MouseLeave => " + this.ToString());
+ #endif
+ MouseLeave.Raise (this, e);
+ }
+ #endregion
+
+ protected virtual void onFocused(object sender, EventArgs e){
+ #if DEBUG_FOCUS
+ Debug.WriteLine("Focused => " + this.ToString());
+ #endif
+ Focused.Raise (this, e);
+ }
+ protected virtual void onUnfocused(object sender, EventArgs e){
+ #if DEBUG_FOCUS
+ Debug.WriteLine("UnFocused => " + this.ToString());
+ #endif
+ Unfocused.Raise (this, e);
+ }
+ public virtual void onEnable(object sender, EventArgs e){
+ Enabled.Raise (this, e);
+ }
+ public virtual void onDisable(object sender, EventArgs e){
+ Disabled.Raise (this, e);
+ }
+ protected virtual void onParentChanged(object sender, DataSourceChangeEventArgs e) {
+ ParentChanged.Raise (this, e);
+ if (logicalParent == null)
+ LogicalParentChanged.Raise (this, e);
+ }
+ protected virtual void onLogicalParentChanged(object sender, DataSourceChangeEventArgs e) {
+ LogicalParentChanged.Raise (this, e);
+ }
+ #region IXmlSerializable
+ public virtual System.Xml.Schema.XmlSchema GetSchema ()
+ {
+ return null;
+ }
+// void affectMember(string name, string value){
+// Type thisType = this.GetType ();
+//
+// if (string.IsNullOrEmpty (value))
+// return;
+//
+// MemberInfo mi = thisType.GetMember (name).FirstOrDefault();
+// if (mi == null) {
+// Debug.WriteLine ("XML: Unknown attribute in " + thisType.ToString() + " : " + name);
+// return;
+// }
+// if (mi.MemberType == MemberTypes.Event) {
+// this.Bindings.Add (new Binding (new MemberReference(this, mi), value));
+// return;
+// }
+// if (mi.MemberType == MemberTypes.Property) {
+// PropertyInfo pi = mi as PropertyInfo;
+//
+// if (pi.GetSetMethod () == null) {
+// Debug.WriteLine ("XML: Read only property in " + thisType.ToString() + " : " + name);
+// return;
+// }
+//
+// XmlAttributeAttribute xaa = (XmlAttributeAttribute)pi.GetCustomAttribute (typeof(XmlAttributeAttribute));
+// if (xaa != null) {
+// if (!string.IsNullOrEmpty (xaa.AttributeName))
+// name = xaa.AttributeName;
+// }
+// if (value.StartsWith("{",StringComparison.Ordinal)) {
+// //binding
+// if (!value.EndsWith("}", StringComparison.Ordinal))
+// throw new Exception (string.Format("XML:Malformed binding: {0}", value));
+//
+// this.Bindings.Add (new Binding (new MemberReference(this, pi), value.Substring (1, value.Length - 2)));
+// return;
+// }
+// if (pi.GetCustomAttribute (typeof(XmlIgnoreAttribute)) != null)
+// return;
+// if (xaa == null)//not define as xmlAttribute
+// return;
+//
+// if (pi.PropertyType == typeof(string)) {
+// pi.SetValue (this, value, null);
+// return;
+// }
+//
+// if (pi.PropertyType.IsEnum) {
+// pi.SetValue (this, Enum.Parse (pi.PropertyType, value), null);
+// } else {
+// MethodInfo me = pi.PropertyType.GetMethod ("Parse", new Type[] { typeof(string) });
+// pi.SetValue (this, me.Invoke (null, new string[] { value }), null);
+// }
+// }
+// }
+ public virtual void ReadXml (System.Xml.XmlReader reader)
+ {
+ if (reader.HasAttributes) {
+
+ style = reader.GetAttribute ("Style");
+
+ loadDefaultValues ();
+
+ while (reader.MoveToNextAttribute ()) {
+ if (reader.Name == "Style")
+ continue;
+
+ //affectMember (reader.Name, reader.Value);
+ }
+ reader.MoveToElement ();
+ }else
+ loadDefaultValues ();
+ }
+ public virtual void WriteXml (System.Xml.XmlWriter writer)
+ {
+ foreach (PropertyInfo pi in this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
+ if (pi.GetSetMethod () == null)
+ continue;
+
+ bool isAttribute = false;
+ bool hasDefaultValue = false;
+ bool ignore = false;
+ string name = "";
+ object value = null;
+ Type valueType = null;
+
+
+ MemberInfo mi = pi.GetGetMethod ();
+
+ if (mi == null)
+ continue;
+
+ value = pi.GetValue (this, null);
+ valueType = pi.PropertyType;
+ name = pi.Name;
+
+
+
+ object[] att = pi.GetCustomAttributes (false);
+
+ foreach (object o in att) {
+ XmlAttributeAttribute xaa = o as XmlAttributeAttribute;
+ if (xaa != null) {
+ isAttribute = true;
+ if (string.IsNullOrEmpty (xaa.AttributeName))
+ name = pi.Name;
+ else
+ name = xaa.AttributeName;
+ continue;
+ }
+
+ XmlIgnoreAttribute xia = o as XmlIgnoreAttribute;
+ if (xia != null) {
+ ignore = true;
+ continue;
+ }
+
+ DefaultValueAttribute dv = o as DefaultValueAttribute;
+ if (dv != null) {
+ if (dv.Value.Equals (value))
+ hasDefaultValue = true;
+ if (dv.Value.ToString () == value.ToString ())
+ hasDefaultValue = true;
+
+ continue;
+ }
+
+
+ }
+
+ if (hasDefaultValue || ignore || value==null)
+ continue;
+
+ if (isAttribute)
+ writer.WriteAttributeString (name, value.ToString ());
+ else {
+ if (valueType.GetInterface ("IXmlSerializable") == null)
+ continue;
+
+ (pi.GetValue (this, null) as IXmlSerializable).WriteXml (writer);
+ }
+ }
+ foreach (EventInfo ei in this.GetType().GetEvents()) {
+ FieldInfo fi = this.GetType().GetField(ei.Name,
+ BindingFlags.NonPublic |
+ BindingFlags.Instance |
+ BindingFlags.GetField);
+
+ Delegate dg = (System.Delegate)fi.GetValue (this);
+
+ if (dg == null)
+ continue;
+
+ foreach (Delegate d in dg.GetInvocationList()) {
+ if (!d.Method.Name.StartsWith ("<"))//Skipping empty handler, not clear it's trikky
+ writer.WriteAttributeString (ei.Name, d.Method.Name);
+ }
+ }
+ }
+ #endregion
+
+ #region ICloneable implementation
+ public object Clone ()
+ {
+ Type type = this.GetType ();
+ GraphicObject result = (GraphicObject)Activator.CreateInstance (type);
+ result.CurrentInterface = CurrentInterface;
+
+ foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
+ if (pi.GetSetMethod () == null)
+ continue;
+
+ if (pi.GetCustomAttribute<XmlIgnoreAttribute> () != null)
+ continue;
+ if (pi.Name == "DataSource")
+ continue;
+
+ pi.SetValue(result, pi.GetValue(this));
+ }
+ return result;
+ }
+ #endregion
+ /// <summary>
+ /// full GraphicTree clone with binding definition
+ /// </summary>
+
+ public override string ToString ()
+ {
+ string tmp ="";
+
+ if (Parent != null)
+ tmp = Parent.ToString () + tmp;
+ #if DEBUG_LAYOUTING
+ return Name == "unamed" ? tmp + "." + this.GetType ().Name + uid.ToString(): tmp + "." + Name;
+ #else
+ return Name == "unamed" ? tmp + "." + this.GetType ().Name : tmp + "." + Name;
+ #endif
+ }
+ }
+}