File: UI\MobileControls\Style.cs
Project: ndp\fx\src\mit\System\Web\System.Web.Mobile.csproj (System.Web.Mobile)
//------------------------------------------------------------------------------
// <copyright file="Style.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Design;
using System.Diagnostics;
using System.Globalization;
using System.Web;
using System.Web.UI;
using System.Web.UI.Design.WebControls;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
 
using WebCtrlStyle = System.Web.UI.WebControls.Style;
using System.Security.Permissions;
 
namespace System.Web.UI.MobileControls
{
 
    /*
     * Mobile Style class.
     * This class can be used to define external styles that can be referenced by other controls.
     */
    /// <include file='doc\Style.uex' path='docs/doc[@for="Style"]/*' />
    [
        ControlBuilderAttribute(typeof(MobileControlBuilder)),
        TypeConverterAttribute(typeof(ExpandableObjectConverter))
    ]
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    [Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
    public class Style : IParserAccessor, ITemplateable, IStateManager, ICloneable
    {
        //  registers styles and retrieves keys used for storing properties in internal hashtable
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.AlignmentKey"]/*' />
        public static readonly Object
            AlignmentKey = RegisterStyle("Alignment", typeof(Alignment)    , System.Web.UI.MobileControls.Alignment.NotSet, true),
            WrappingKey  = RegisterStyle("Wrapping" , typeof(Wrapping)     , System.Web.UI.MobileControls.Wrapping.NotSet , true),
            BoldKey      = RegisterStyle("Bold",      typeof(BooleanOption), BooleanOption.NotSet, true),
            ItalicKey    = RegisterStyle("Italic",    typeof(BooleanOption), BooleanOption.NotSet, true),
            FontSizeKey  = RegisterStyle("FontSize" , typeof(FontSize)     , System.Web.UI.MobileControls.FontSize.NotSet , true),
            FontNameKey  = RegisterStyle("FontName" , typeof(String)       , String.Empty    , true),
            ForeColorKey = RegisterStyle("ForeColor", typeof(Color)        , Color.Empty     , true),
            BackColorKey = RegisterStyle("BackColor", typeof(Color)        , Color.Empty     , false);
 
        private bool _marked = false;       //  used by IStateManager
        private MobileControl _control;     //  containing control
        private Style _referredStyle;       //  referred style
        private bool _checkedStyleReference;//  referred style is valid.
        private StateBag _state;            //  name => object pairs
        private DeviceSpecific _deviceSpecific;
        private FontInfo _font;
        private Style _cachedParentStyle;
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.Style"]/*' />
        public Style()
        {
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.Clone"]/*' />
        public object Clone()
        {
            Style clone = new Style();
            foreach(String key in State.Keys)
            {
                clone.State[key] = State[key];
            }
            clone._control = _control;
            return clone;
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.ApplyTo"]/*' />
        public void ApplyTo(WebControl control)
        {
            control.Font.Name = (String)this[FontNameKey, true];
            switch ((FontSize)this[FontSizeKey, true])
            {
                case FontSize.Small:
                    control.Font.Size = FontUnit.Small;
                    break;
                case FontSize.Large:
                    control.Font.Size = FontUnit.Large;
                    break;
                default:
                    control.Font.Size = FontUnit.Medium;
                    break;
            }
            control.Font.Bold   = ((BooleanOption)this[BoldKey, true]) == BooleanOption.True;
            control.Font.Italic = ((BooleanOption)this[ItalicKey, true]) == BooleanOption.True;
            control.ForeColor   = (Color)this[ForeColorKey, true];
            control.BackColor   = (Color)this[BackColorKey, true];
        }
 
        internal void ApplyTo(WebCtrlStyle style)
        {
            style.Font.Bold     = ((BooleanOption)this[BoldKey, true]) == BooleanOption.True;
            style.Font.Italic   = ((BooleanOption)this[ItalicKey, true]) == BooleanOption.True;
            style.Font.Name     = (String)this[FontNameKey, true];
            style.ForeColor     = (Color)this[ForeColorKey, true];
            style.BackColor     = (Color)this[BackColorKey, true];
 
            switch ((FontSize)this[FontSizeKey, true])
            {
                case FontSize.Large :
                    style.Font.Size = FontUnit.Larger;
                    break;
 
                case FontSize.Small :
                    style.Font.Size = FontUnit.Smaller;
                    break;
 
                default :
                    break;
            }
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.State"]/*' />
        protected internal StateBag State
        {
            get
            {
                if (_state == null)
                {
                    _state = new StateBag();
                    if (((IStateManager)this).IsTrackingViewState)
                    {
                        ((IStateManager)_state).TrackViewState();
                    }
                }
 
                return _state;
            }
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.IStateManager.IsTrackingViewState"]/*' />
        /// <internalonly/>
        protected bool IsTrackingViewState
        {
            get
            {
                return _marked;
            }
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.IStateManager.TrackViewState"]/*' />
        /// <internalonly/>
        protected void TrackViewState()
        {
            _marked = true;
            if (_state != null)
            {
                ((IStateManager)_state).TrackViewState();
            }
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.IStateManager.SaveViewState"]/*' />
        /// <internalonly/>
        protected Object SaveViewState()
        {
            return _state != null ? ((IStateManager)_state).SaveViewState() : null;
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.IStateManager.LoadViewState"]/*' />
        /// <internalonly/>
        protected void LoadViewState(Object state)
        {
            if (state != null)
            {
                // mark _referredStyle as dirty to force reload from viewstate.
                Refresh();
                
                ((IStateManager)State).LoadViewState(state);
            }
        }
 
        internal void SetDirty()
        {
            // VSWHIDBEY 236464. The bag needs to be set dirty not individual items.
            State.SetDirty(true);
/*            
            foreach (StateItem item in State.Values)
            {
                item.IsDirty = true;
            }
*/
        }
 
        internal void Refresh()
        {
            _referredStyle = null;
            _checkedStyleReference = false;
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.Control"]/*' />
        [
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        public MobileControl Control
        {
            get
            {
                return _control;
            }
        }
 
        internal void SetControl(MobileControl control)
        {
            _control = control;
            _cachedParentStyle = null;
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.Name"]/*' />
        [
            Browsable(false),
            DefaultValue(""),
            MobileSysDescription(SR.Style_Name),
            NotifyParentProperty(true),
        ]
        public String Name
        {
            get
            {
                String name = (String)State["Name"];
                return name != null ? name : String.Empty;
            }
            set
            {
                State["Name"] = value;
            }
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.StyleReference"]/*' />
        [
            Bindable(false),
            DefaultValue(null),
            MobileCategory(SR.Category_Appearance),
            MobileSysDescription(SR.Style_Reference),
            NotifyParentProperty(true),
            TypeConverter(typeof(System.Web.UI.Design.MobileControls.Converters.StyleReferenceConverter)),
        ]
        public virtual String StyleReference
        {
            get
            {
                return (String)State["StyleReference"];
            }
            set
            {
                State["StyleReference"] = value;
                Refresh();      // mark referred style as dirty
            }
        }
 
        [
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        internal Style ReferredStyle
        {
            get
            {
                if (_checkedStyleReference)
                {
                    return _referredStyle;
                }
 
                _checkedStyleReference = true;
                String reference = StyleReference;
 
                if (String.IsNullOrEmpty(reference))
                {
                    _referredStyle = null;
                    return null;
                }
 
                Debug.Assert(_referredStyle == null ||
                             String.Equals(reference, _referredStyle.Name, StringComparison.OrdinalIgnoreCase), 
                             "Inconsistency in style names - _referredStyle must be dirty.");
                
                if (_referredStyle == null)
                {
                    // Look in the stylesheet in the nearest templated control
                    TemplateControl nearestTemplatedControl =
                        _control.FindContainingTemplateControl();
 
                    StyleSheet stylesheet = null;
                    MobilePage mobilePage = nearestTemplatedControl as MobilePage;
                    if (mobilePage != null)
                    {
                        stylesheet = mobilePage.StyleSheet;
                    }
                    else
                    {
                        MobileUserControl mobileUserControl =
                            nearestTemplatedControl as MobileUserControl;
                        if (mobileUserControl != null)
                        {
                            stylesheet = mobileUserControl.StyleSheet;
 
                            // If stylesheet is in mobileUserControl at designtime, 
                            // simply use the one in MobilePage
                            if (_control.MobilePage.DesignMode)
                            {
                                Debug.Assert(stylesheet == StyleSheet.Default);
                                stylesheet = _control.MobilePage.StyleSheet;
                            }
                        }
 
                        // Stylesheets won't be recognized in regular user
                        // controls.
                    }
 
                    if (stylesheet != null)
                    {
                        // when page does not contain StyleSheet
                        // controls, Default stylesheet will search twice. 
                        _referredStyle = stylesheet[reference];
                    }
 
                    if (_referredStyle == null)
                    {
                        // built in styles
                        _referredStyle = StyleSheet.Default[reference];
 
                        // No exceptions in Designer, will handle differently.
                        if (_referredStyle == null && !_control.MobilePage.DesignMode)
                        {
                            String exceptionResource;
                            if (nearestTemplatedControl is UserControl &&
                                !(nearestTemplatedControl is MobileUserControl))
                            {
                                // Throw a specific error message in this case
                                exceptionResource =
                                    SR.Style_StyleNotFoundOnGenericUserControl;
                            }
                            else
                            {
                                exceptionResource =
                                    SR.Style_StyleNotFound;
                            }
                                
                            throw new Exception(SR.GetString(exceptionResource,
                                                             reference)); 
                        }
                    }
                }
 
                return _referredStyle;
            }
        }
 
        //  late bound method for accessing style properties
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.this"]/*' />
        public Object this[Object key]
        {
            get
            {
                return GetValue((Property)key, false, true, null);
            }
            set
            {
                Property property = (Property)key;
                Object defaultValue = property.DefaultValue;
                State[property.Name] = defaultValue.Equals(value) ? null : value;
            }
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.this1"]/*' />
        public Object this[Object key, bool inherit]
        {
            get
            {
                return GetValue((Property)key, inherit, true, null);
            }
        }
 
        private Object GetValue(Property property, bool inherit, bool returnDefault, Hashtable stylesEncountered)
        {
            //  try to retrieve from internal value
            Object value = State[property.Name];
 
            if (value == null)
            {
                //  else retrieve from style reference
                if (inherit)
                {
                    Style style = this.ReferredStyle;
                    if (style != null)
                    {
                        if (stylesEncountered == null)
                        {
                            stylesEncountered = new Hashtable(); 
                        }
 
                        if (stylesEncountered.ContainsKey(style))
                        {
                            if (_control.MobilePage != null && _control.MobilePage.DesignMode)
                            {
                                return property.DefaultValue;
                            }
                            else
                            {
                                throw new Exception(SR.GetString(SR.Style_CircularReference, this.Name));
                            }
                        }
                        stylesEncountered[style] = true;
                        value = style.GetValue(property, inherit, false, stylesEncountered);
                    }
                }
 
                if (value == null)
                {
                    //  else retrieve from control ancestor
                    if (inherit && property.Inherit && _control != null)
                    {
                        Style parentStyle = null;
                        if (_cachedParentStyle == null)
                        {
                            if (_control.Parent is MobileControl)
                            {
                                parentStyle = ((MobileControl)_control.Parent).Style;
                                _cachedParentStyle = parentStyle;
                            }
                            // DeviceSpecific is treated as control at design time, however, we need to get
                            // the styles from devicespecific's parent.
                            else if (_control.MobilePage != null &&
                                _control.MobilePage.DesignMode && 
                                _control.Parent is DeviceSpecific &&
                                _control.Parent.Parent is MobileControl)
                            {
                                parentStyle = ((MobileControl)_control.Parent.Parent).Style;
                            }
                            else if(!(_control is Form))
                            {
                                Control _tempControl = _control.Parent;
                                while(!(_tempControl is MobileControl) && (_tempControl != null))
                                {
                                    _tempControl = _tempControl.Parent;
                                }
                                if(_tempControl != null)
                                {
                                    parentStyle = ((MobileControl)_tempControl).Style;
                                }
                            }
                        }
                        else
                        {
                            parentStyle = _cachedParentStyle;
                        }
 
                        if (parentStyle != null)
                        {
                            value = parentStyle.GetValue(property, inherit, false, null);
                        }
                    }
 
                    //  else retrieve default value
                    if (value == null && returnDefault)
                    {
                        value = property.DefaultValue;
                    }
                }
            }
            return value;
        }
 
        internal void InvalidateParentStyle()
        {
            _cachedParentStyle = null;
        }
 
        ////////////////////////////////////////////////////////////////////////////
        //  BEGIN STYLES
        ////////////////////////////////////////////////////////////////////////////
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.Font"]/*' />
        [
            DefaultValue(null),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
            MobileCategory(SR.Category_Appearance),
            MobileSysDescription(SR.Style_Font),
            NotifyParentProperty(true)
        ]
        public FontInfo Font
        {
            get
            {
                if (_font == null)
                {
                    _font = new FontInfo(this);
                }
                return _font;
            }
        }
 
        // FontSize and FontName internal
        // we still need these methods on style due to their inheritance and
        // persistence behavior, they're referenced from FontInfo.cs.
        // 
 
 
        [
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        internal String FontName
        {
            get
            {
                return (String)this[FontNameKey];
            }
            set
            {
                this[FontNameKey] = value;
            }
        }
 
        [
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        internal BooleanOption Bold
        {
            get
            {
                return (BooleanOption)this[BoldKey];
            }
            set
            {
                this[BoldKey] = value;
            }
        }
 
        [
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        internal BooleanOption Italic
        {
            get
            {
                return (BooleanOption)this[ItalicKey];
            }
            set
            {
                this[ItalicKey] = value;
            }
        }
 
        [
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        internal FontSize FontSize
        {
            get
            {
                return (FontSize)this[FontSizeKey];
            }
            set
            {
                this[FontSizeKey] = value;
            }
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.Alignment"]/*' />
        [
            Bindable(true),
            DefaultValue(Alignment.NotSet),
            MobileCategory(SR.Category_Appearance),
            MobileSysDescription(SR.Style_Alignment),
            NotifyParentProperty(true),
        ]
        public Alignment Alignment
        {
            get
            {
                return (Alignment)this[AlignmentKey];
            }
            set
            {
                this[AlignmentKey] = value;
            }
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.Wrapping"]/*' />
        [
            Bindable(true),
            DefaultValue(Wrapping.NotSet),
            MobileCategory(SR.Category_Appearance),
            MobileSysDescription(SR.Style_Wrapping),
            NotifyParentProperty(true),
        ]
        public Wrapping Wrapping
        {
            get
            {
                return (Wrapping)this[WrappingKey];
            }
            set
            {
                this[WrappingKey] = value;
            }
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.ForeColor"]/*' />
        [
            Bindable(true),
            DefaultValue(typeof(Color), ""),
            MobileCategory(SR.Category_Appearance),
            MobileSysDescription(SR.Style_ForeColor),
            NotifyParentProperty(true),
            TypeConverterAttribute(typeof(WebColorConverter)),
        ]
        public Color ForeColor
        {
            get
            {
                return (Color)this[ForeColorKey];
            }
            set
            {
                this[ForeColorKey] = value;
            }
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.BackColor"]/*' />
        [
            Bindable(true),
            DefaultValue(typeof(Color), ""),
            MobileCategory(SR.Category_Appearance),
            MobileSysDescription(SR.Style_BackColor),
            NotifyParentProperty(true),
            TypeConverterAttribute(typeof(WebColorConverter))
        ]
        public Color BackColor
        {
            get
            {
                return (Color)this[BackColorKey];
            }
            set
            {
                this[BackColorKey] = value;
            }
        }
 
        /////////////////////////////////////////////////////////////////////////
        //  TEMPLATES SUPPORT
        /////////////////////////////////////////////////////////////////////////
 
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.IParserAccessor.AddParsedSubObject"]/*' />
        /// <internalonly/>
        protected void AddParsedSubObject(Object o)
        {
            if (o is DeviceSpecific)
            {
                if (DeviceSpecific != null)
                {
                    throw new Exception(
                        SR.GetString(SR.MobileControl_NoMultipleDeviceSpecifics));
                }
                DeviceSpecific = (DeviceSpecific)o;
 
                // This code works by assuming that the time this
                // method is called, that Control has not yet been
                // set.  Thus, we set the DeviceSpecific's parent
                // control when the Control itself is set.  If Control
                // != null here, then we won't get that opportunity. 
                Debug.Assert(Control == null);
            }
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.IsTemplated"]/*' />
        [
            Browsable(false),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        ]
        public bool IsTemplated
        {
            get
            {
                return IsTemplatedInternal(null);
            }
        }
 
        internal bool IsTemplatedInternal(Hashtable stylesEncountered)
        {
            if (_deviceSpecific != null && _deviceSpecific.HasTemplates)
            {
                return true;
            }
 
            Style referredStyle = ReferredStyle;
            if (referredStyle == null)
            {
                return false;
            }
 
            if (stylesEncountered == null)
            {
                stylesEncountered = new Hashtable(); 
            }
            if (stylesEncountered.ContainsKey(referredStyle))
            {
                if (_control.MobilePage != null && _control.MobilePage.DesignMode)
                {
                    return false;
                }
                else
                {
                    throw new Exception(SR.GetString(SR.Style_CircularReference, this.Name));
                }
            }
            // referredStyle != null
            stylesEncountered[referredStyle] = true;
            return referredStyle.IsTemplatedInternal(stylesEncountered);
        }
 
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.GetTemplate"]/*' />
        public ITemplate GetTemplate(String templateName)
        {
            return GetTemplateInternal(templateName, null);
        }
 
        internal ITemplate GetTemplateInternal(String templateName, Hashtable stylesEncountered)
        {
            ITemplate t = null;
            if (_deviceSpecific != null)
            {
                t = (ITemplate)_deviceSpecific.GetTemplate (templateName);
            }
 
            Style referredStyle = ReferredStyle;
            if (t == null && referredStyle != null)
            {
                // Check for cyclical style references.
                if (stylesEncountered == null)
                {
                    stylesEncountered = new Hashtable ();
                }
                if (stylesEncountered.ContainsKey(referredStyle))
                {
                    if (_control.MobilePage != null && _control.MobilePage.DesignMode)
                    {
                        return null;
                    }
                    else
                    {
                        throw new Exception(SR.GetString(SR.Style_CircularReference, this.Name));
                    }
                }
 
                // No cycle detected.
                stylesEncountered[referredStyle] = true;                
                t = referredStyle.GetTemplateInternal(templateName, stylesEncountered);
            }
 
            return t;
        }
 
        // Design-time only property
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.DeviceSpecific"]/*' />
        [
            Browsable(false),
            PersistenceMode(PersistenceMode.InnerProperty)
        ]
        public DeviceSpecific DeviceSpecific
        {
            get
            {
                return _deviceSpecific;
            }
            set
            {
                _deviceSpecific = value;
                if (null != value)
                {
                    value.SetOwner(this);
                }
            }
        }
 
        //  registers a new type of style and returns the KEY for accessing it
        /// <include file='doc\Style.uex' path='docs/doc[@for="Style.RegisterStyle"]/*' />
        public static Object RegisterStyle(String name, Type type, Object defaultValue, bool inherit)
        {
            return new Property(name, type, defaultValue, inherit);
        }
 
        class Property
        {
            public String Name;
            public Type   Type;
            public Object DefaultValue;
            public bool   Inherit;        // can be inherited from parent?
 
            public Property(String name, Type type, Object defaultValue, bool inherit)
            {
                this.Name = name;
                this.Type = type;
                this.DefaultValue = defaultValue;
                this.Inherit = inherit;
            }
        }
 
        #region Implementation of IStateManager
        /// <internalonly/>
        bool IStateManager.IsTrackingViewState {
            get {
                return IsTrackingViewState;
            }
        }
 
        /// <internalonly/>
        void IStateManager.LoadViewState(object state) {
            LoadViewState(state);
        }
 
        /// <internalonly/>
        void IStateManager.TrackViewState() {
            TrackViewState();
        }
 
        /// <internalonly/>
        object IStateManager.SaveViewState() {
            return SaveViewState();
        }
        #endregion
 
        #region IParserAccessor implementation
        void IParserAccessor.AddParsedSubObject(Object o) {
            AddParsedSubObject(o);
        }
        #endregion
    }
}