File: System.Activities.Presentation\System\Activities\Presentation\FreeFormEditing\AutoConnectAdorner.cs
Project: ndp\cdf\src\NetFx40\Tools\System.Activities.Presentation.csproj (System.Activities.Presentation)
//----------------------------------------------------------------
// <copyright company="Microsoft Corporation">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//----------------------------------------------------------------
 
namespace System.Activities.Presentation.FreeFormEditing
{
    using System;
    using System.Activities.Presentation.Model;
    using System.Activities.Presentation.View;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime;
    using System.Text;
    using System.Windows;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Shapes;
    
    internal sealed class AutoConnectAdorner : Adorner
    {
        private const double TriangleBaseLength = 15;
        private const double TriangleHeight = 10;
        private const double HitTestWidth = 20;
        private const double HitTestHeight = 30;
        private const double DropTargetOffset = 15;
        private AutoConnectDirections highlightedDirection = AutoConnectDirections.None;
        private AutoConnectDirections directions;
        private Rect[] hitTestRects;
        private PathGeometry[] renderGeometries;
        private Rect adornedElementRect;
        private SolidColorBrush hitTestBrush = Brushes.Transparent;
        private Pen hitTestPen = new Pen(Brushes.Transparent, 0);
        private SolidColorBrush renderBrush = Brushes.Azure;
        private Pen renderPen = new Pen(Brushes.LightBlue, 1.0);
        private SolidColorBrush highlightBrush = Brushes.LightBlue;
        private Pen highlightPen = new Pen(Brushes.SteelBlue, 1);
        private FreeFormPanel panel;
        
        public AutoConnectAdorner(UIElement adornedElement, FreeFormPanel panel, AutoConnectDirections directions) 
            : base(adornedElement)
        {
            this.panel = panel;
            this.directions = directions;
 
            Size size = FreeFormPanel.GetChildSize(this.AdornedElement);
            this.adornedElementRect = new Rect(new Point(0, 0), size);
            this.hitTestRects = new Rect[] 
            { 
                new Rect(-HitTestHeight, (size.Height / 2) - (HitTestWidth / 2), HitTestHeight, HitTestWidth),
                new Rect(size.Width, (size.Height / 2) - (HitTestWidth / 2), HitTestHeight, HitTestWidth),
                new Rect((size.Width / 2) - (HitTestWidth / 2), -HitTestHeight, HitTestWidth, HitTestHeight),
                new Rect((size.Width / 2) - (HitTestWidth / 2), size.Height, HitTestWidth, HitTestHeight)
            };
 
            this.renderGeometries = new PathGeometry[]
            {
                new PathGeometry() 
                { 
                    Figures =
                    {
                        new PathFigure()
                        {
                            StartPoint = new Point(-DropTargetOffset - TriangleHeight, size.Height / 2), Segments = 
                            { 
                                new LineSegment() { Point = new Point(-DropTargetOffset, (size.Height / 2) - (TriangleBaseLength / 2)) },
                                new LineSegment() { Point = new Point(-DropTargetOffset, (size.Height / 2) + (TriangleBaseLength / 2)) },
                                new LineSegment() { Point = new Point(-DropTargetOffset - TriangleHeight, size.Height / 2) }
                            }
                        }
                    } 
                },
                new PathGeometry() 
                { 
                    Figures =
                    {
                        new PathFigure()
                        {
                            StartPoint = new Point(size.Width + DropTargetOffset, (size.Height / 2) - (TriangleBaseLength / 2)), Segments = 
                            { 
                                new LineSegment() { Point = new Point(size.Width + DropTargetOffset + TriangleHeight, size.Height / 2) },
                                new LineSegment() { Point = new Point(size.Width + DropTargetOffset, (size.Height / 2) + (TriangleBaseLength / 2)) },
                                new LineSegment() { Point = new Point(size.Width + DropTargetOffset, (size.Height / 2) - (TriangleBaseLength / 2)) }
                            }
                        }
                    }
                },
                new PathGeometry() 
                { 
                    Figures =
                    {
                        new PathFigure()
                        {
                            StartPoint = new Point((size.Width / 2) - (TriangleBaseLength / 2), -DropTargetOffset), Segments = 
                            { 
                                new LineSegment() { Point = new Point((size.Width / 2), -DropTargetOffset - TriangleHeight) },
                                new LineSegment() { Point = new Point((size.Width / 2) + (TriangleBaseLength / 2), -DropTargetOffset) },
                                new LineSegment() { Point = new Point((size.Width / 2) - (TriangleBaseLength / 2), -DropTargetOffset) }
                            }
                        }
                    }
                },
                new PathGeometry() 
                { 
                    Figures = 
                    {
                        new PathFigure()
                        {
                            StartPoint = new Point((size.Width / 2) - (TriangleBaseLength / 2), size.Height + DropTargetOffset), Segments = 
                            { 
                                new LineSegment() { Point = new Point((size.Width / 2) + (TriangleBaseLength / 2), size.Height + DropTargetOffset) },
                                new LineSegment() { Point = new Point((size.Width / 2), size.Height + TriangleHeight + DropTargetOffset) },
                                new LineSegment() { Point = new Point((size.Width / 2) - (TriangleBaseLength / 2), size.Height + DropTargetOffset) }
                            }
                        }
                    }
                }
            };
        }
 
        internal AutoConnectDirections AutoConnectDirection
        {
            get
            {
                return this.highlightedDirection;
            }
        }
 
        internal void OnDrag(DragEventArgs e)
        {
            Point position = e.GetPosition(this.AdornedElement);
            this.UpdateHighlightedDirection(position);
            e.Effects |= DragDropEffects.Move;
            e.Handled = true;
        }
 
        internal void UpdateHighlightedDirection(Point position)
        {
            Size size = FreeFormPanel.GetChildSize(this.AdornedElement);
            if (position.X < 0 && this.highlightedDirection != AutoConnectDirections.Left)
            {
                this.highlightedDirection = AutoConnectDirections.Left;
                this.InvalidateVisual();
            }
            else if (position.X > size.Width && this.highlightedDirection != AutoConnectDirections.Right)
            {
                this.highlightedDirection = AutoConnectDirections.Right;
                this.InvalidateVisual();
            }
            else if (position.Y < 0 && this.highlightedDirection != AutoConnectDirections.Top)
            {
                this.highlightedDirection = AutoConnectDirections.Top;
                this.InvalidateVisual();
            }
            else if (position.Y > size.Height && this.highlightedDirection != AutoConnectDirections.Bottom)
            {
                this.highlightedDirection = AutoConnectDirections.Bottom;
                this.InvalidateVisual();
            }
        }
 
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            this.AllowDrop = true;
        }
 
        protected override void OnDragEnter(DragEventArgs e)
        {
            base.OnDragEnter(e);
            this.OnDrag(e);
        }
 
        protected override void OnDragOver(DragEventArgs e)
        {
            base.OnDragOver(e);
            this.OnDrag(e);
        }
 
        protected override void OnDragLeave(DragEventArgs e)
        {
            base.OnDragLeave(e);
            this.highlightedDirection = AutoConnectDirections.None;
            this.InvalidateVisual();
        }
 
        protected override void OnDrop(DragEventArgs e)
        {
            if (this.panel.AutoConnectContainer != null)
            {
                try
                {
                    this.panel.AutoConnectContainer.DoAutoConnect(e, this.AdornedElement, this.AutoConnectDirection);
                }
                finally
                {
                    e.Handled = true;
                    this.panel.RemoveAutoConnectAdorner();
                }
            }
 
            base.OnDrop(e);
        }
 
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
 
            for (int i = 0; i < 4; i++)
            {
                if ((AutoConnectHelper.GetAutoConnectDirection(i) & this.directions) != 0)
                {
                    drawingContext.DrawRectangle(this.hitTestBrush, this.hitTestPen, this.hitTestRects[i]);
                    drawingContext.DrawGeometry(
                        (AutoConnectHelper.GetAutoConnectDirection(i) == this.highlightedDirection) ? this.highlightBrush : this.renderBrush,
                        (AutoConnectHelper.GetAutoConnectDirection(i) == this.highlightedDirection) ? this.highlightPen : this.renderPen,
                        this.renderGeometries[i]);
                }
            }
 
            if (this.AutoConnectDirection != AutoConnectDirections.None)
            {
                drawingContext.DrawRectangle(null, this.highlightPen, this.adornedElementRect);
            }
        }
    }
}