File: AuthoringOM\Design\MessageFilters\GlyphManager.cs
Project: ndp\cdf\src\WF\Common\System.Workflow.ComponentModel.csproj (System.Workflow.ComponentModel)
namespace System.Workflow.ComponentModel.Design
{
    using System;
    using System.Drawing;
    using System.Collections;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Windows.Forms;
    using System.ComponentModel;
    using System.Drawing.Design;
    using System.Drawing.Drawing2D;
    using System.Collections.Generic;
    using System.ComponentModel.Design;
    using System.Workflow.ComponentModel.Design;
 
    #region Class ActivityDesignerGlyphCollection
    [Obsolete("The System.Workflow.* types are deprecated.  Instead, please use the new types from System.Activities.*")]
    public sealed class ActivityDesignerGlyphCollection : List<DesignerGlyph>
    {
        public ActivityDesignerGlyphCollection()
        {
        }
 
        public ActivityDesignerGlyphCollection(IEnumerable<DesignerGlyph> glyphs)
            : base(glyphs)
        {
        }
 
        public ActivityDesignerGlyphCollection(ActivityDesignerGlyphCollection glyphs)
            : base(glyphs)
        {
        }
 
        internal DesignerGlyph this[Type type]
        {
            get
            {
                if (type == null)
                    throw new ArgumentNullException();
 
                DesignerGlyph glyph = null;
                foreach (DesignerGlyph designerGlyph in this)
                {
                    if (designerGlyph.GetType() == type)
                    {
                        glyph = designerGlyph;
                        break;
                    }
                    else if (type.IsAssignableFrom(designerGlyph.GetType()) && glyph == null)
                    {
                        glyph = designerGlyph;
                    }
                }
 
                return glyph;
            }
        }
    }
    #endregion
 
    #region Class DesignerGlyph
    [Obsolete("The System.Workflow.* types are deprecated.  Instead, please use the new types from System.Activities.*")]
    public abstract class DesignerGlyph
    {
        public const int HighestPriority = 0;
        public const int NormalPriority = 10000;
        public const int LowestPriority = 1000000;
 
        internal const int ConnectionPointPriority = 1;
        internal const int MoveAnchorPriority = 1;
        internal const int ConfigErrorPriority = 2;
        internal const int ConnectorDragDropPriority = 2;
        internal const int FadeGlyphPriority = 3;
        internal const int LockedGlyphPriority = 3;
        internal const int ReadOnlyGlyphPriority = 3;
        internal const int CommentPriority = 3;
        internal const int SelectionPriority = 4;
        internal const int NonExecutionStatePriority = 5;
 
        public virtual bool CanBeActivated
        {
            get
            {
                return false;
            }
        }
 
        public virtual int Priority
        {
            get
            {
                return DesignerGlyph.NormalPriority;
            }
        }
 
        public virtual Rectangle GetBounds(ActivityDesigner designer, bool activated)
        {
            if (designer == null)
                throw new ArgumentNullException("designer");
 
            return designer.Bounds;
        }
 
        protected abstract void OnPaint(Graphics graphics, bool activated, AmbientTheme ambientTheme, ActivityDesigner designer);
 
        protected virtual void OnActivate(ActivityDesigner designer)
        {
        }
 
        internal void DrawActivated(Graphics graphics, ActivityDesigner designer)
        {
            OnPaint(graphics, true, WorkflowTheme.CurrentTheme.AmbientTheme, designer);
        }
 
        internal void Draw(Graphics graphics, ActivityDesigner designer)
        {
            OnPaint(graphics, false, WorkflowTheme.CurrentTheme.AmbientTheme, designer);
        }
 
        internal void Activate(ActivityDesigner designer)
        {
            OnActivate(designer);
        }
 
        internal static int OnComparePriority(DesignerGlyph x, DesignerGlyph y)
        {
            return (y.Priority - x.Priority);
        }
    }
    #endregion
 
    #region Class GlyphManager
    internal class GlyphManager : WorkflowDesignerMessageFilter, IDesignerGlyphProviderService
    {
        #region Members and Constructor
        // cache all the services so that in the dispose we properly clean up ourselves
        private List<IDesignerGlyphProvider> designerGlyphProviders = new List<IDesignerGlyphProvider>();
 
        // these two variables are only valid during MouseEnter and MouseLeave of a glyph
        private DesignerGlyph activeGlyph = null;
        private ActivityDesigner activeDesigner = null;
 
        internal GlyphManager()
        {
        }
 
        protected override void Dispose(bool disposing)
        {
            this.designerGlyphProviders.Clear();
            this.activeGlyph = null;
            this.activeDesigner = null;
 
            IServiceContainer serviceContainer = GetService(typeof(IServiceContainer)) as IServiceContainer;
            if (serviceContainer != null)
            {
                if (GetService(typeof(IDesignerGlyphProviderService)) != null)
                    serviceContainer.RemoveService(typeof(IDesignerGlyphProviderService));
            }
 
            base.Dispose(disposing);
        }
        #endregion
 
        #region WorkflowDesignerMessageFilter Methods
        protected override void Initialize(WorkflowView parentView)
        {
            base.Initialize(parentView);
 
            IServiceContainer serviceContainer = GetService(typeof(IServiceContainer)) as IServiceContainer;
            if (serviceContainer != null)
            {
                if (GetService(typeof(IDesignerGlyphProviderService)) != null)
                    serviceContainer.RemoveService(typeof(IDesignerGlyphProviderService));
                serviceContainer.AddService(typeof(IDesignerGlyphProviderService), this);
            }
        }
 
        protected override bool OnMouseDown(MouseEventArgs eventArgs)
        {
            if (this.activeGlyph != null)
            {
                this.activeGlyph.Activate(this.activeDesigner);
                return true;
            }
            else
            {
                return false;
            }
        }
 
        //if there is an active glyph, handle the double click event as the single click event
        //to make sure we dont execute the default action in that case
        protected override bool OnMouseDoubleClick(MouseEventArgs eventArgs)
        {
            if (this.activeGlyph != null)
            {
                this.activeGlyph.Activate(this.activeDesigner);
                return true;
            }
            return false;
        }
 
 
        protected override bool OnMouseMove(MouseEventArgs eventArgs)
        {
            RefreshActiveGlyph(ParentView.ClientPointToLogical(new Point(eventArgs.X, eventArgs.Y)));
            return false;
        }
 
        protected override bool OnMouseEnter(MouseEventArgs eventArgs)
        {
            RefreshActiveGlyph(ParentView.ClientPointToLogical(new Point(eventArgs.X, eventArgs.Y)));
            return false;
        }
 
        protected override bool OnMouseHover(MouseEventArgs eventArgs)
        {
            RefreshActiveGlyph(ParentView.ClientPointToLogical(new Point(eventArgs.X, eventArgs.Y)));
            return false;
        }
        #endregion
 
        #region IDesignerGlyphProviderService Implementation
        void IDesignerGlyphProviderService.AddGlyphProvider(IDesignerGlyphProvider glyphProvider)
        {
            if (!this.designerGlyphProviders.Contains(glyphProvider))
            {
                this.designerGlyphProviders.Add(glyphProvider);
                ParentView.InvalidateClientRectangle(Rectangle.Empty);
            }
        }
 
        void IDesignerGlyphProviderService.RemoveGlyphProvider(IDesignerGlyphProvider glyphProvider)
        {
            this.designerGlyphProviders.Remove(glyphProvider);
            ParentView.InvalidateClientRectangle(Rectangle.Empty);
        }
 
        ReadOnlyCollection<IDesignerGlyphProvider> IDesignerGlyphProviderService.GlyphProviders
        {
            get
            {
                return this.designerGlyphProviders.AsReadOnly();
            }
        }
        #endregion
 
        #region Internal methods
        internal void DrawDesignerGlyphs(ActivityDesignerPaintEventArgs e, ActivityDesigner designer)
        {
            foreach (DesignerGlyph glyph in GetDesignerGlyphs(designer))
                glyph.Draw(e.Graphics, designer);
 
            if (this.activeGlyph != null && designer == this.activeDesigner)
                this.activeGlyph.DrawActivated(e.Graphics, this.activeDesigner);
        }
 
        internal ActivityDesignerGlyphCollection GetDesignerGlyphs(ActivityDesigner designer)
        {
            ActivityDesignerGlyphCollection glyphs = new ActivityDesignerGlyphCollection();
            if (designer.Glyphs != null)
                glyphs.AddRange(designer.Glyphs);
 
            foreach (IDesignerGlyphProvider glyphProvider in this.designerGlyphProviders)
            {
                ActivityDesignerGlyphCollection extendedGlyphs = glyphProvider.GetGlyphs(designer);
                if (extendedGlyphs != null)
                    glyphs.AddRange(extendedGlyphs);
            }
 
            glyphs.Sort(new Comparison<DesignerGlyph>(DesignerGlyph.OnComparePriority));
            return glyphs;
        }
        #endregion
 
        #region Helper Methods
        private void RefreshActiveGlyph(Point point)
        {
            WorkflowView parentView = ParentView;
            if (parentView != null)
            {
                DesignerGlyph previousActiveGlyph = this.activeGlyph;
 
                if (this.activeGlyph == null || !this.activeGlyph.GetBounds(this.activeDesigner, true).Contains(point))
                {
                    ActivityDesigner newActiveDesigner = null;
                    DesignerGlyph newActiveGlyph = GlyphFromPoint(point, out newActiveDesigner);
 
                    if (this.activeGlyph != null)
                        parentView.InvalidateLogicalRectangle(this.activeGlyph.GetBounds(this.activeDesigner, true));
 
                    this.activeGlyph = newActiveGlyph;
                    this.activeDesigner = newActiveDesigner;
 
                    if (this.activeGlyph != null)
                        parentView.InvalidateLogicalRectangle(this.activeGlyph.GetBounds(this.activeDesigner, true));
                }
 
                if (previousActiveGlyph != this.activeGlyph)
                {
                    if (this.activeGlyph != null && this.activeGlyph.CanBeActivated)
                        parentView.Cursor = Cursors.Hand;
                    else if (parentView.Cursor == Cursors.Hand)
                        parentView.Cursor = Cursors.Default;
                }
            }
        }
 
        private class RectangleCollection
        {
            private List<Rectangle> rectangles = new List<Rectangle>();
 
            internal void AddRectangle(Rectangle rectangle)
            {
                this.rectangles.Add(rectangle);
            }
 
            internal bool IsPointInsideAnyRectangle(Point p)
            {
                for (int i = 0; i < this.rectangles.Count; i++)
                {
                    if (this.rectangles[i].Contains(p))
                        return true;
                }
                return false;
            }
        }
 
        private DesignerGlyph GlyphFromPoint(Point point, out ActivityDesigner activityDesigner)
        {
            activityDesigner = null;
 
            WorkflowView parentView = ParentView;
            if (parentView != null)
            {
                RectangleCollection collection = new RectangleCollection();
                {
                    ActivityDesigner[] containedDesigners = GetActivityDesigners(parentView.ClientRectangleToLogical(new Rectangle(Point.Empty, parentView.ViewPortSize)));
                    foreach (ActivityDesigner designer in containedDesigners)
                    {
                        if (!collection.IsPointInsideAnyRectangle(point))
                        {
                            foreach (DesignerGlyph glyph in GetDesignerGlyphs(designer))
                            {
                                if (glyph.GetBounds(designer, false).Contains(point))
                                {
                                    if (glyph.CanBeActivated)
                                    {
                                        activityDesigner = designer;
                                        return glyph;
                                    }
                                }
                            }
                        }
                        collection.AddRectangle(designer.Bounds);
                    }
                }
            }
 
            return null;
        }
 
        //Please note that before changing this algorithm, you need to know that changing this algorithm
        //will affect the z order of the designers and will affect the way glyphs are drawn. 
        //Here what we are using depth first search algorithm to maintain the Z order.
        //Please note that even though one might think the algo might cause some inefficiency, the algo
        //has been timed for huge workflow and typically takes < 20ms to execute
        private ActivityDesigner[] GetActivityDesigners(Rectangle logicalViewPort)
        {
            //We need to go to the deepest point and then start drawing outwards
            List<ActivityDesigner> designerList = new List<ActivityDesigner>();
            bool viewPortEmpty = logicalViewPort.IsEmpty;
 
            ActivityDesigner rootDesigner = ActivityDesigner.GetSafeRootDesigner(ParentView);
            if (rootDesigner != null)
            {
                Stack<object> designerStack = new Stack<object>();
 
                designerStack.Push(rootDesigner);
                CompositeActivityDesigner compositeDesigner = rootDesigner as CompositeActivityDesigner;
                if (compositeDesigner != null && compositeDesigner.ContainedDesigners.Count > 0)
                    designerStack.Push(compositeDesigner.ContainedDesigners);
 
                while (designerStack.Count > 0)
                {
                    object topOfStack = designerStack.Pop();
                    ICollection designers = topOfStack as ICollection;
                    if (designers != null)
                    {
                        foreach (ActivityDesigner activityDesigner in designers)
                        {
                            if ((viewPortEmpty || logicalViewPort.IntersectsWith(activityDesigner.Bounds)) && activityDesigner.IsVisible)
                            {
                                designerStack.Push(activityDesigner);
                                compositeDesigner = activityDesigner as CompositeActivityDesigner;
                                if (compositeDesigner != null && compositeDesigner.ContainedDesigners.Count > 0)
                                    designerStack.Push(compositeDesigner.ContainedDesigners);
                            }
                        }
                    }
                    else
                    {
                        //Draw glyphs for composite designers
                        designerList.Add((ActivityDesigner)topOfStack);
                    }
                }
            }
 
            return designerList.ToArray();
        }
        #endregion
    }
    #endregion
}