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, IBindable { #region IBindable implementation List bindings = new List (); public List Bindings { get { return bindings; } } #endregion internal static ulong currentUid = 0; internal ulong uid = 0; Interface currentInterface = null; 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 ValueChanged; public virtual void NotifyValueChanged(string MemberName, object _value) { ValueChanged.Raise(this, new ValueChangeEventArgs(MemberName, _value)); } #endregion #region CTOR public GraphicObject () { uid = currentUid; currentUid++; } #endregion internal protected virtual void initialize(){ loadDefaultValues (); } #region private fields LayoutingType registeredLayoutings = LayoutingType.All; ILayoutable logicalParent; ILayoutable parent; string name = "unamed"; 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; object dataSource; string style; #endregion #region public fields /// /// Current size and position computed during layouting pass /// public Rectangle Slot = new Rectangle (); /// /// keep last slot components for each layouting pass to track /// changes and trigger update of other component accordingly /// public Rectangle LastSlots; /// /// 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 /// public Rectangle LastPaintedSlot; /// Prevent requeuing multiple times the same widget public bool IsQueueForRedraw = false; /// Random value placeholder public object Tag; /// drawing Cache, if null, a redraw is done, cached or not public byte[] bmp; /// /// 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) /// 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 /// /// Parent in the graphic tree, used for rendering and layouting /// [XmlIgnore]public virtual ILayoutable Parent { get { return parent; } set { parent = value; } } [XmlIgnore]public ILayoutable LogicalParent { get { return logicalParent == null ? Parent : logicalParent; } set { logicalParent = value; } } [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 MouseWheelChanged; public event EventHandler MouseUp; public event EventHandler MouseDown; public event EventHandler MouseClick; public event EventHandler MouseDoubleClick; public event EventHandler MouseMove; public event EventHandler MouseEnter; public event EventHandler MouseLeave; public event EventHandler KeyDown; public event EventHandler KeyUp; public event EventHandler KeyPress; public event EventHandler Focused; public event EventHandler Unfocused; public event EventHandler Enabled; public event EventHandler Disabled; public event EventHandler LayoutChanged; #endregion #region public properties [XmlAttributeAttribute()][DefaultValue(true)] public virtual bool CacheEnabled { get { return cacheEnabled; } set { if (cacheEnabled == value) return; cacheEnabled = value; NotifyValueChanged ("CacheEnabled", cacheEnabled); } } [XmlAttributeAttribute()][DefaultValue(true)] public virtual bool ClipToClientRect { get { return clipToClientRect; } set { if (clipToClientRect == value) return; clipToClientRect = value; NotifyValueChanged ("ClipToClientRect", clipToClientRect); this.RegisterForRedraw (); } } [XmlAttributeAttribute()][DefaultValue("unamed")] public virtual string Name { get { return name; } set { if (name == value) return; name = value; NotifyValueChanged("Name", verticalAlignment); } } [XmlAttributeAttribute ()][DefaultValue(VerticalAlignment.Center)] public virtual VerticalAlignment VerticalAlignment { get { return verticalAlignment; } set { if (verticalAlignment == value) return; verticalAlignment = value; NotifyValueChanged("VerticalAlignment", verticalAlignment); } } [XmlAttributeAttribute()][DefaultValue(HorizontalAlignment.Center)] public virtual HorizontalAlignment HorizontalAlignment { get { return horizontalAlignment; } set { if (horizontalAlignment == value) return; horizontalAlignment = value; NotifyValueChanged("HorizontalAlignment", horizontalAlignment); } } [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); } } /// /// When set to True, the 's width and height will be set to Fit. /// [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("Stretched")] public virtual Measure Width { get { return 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("Stretched")] public virtual Measure Height { get { return 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); } } /// /// Used for binding on dimensions, this property will never hold fixed size, but instead only /// Fit or Stretched /// [XmlIgnore]public virtual Measure WidthPolicy { get { return Width.Units == Unit.Percent || Width.IsFixed ? Measure.Stretched : Measure.Fit; } } /// /// Used for binding on dimensions, this property will never hold fixed size, but instead only /// Fit or Stretched /// [XmlIgnore]public virtual Measure HeightPolicy { get { return Height.Units == Unit.Percent || Height.IsFixed ? Measure.Stretched : Measure.Fit; } } [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; 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); } } [XmlAttributeAttribute()][DefaultValue("Transparent")] public virtual Fill Background { get { return background; } set { if (background == value) return; background = value; NotifyValueChanged ("Background", background); RegisterForRedraw (); } } [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; //ensure main win doesn't keep hidden childrens ref if (!isVisible && this.Contains (CurrentInterface.HoverWidget)) CurrentInterface.HoverWidget = null; if (Parent is GraphicObject) (Parent as GraphicObject).RegisterForLayouting (LayoutingType.Sizing); if (Parent is GenericStack) (Parent as GraphicObject).RegisterForLayouting (LayoutingType.ArrangeChildren); if (isVisible) RegisterForLayouting (LayoutingType.Sizing); CurrentInterface.EnqueueForRepaint (this); 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); } } [XmlAttributeAttribute][DefaultValue(null)] public virtual object DataSource { set { if (dataSource == value) return; #if DEBUG_BINDING Debug.WriteLine("******************************"); Debug.WriteLine("New DataSource for => " + this.ToString()); Debug.WriteLine("\t- " + DataSource); Debug.WriteLine("\t+ " + value); #endif this.ClearBinding (); dataSource = value; this.ResolveBindings(); NotifyValueChanged ("DataSource", dataSource); } get { return dataSource == null ? LogicalParent == null ? null : LogicalParent is GraphicObject ? (LogicalParent as GraphicObject).DataSource : null : dataSource; } } [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 /// Loads the default values from XML attributes default 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