File: cdf\src\NetFx40\Tools\System.Activities.Presentation\System\Activities\Presentation\FreeFormEditing\FreeFormPanel.cs
Project: ndp\System.Data.csproj (System.Data)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.Activities.Presentation.FreeFormEditing
{
    using System;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Threading;
    using System.Activities.Presentation;
    using System.Activities.Presentation.View;
    using System.Activities.Presentation.Internal.PropertyEditing;
    using System.Runtime;
    using System.Linq;
    using System.Activities.Presentation.Model;
 
    internal class FreeFormPanel : Panel
    {
        public static readonly DependencyProperty ChildSizeProperty = DependencyProperty.RegisterAttached("ChildSize", typeof(Size), typeof(FreeFormPanel), new FrameworkPropertyMetadata());
        public static readonly DependencyProperty LocationProperty = DependencyProperty.RegisterAttached("Location", typeof(Point), typeof(FreeFormPanel), new FrameworkPropertyMetadata(new Point(-1, -1)));
        public static readonly DependencyProperty RequiredWidthProperty = DependencyProperty.Register("RequiredWidth", typeof(Double), typeof(FreeFormPanel), new FrameworkPropertyMetadata(double.NaN));
        public static readonly DependencyProperty RequiredHeightProperty = DependencyProperty.Register("RequiredHeight", typeof(Double), typeof(FreeFormPanel), new FrameworkPropertyMetadata(double.NaN));
        public static readonly DependencyProperty DestinationConnectionPointProperty = DependencyProperty.RegisterAttached("DestinationConnectionPoint", typeof(ConnectionPoint), typeof(FreeFormPanel), new FrameworkPropertyMetadata());
        public static readonly DependencyProperty SourceConnectionPointProperty = DependencyProperty.RegisterAttached("SourceConnectionPoint", typeof(ConnectionPoint), typeof(FreeFormPanel), new FrameworkPropertyMetadata());
        public static readonly DependencyProperty DisabledProperty = DependencyProperty.Register("Disabled", typeof(bool), typeof(FreeFormPanel), new UIPropertyMetadata(false));
        public static readonly DependencyProperty AutoConnectContainerProperty = DependencyProperty.Register("AutoConnectContainer", typeof(IAutoConnectContainer), typeof(FreeFormPanel), new UIPropertyMetadata(null));
 
        public const double ConnectorEditorOpacity = 1.0;
        public const double ConnectorEditorThickness = 1.5;
        public const double LeftStackingMargin = 50;
        public const double TopStackingMargin = 80;
        public const double VerticalStackingDistance = 50;
        public const double GridSize = 10;
        public ConnectorEditor connectorEditor;
        double lastYPosition;
        bool measureConnectors = false;
        bool measureConnectorsPosted = false;
        AutoConnectHelper autoConnectHelper = null;
        DesignerConfigurationService designerConfigurationService = null;
 
        public FreeFormPanel()
        {
            connectorEditor = null;
            this.autoConnectHelper = new AutoConnectHelper(this);
            lastYPosition = FreeFormPanel.TopStackingMargin;
 
            this.Unloaded += (sender, e) =>
            {
                this.RemoveConnectorEditor();
            };
        }
 
        public event LocationChangedEventHandler LocationChanged;
        public event ConnectorMovedEventHandler ConnectorMoved;
        public event RequiredSizeChangedEventHandler RequiredSizeChanged;
 
        public static Size GetChildSize(DependencyObject obj)
        {
            return (Size)obj.GetValue(FreeFormPanel.ChildSizeProperty);
        }
 
        public static void SetChildSize(DependencyObject obj, Size size)
        {
            obj.SetValue(FreeFormPanel.ChildSizeProperty, size);
        }
 
        public double RequiredHeight
        {
            get { return (double)GetValue(FreeFormPanel.RequiredHeightProperty); }
            private set { SetValue(FreeFormPanel.RequiredHeightProperty, value); }
        }
 
        public double RequiredWidth
        {
            get { return (double)GetValue(FreeFormPanel.RequiredWidthProperty); }
            private set { SetValue(FreeFormPanel.RequiredWidthProperty, value); }
        }
 
        public bool Disabled
        {
            get { return (bool)GetValue(DisabledProperty); }
            set { SetValue(DisabledProperty, value); }
        }
 
        public IAutoConnectContainer AutoConnectContainer
        {
            get { return (IAutoConnectContainer)GetValue(AutoConnectContainerProperty); }
            set { SetValue(AutoConnectContainerProperty, value); }
        }
 
        public static Vector CalculateMovement(Key key, bool isRightToLeft)
        {
            Vector moveDir;
            switch (key)
            {
                case Key.Down:
                    moveDir = new Vector(0, FreeFormPanel.GridSize);
                    break;
                case Key.Up:
                    moveDir = new Vector(0, -FreeFormPanel.GridSize);
                    break;
                case Key.Right:
                    moveDir = new Vector(FreeFormPanel.GridSize, 0);
                    break;
                case Key.Left:
                    moveDir = new Vector(-FreeFormPanel.GridSize, 0);
                    break;
                default:
                    Fx.Assert(false, "Invalid case");
                    moveDir = new Vector(0, 0);
                    break;
            }
 
            if (isRightToLeft)
            {
                moveDir.X = -moveDir.X;
            }
 
            return moveDir;
        }
 
        public static double ZeroIfNegative(double val)
        {
            return val.IsNoGreaterThan(0) ? 0 : val;
        }
 
        internal UIElement CurrentAutoConnectTarget
        {
            get
            {
                return this.autoConnectHelper.CurrentTarget;
            }
        }
 
        internal Connector CurrentAutoSplitTarget
        {
            get;
            set;
        }
 
        bool AutoConnectEnabled
        {
            get
            {
                if (this.designerConfigurationService == null)
                {
                    DesignerView view = VisualTreeUtils.FindVisualAncestor<DesignerView>(this);
                    if (view != null)
                    {
                        this.designerConfigurationService = view.Context.Services.GetService<DesignerConfigurationService>();
                        return this.designerConfigurationService.AutoConnectEnabled;
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return this.designerConfigurationService.AutoConnectEnabled;
                }
            }
        }
 
        public static ConnectionPoint GetDestinationConnectionPoint(DependencyObject obj)
        {
            return (ConnectionPoint)obj.GetValue(FreeFormPanel.DestinationConnectionPointProperty);
        }
 
        public static void SetDestinationConnectionPoint(DependencyObject obj, ConnectionPoint connectionPoint)
        {
            obj.SetValue(FreeFormPanel.DestinationConnectionPointProperty, connectionPoint);
        }
 
        public static ConnectionPoint GetSourceConnectionPoint(DependencyObject obj)
        {
            return (ConnectionPoint)obj.GetValue(FreeFormPanel.SourceConnectionPointProperty);
        }
 
        public static void SetSourceConnectionPoint(DependencyObject obj, ConnectionPoint connectionPoint)
        {
            obj.SetValue(FreeFormPanel.SourceConnectionPointProperty, connectionPoint);
        }
 
        public static Point GetLocation(DependencyObject obj)
        {
            return (Point)obj.GetValue(FreeFormPanel.LocationProperty);
        }
 
        public static void SetLocation(DependencyObject obj, Point point)
        {
            obj.SetValue(FreeFormPanel.LocationProperty, point);
        }
 
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            this.SnapsToDevicePixels = true;
            this.AllowDrop = true;
        }
 
        internal void RemoveAutoConnectAdorner()
        {
            this.autoConnectHelper.RemoveDropTargets();
        }
 
        internal List<DependencyObject> GetChildShapes(DependencyObject excluded)
        {
            List<DependencyObject> children = new List<DependencyObject>();
            foreach (UIElement element in this.Children)
            {
                if (element is Connector)
                {
                    continue;
                }
                if (object.Equals(element, excluded))
                {
                    continue;
                }
                else if (element is VirtualizedContainerService.VirtualizingContainer)
                {
                    if (object.Equals(excluded, ((VirtualizedContainerService.VirtualizingContainer)element).Child))
                    {
                        continue;
                    }
                }
                children.Add(element);
            }
            return children;
        }
 
        protected override void OnPreviewDragOver(DragEventArgs e)
        {
            if (this.IsOutmostPanel())
            {
                if (this.AutoConnectEnabled && DragDropHelper.GetDraggedObjectCount(e) == 1)
                {
                    this.autoConnectHelper.OnPreviewDragOverPanel(e);
                }
            }
            base.OnPreviewDragOver(e);
        }
 
        public void UpdateConnectorPoints(Connector connector, List<Point> points)
        {
            PointCollection pointCollection = new PointCollection();
            foreach (Point point in points)
            {
                pointCollection.Add(new Point(point.X < 0 ? 0 : point.X, point.Y < 0 ? 0 : point.Y));
            }
            connector.Points = pointCollection;
            OnLocationChanged(connector, null);
        }
 
        static public List<Point> GetEdgeRelativeToOutmostPanel(ConnectionPoint connectionPoint)
        {
            return connectionPoint.Edge;
        }
 
        static public Point GetLocationRelativeToOutmostPanel(ConnectionPoint connectionPoint)
        {
            return connectionPoint.Location;
        }
 
        public Point GetLocationRelativeToOutmostPanel(Point location)
        {
            return this.TranslatePoint(location, this.GetOutmostPanel());
        }
 
        FreeFormPanel GetOutmostPanel()
        {
            DependencyObject obj = this;
            do
            {
                obj = VisualTreeHelper.GetParent(obj);
            }
            while (obj != null && !typeof(INestedFreeFormPanelContainer).IsAssignableFrom(obj.GetType()));
 
            if (obj != null)
            {
                INestedFreeFormPanelContainer container = (INestedFreeFormPanelContainer)obj;
                if (container.GetChildFreeFormPanel() == this)
                {
                    return container.GetOutmostFreeFormPanel();
                }
            }
            return this;
        }
 
        internal bool IsOutmostPanel()
        {
            return this == this.GetOutmostPanel();
        }
 
        internal static ConnectionPoint ConnectionPointHitTest(Point hitPoint, ConnectionPointsAdorner adorner)
        {
            FreeFormPanel panel = VisualTreeUtils.FindVisualAncestor<FreeFormPanel>(adorner.AdornedElement);
            return ConnectionPointHitTest(hitPoint, adorner.ConnectionPoints, panel);
        }
 
        internal static ConnectionPoint ConnectionPointHitTest(Point hitPoint, List<ConnectionPoint> connectionPoints, FreeFormPanel panel)
        {
            ConnectionPoint hitConnectionPoint = null;
            FreeFormPanel outmost = panel.GetOutmostPanel();
            foreach (ConnectionPoint connPoint in connectionPoints)
            {
                if (connPoint != null && connPoint.IsEnabled)
                {
                    if (new Rect(panel.TranslatePoint(connPoint.Location, outmost) + connPoint.HitTestOffset, connPoint.HitTestSize).Contains(hitPoint))
                    {
                        hitConnectionPoint = connPoint;
                        break;
                    }
                }
            }
            return hitConnectionPoint;
        }
 
        protected override Size ArrangeOverride(Size finalSize)
        {
            double height = 0;
            double width = 0;
            for (int i = 0; i < Children.Count; i++)
            {
                Point pt = new Point(0, 0);
                Size size = Children[i].DesiredSize;
                if (Children[i].GetType() == typeof(Connector))
                {
                    ((UIElement)Children[i]).Arrange(new Rect(pt, size));
                }
                else
                {
                    pt = FreeFormPanel.GetLocation(Children[i]);
                    ((UIElement)Children[i]).Arrange(new Rect(pt, size));
                }
                if (width < (size.Width + pt.X))
                {
                    width = size.Width + pt.X;
                }
                if (height < (size.Height + pt.Y))
                {
                    height = size.Height + pt.Y;
                }
            }
            width = (width < this.MinWidth) ? this.MinWidth : width;
            width = (width < this.Width) ? (this.Width < Double.MaxValue ? this.Width : width) : width;
 
            height = (height < this.MinHeight) ? this.MinHeight : height;
            height = (height < this.Height) ? (this.Height < Double.MaxValue ? this.Height : height) : height;
 
            return new Size(width, height);
        }
 
        protected override Size MeasureOverride(Size availableSize)
        {
            bool isOutmostPanel = this.IsOutmostPanel();
            base.MeasureOverride(availableSize);
            double height;
            double width;
            this.MeasureChildren(out height, out width);
            if (this.RequiredSizeChanged != null)
            {
                this.RequiredSizeChanged(this, new RequiredSizeChangedEventArgs(new Size(width, height)));
            }
            this.RequiredWidth = width;
            this.RequiredHeight = height;
 
            if (isOutmostPanel)
            {
                Action MeasureConnectors = () =>
                {
                    //This action will execute at Input priority. 
                    //Enabling measuring on Connectors and forcing a MeasureOverride by calling InvalidateMeasure.
                    this.measureConnectors = true;
                    this.InvalidateMeasure();
                };
                if (!measureConnectorsPosted)
                {
                    this.Dispatcher.BeginInvoke(DispatcherPriority.Input, MeasureConnectors);
                    measureConnectorsPosted = true;
                }
                if (measureConnectors)
                {
                    measureConnectors = false;
                    measureConnectorsPosted = false;
                }
            }
            width = (width < this.Width) ? (this.Width < Double.MaxValue ? this.Width : width) : width;
            height = (height < this.Height) ? (this.Height < Double.MaxValue ? this.Height : height) : height;
            return new Size(width, height);
        }
 
        private void MeasureChildren(out double height, out double width)
        {
            height = 0;
            width = 0;
            Point pt = new Point(0, 0);
            bool isOutmostPanel = this.IsOutmostPanel();
            foreach (UIElement child in Children)
            {
                Connector connectorChild = child as Connector;
                if (connectorChild != null && isOutmostPanel)
                {
                    pt = new Point(0, 0);
 
                    if (measureConnectors)
                    {
                        Point srcPoint = FreeFormPanel.GetLocationRelativeToOutmostPanel(FreeFormPanel.GetSourceConnectionPoint(connectorChild));
                        Point destPoint = FreeFormPanel.GetLocationRelativeToOutmostPanel(FreeFormPanel.GetDestinationConnectionPoint(connectorChild));
                        if (connectorChild.Points.Count == 0 || !this.Disabled &&
                            ((DesignerGeometryHelper.ManhattanDistanceBetweenPoints(connectorChild.Points[0], srcPoint) > ConnectorRouter.EndPointTolerance)
                            || (DesignerGeometryHelper.ManhattanDistanceBetweenPoints(connectorChild.Points[connectorChild.Points.Count - 1], destPoint) > ConnectorRouter.EndPointTolerance)))
                        {
                            connectorChild.Points = new PointCollection();
                            RoutePolyLine(connectorChild);
                        }
                        connectorChild.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                    }
                    else
                    {
                        continue;
                    }
                }
                else //Measure non-connector elements.
                {
                    child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                    if (!child.DesiredSize.Equals(((Size)FreeFormPanel.GetChildSize(child))))
                    {
                        FreeFormPanel.SetChildSize(child, child.DesiredSize);
                    }
                    pt = FreeFormPanel.GetLocation(child);
                    if (!IsLocationValid(pt))
                    {
                        pt = new Point(LeftStackingMargin, lastYPosition);
                        OnLocationChanged(child, new LocationChangedEventArgs(pt));
                        FreeFormPanel.SetLocation(child, pt);
                        lastYPosition += child.DesiredSize.Height + VerticalStackingDistance;
                    }
                }
                if (height < child.DesiredSize.Height + pt.Y)
                {
                    height = child.DesiredSize.Height + pt.Y;
                }
                if (width < child.DesiredSize.Width + pt.X)
                {
                    width = child.DesiredSize.Width + pt.X;
                }
            }
 
            width = (width < this.MinWidth) ? this.MinWidth : width;
            height = (height < this.MinHeight) ? this.MinHeight : height;
        }
 
        static bool IsLocationValid(Point location)
        {
            return location.X >= 0 && location.Y >= 0;
        }
 
        void OnLocationChanged(Object sender, LocationChangedEventArgs e)
        {
            if (LocationChanged != null)
            {
                LocationChanged(sender, e);
            }
        }
 
        protected override void OnMouseLeave(MouseEventArgs e)
        {
            if (e != null && !this.Disabled && this.IsOutmostPanel())
            {
                if (connectorEditor != null && connectorEditor.BeingEdited
                    && Mouse.DirectlyOver != null
                    && !(Mouse.DirectlyOver is ConnectionPointsAdorner))
                {
                    SaveConnectorEditor(e.GetPosition(this));
                }
            }
            base.OnMouseLeave(e);
        }
 
        protected override void OnMouseMove(System.Windows.Input.MouseEventArgs e)
        {
            if (e != null && !this.Disabled && this.IsOutmostPanel())
            {
                if (this.AutoConnectEnabled)
                {
                    this.RemoveAutoConnectAdorner();
                }
                if (e.LeftButton == MouseButtonState.Pressed)
                {
                    if (connectorEditor != null && connectorEditor.BeingEdited)
                    {
                        AutoScrollHelper.AutoScroll(e, this, 1);
                        connectorEditor.Update(e.GetPosition(this));
                        e.Handled = true;
                    }
                }
            }
            base.OnMouseMove(e);
        }
 
        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            if (e != null && !this.Disabled && this.IsOutmostPanel())
            {
                if (connectorEditor != null && connectorEditor.BeingEdited)
                {
                    SaveConnectorEditor(e.GetPosition(this));
                }
            }
            base.OnMouseLeftButtonUp(e);
        }
 
        public void RemoveConnectorEditor()
        {
            if (connectorEditor != null)
            {
                connectorEditor.Remove();
                connectorEditor = null;
            }
 
        }
 
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e != null && !this.Disabled && this.IsOutmostPanel())
            {
                if (connectorEditor != null && connectorEditor.BeingEdited)
                {
                    if (e.Key == Key.Escape)
                    {
                        //If escape key is hit while dragging a connector, end dragging.
 
                        Connector affectedConnector = connectorEditor.Connector;
                        RemoveConnectorEditor();
                        this.connectorEditor = new ConnectorEditor(this, affectedConnector);
                    }
 
                    // Ignore all other Keyboard input when rerouting connector
                    e.Handled = true;
                }
            }
 
            base.OnKeyDown(e);
        }
 
        static bool ShouldCreateNewConnectorEditor(MouseButtonEventArgs e)
        {
            Connector connector = e.Source as Connector;
            // Don't create new connector editor when clicking on the start dot.
            if (connector == null || (connector.StartDot != null && connector.StartDot.IsAncestorOf(e.MouseDevice.DirectlyOver as DependencyObject)))
            {
                return false;
            }
            return true;
        }
 
        protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            if (e != null && !this.Disabled && this.IsOutmostPanel() && e.ClickCount == 1)
            {
                //If one of the edit points is clicked, update the connector editor.
                if ((connectorEditor != null) && connectorEditor.EditPointsHitTest(e.GetPosition(this)))
                {
                    connectorEditor.Update(e.GetPosition(this));
                    e.Handled = true;
                }
                else if (ShouldCreateNewConnectorEditor(e))
                {
                    CreateNewConnectorEditor(e);
                }
            }
            base.OnPreviewMouseLeftButtonDown(e);
        }
 
        void CreateNewConnectorEditor(MouseButtonEventArgs e)
        {
            if (connectorEditor == null || !e.Source.Equals(connectorEditor.Connector))
            {
                //If user clicks anywhere other than the connector editor, destroy it.
                RemoveConnectorEditor();
                if (typeof(Connector).IsAssignableFrom(e.Source.GetType()))
                {
                    this.connectorEditor = new ConnectorEditor(this, e.Source as Connector);
                }
            }
        }
 
        //Calls the Line routing algorithm and populates the points collection of the connector.
        void RoutePolyLine(Connector connector)
        {
            Point[] pts = ConnectorRouter.Route(this, FreeFormPanel.GetSourceConnectionPoint(connector), FreeFormPanel.GetDestinationConnectionPoint(connector));
            List<Point> points = new List<Point>(pts);
            if (pts != null)
            {
                UpdateConnectorPoints(connector, points);
            }
        }
 
 
        //Connector editing is complete, save the final connectorEditor state into the connector.
        void SaveConnectorEditor(Point pt)
        {
            bool isConnectionEndPointMoved = !connectorEditor.Persist(pt);
 
            if (this.ConnectorMoved != null)
            {
                Connector connector = this.connectorEditor.Connector;
                List<Point> points = this.connectorEditor.ConnectorEditorLocation;
                ConnectorMoved(connector, new ConnectorMovedEventArgs(points));
            }
 
            if (isConnectionEndPointMoved)
            {
                //Persist will return false, when the ConnectionEndPoint has been moved.
                RemoveConnectorEditor();
            }
            else
            {
                this.InvalidateMeasure();
            }
        }
    }
}