File: System\Activities\Core\Presentation\StateContainerEditor.Utilities.cs
Project: ndp\cdf\src\NetFx40\Tools\System.Activities.Core.Presentation\System.Activities.Core.Presentation.csproj (System.Activities.Core.Presentation)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.Activities.Core.Presentation
{
    using System;
    using System.Activities.Presentation.FreeFormEditing;
    using System.Activities.Presentation.Internal.PropertyEditing;
    using System.Activities.Presentation.Model;
    using System.Activities.Presentation.View;
    using System.Activities.Statements;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Runtime;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Media;
 
    partial class StateContainerEditor
    {
        internal static ModelItem GetConnectorModelItem(DependencyObject obj)
        {
            return (ModelItem)obj.GetValue(StateContainerEditor.ConnectorModelItemProperty);
        }
 
        static void SetConnectorModelItem(DependencyObject obj, ModelItem modelItem)
        {
            obj.SetValue(StateContainerEditor.ConnectorModelItemProperty, modelItem);
        }
 
        internal static List<ConnectionPoint> GetConnectionPoints(DependencyObject obj)
        {
            if (obj is StartSymbol)
            {
                return (List<ConnectionPoint>)obj.GetValue(StateContainerEditor.ConnectionPointsProperty);
            }
            if (!(obj is VirtualizedContainerService.VirtualizingContainer))
            {
                obj = VisualTreeUtils.FindVisualAncestor<VirtualizedContainerService.VirtualizingContainer>(obj);
            }
            return (List<ConnectionPoint>)obj.GetValue(StateContainerEditor.ConnectionPointsProperty);
        }
 
        static void SetConnectionPoints(DependencyObject obj, List<ConnectionPoint> connectionPoints)
        {
            obj.SetValue(StateContainerEditor.ConnectionPointsProperty, connectionPoints);
        }
 
        static void SetConnectorSrcDestConnectionPoints(Connector connector, ConnectionPoint srcConnectionPoint, ConnectionPoint destConnectionPoint)
        {
            FreeFormPanel.SetSourceConnectionPoint(connector, srcConnectionPoint);
            FreeFormPanel.SetDestinationConnectionPoint(connector, destConnectionPoint);
            srcConnectionPoint.AttachedConnectors.Add(connector);
            destConnectionPoint.AttachedConnectors.Add(connector);
        }
 
        static void SetConnectorLabel(Connector connector, ModelItem connectorModelItem)
        {
 
            connector.SetBinding(Connector.LabelTextProperty,  new Binding()
            {
                Source = connectorModelItem,
                Path = new PropertyPath("DisplayName")
            });
 
            TextBlock toolTip = new TextBlock();
            toolTip.SetBinding(TextBlock.TextProperty, new Binding()
            {
                Source = connectorModelItem,
                Path = new PropertyPath("DisplayName"),
                StringFormat = TransitionNameToolTip + Environment.NewLine + SR.EditTransitionTooltip + Environment.NewLine + SR.CopyTransitionToolTip
            });
 
            connector.SetLabelToolTip(toolTip);
        }
 
        static void SetConnectorStartDotToolTip(FrameworkElement startDot, ModelItem connectorModelItem)
        {
            ModelItem triggerModelItem = connectorModelItem.Properties[TransitionDesigner.TriggerPropertyName].Value as ModelItem;
            string triggerName = null;
            if (triggerModelItem == null)
            {
                triggerName = "(null)";
            }
            else
            {
                ModelItem displayNameModelItem = triggerModelItem.Properties["DisplayName"].Value;
                if (displayNameModelItem != null)
                {
                    triggerName = displayNameModelItem.GetCurrentValue() as string;
                }
            }
            startDot.ToolTip = string.Format(CultureInfo.InvariantCulture, TriggerNameToolTip, triggerName) + Environment.NewLine + SR.SharedTriggerToolTip;
        }
 
 
        // Returns true if visual is on the visual tree for point p relative to the reference.
        static bool IsVisualHit(UIElement visual, UIElement reference, Point point)
        {
            bool visualIsHit = false;
            HitTestResult result = VisualTreeHelper.HitTest(reference, point);
            if (result != null)
            {
                DependencyObject obj = result.VisualHit;
                while (obj != null)
                {
                    if (visual.Equals(obj))
                    {
                        visualIsHit = true;
                        break;
                    }
                    obj = VisualTreeHelper.GetParent(obj);
                }
            }
            return visualIsHit;
        }
 
        //This snaps the center of the element to grid.
        //Wherever shapeAnchorPoint is valid, it is made co-incident with the drop location.
        static Point SnapVisualToGrid(UIElement element, Point location, Point shapeAnchorPoint, bool isAnchorPointValid)
        {
            Fx.Assert(element != null, "Input UIElement is null");
            element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
            Point oldCenter = location;
            if (!isAnchorPointValid)
            {
                //shapeAnchorPoint is invalid in case where it does not make sense (eg. toolbox drop).
                location.X -= InitialNodeWidth / 2;
                location.Y -= InitialNodeHeight / 2;
            }
            else
            {
                location.X -= shapeAnchorPoint.X;
                location.Y -= shapeAnchorPoint.Y;
                oldCenter = new Point(location.X + element.DesiredSize.Width / 2, location.Y + element.DesiredSize.Height / 2);
            }
 
            Point newCenter = SnapPointToGrid(oldCenter);
 
            location.Offset(newCenter.X - oldCenter.X, newCenter.Y - oldCenter.Y);
 
            if (location.X < 0)
            {
                double correction = FreeFormPanel.GridSize - ((location.X * (-1)) % FreeFormPanel.GridSize);
                location.X = (correction == FreeFormPanel.GridSize) ? 0 : correction;
            }
            if (location.Y < 0)
            {
                double correction = FreeFormPanel.GridSize - ((location.Y * (-1)) % FreeFormPanel.GridSize);
                location.Y = (correction == FreeFormPanel.GridSize) ? 0 : correction;
            }
            return location;
        }
 
        static Point SnapPointToGrid(Point pt)
        {
            pt.X -= pt.X % FreeFormPanel.GridSize;
            pt.Y -= pt.Y % FreeFormPanel.GridSize;
            pt.X = pt.X < 0 ? 0 : pt.X;
            pt.Y = pt.Y < 0 ? 0 : pt.Y;
            return pt;
        }
 
        static IEnumerable<Adorner> RemoveAdorner(UIElement adornedElement, Type adornerType)
        {
            Fx.Assert(adornedElement != null, "Invalid argument");
            Fx.Assert(typeof(Adorner).IsAssignableFrom(adornerType), "Invalid argument");
            List<Adorner> adornersRemoved = new List<Adorner>();
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(adornedElement);
            if (adornerLayer != null)
            {
                Adorner[] adorners = adornerLayer.GetAdorners(adornedElement);
                if (adorners != null)
                {
                    foreach (Adorner adorner in adorners)
                    {
                        if (adornerType.IsAssignableFrom(adorner.GetType()))
                        {
                            adornerLayer.Remove(adorner);
                            adornersRemoved.Add(adorner);
                        }
                    }
                }
            }
            return adornersRemoved;
        }
 
        internal static List<Connector> GetAttachedConnectors(UIElement shape)
        {
            HashSet<Connector> attachedConnectors = new HashSet<Connector>();
            List<ConnectionPoint> allConnectionPoints = GetConnectionPoints(shape);
            if (allConnectionPoints != null)
            {
                foreach (ConnectionPoint connPoint in allConnectionPoints)
                {
                    if (connPoint != null)
                    {
                        foreach (Connector connector in connPoint.AttachedConnectors)
                        {
                            attachedConnectors.Add(connector);
                        }
                    }
                }
            }
            return attachedConnectors.ToList<Connector>();
        }
 
        static List<Connector> GetOutgoingConnectors(UIElement shape)
        {
            List<Connector> outgoingConnectors = new List<Connector>();
            List<ConnectionPoint> allConnectionPoints = GetConnectionPoints(shape);
            foreach (ConnectionPoint connPoint in allConnectionPoints)
            {
                if (connPoint != null)
                {
                    outgoingConnectors.AddRange(connPoint.AttachedConnectors.Where(p => FreeFormPanel.GetSourceConnectionPoint(p).Equals(connPoint)));
                }
            }
            return outgoingConnectors;
        }
 
        static List<Connector> GetIncomingConnectors(UIElement shape)
        {
            List<Connector> incomingConnectors = new List<Connector>();
            List<ConnectionPoint> allConnectionPoints = GetConnectionPoints(shape);
            foreach (ConnectionPoint connPoint in allConnectionPoints)
            {
                if (connPoint != null)
                {
                    incomingConnectors.AddRange(connPoint.AttachedConnectors.Where(p => FreeFormPanel.GetDestinationConnectionPoint(p).Equals(connPoint)));
                }
            }
            return incomingConnectors;
        }
 
        static ConnectionPoint ConnectionPointHitTest(UIElement element, Point hitPoint)
        {
            FreeFormPanel panel = VisualTreeUtils.FindVisualAncestor<FreeFormPanel>(element);
            List<ConnectionPoint> connectionPoints = StateContainerEditor.GetConnectionPoints(element);
            return FreeFormPanel.ConnectionPointHitTest(hitPoint, connectionPoints, panel);
        }
 
        static ConnectionPoint GetConnectionPoint(UIElement element, Point location)
        {
            List<ConnectionPoint> connectionPoints = StateContainerEditor.GetConnectionPoints(element);
            foreach (ConnectionPoint connectionPoint in connectionPoints)
            {
                if (DesignerGeometryHelper.ManhattanDistanceBetweenPoints(location, connectionPoint.Location) <= ConnectorRouter.EndPointTolerance)
                {
                    return connectionPoint;
                }
            }
            return null;
        }
 
        internal static ModelItem GetStateMachineModelItem(ModelItem modelItem)
        {
            ModelItem currentModelItem = modelItem;
            while (currentModelItem != null && currentModelItem.ItemType != typeof(StateMachine))
            {
                currentModelItem = currentModelItem.Parent;
            }
            return currentModelItem;
        }
 
        static bool AreInSameStateMachine(ModelItem modelItem1, ModelItem modelItem2)
        {
            return GetStateMachineModelItem(modelItem1) == GetStateMachineModelItem(modelItem2);
        }
 
        internal static ModelItem GetParentStateModelItemForTransition(ModelItem transitionModelItem)
        {
            ModelItem parent = transitionModelItem;
            while (parent != null && parent.ItemType != typeof(State))
            {
                parent = parent.Parent;
            }
            return parent;
        }
 
        internal static UIElement GetStateView(ModelItem stateModelItem)
        {
            ModelItem parent = GetStateMachineModelItem(stateModelItem);
            if (parent.View is StateMachineDesigner)
            {
                return ((StateMachineDesigner)parent.View).StateContainerEditor.modelItemToUIElement[stateModelItem];
            }
            return null;
        }
 
        static ModelItem GetModelItemFromView(UIElement element)
        {
            ModelItem modelItem = null;
            if (element is StartSymbol)
            {
                modelItem = ((StartSymbol)element).ModelItem;
            }
            else
            {
                modelItem = ((VirtualizedContainerService.VirtualizingContainer)element).ModelItem;
            }
            return modelItem;
        }
 
        static internal ConnectionPoint GetClosestConnectionPoint(ConnectionPoint srcConnPoint, List<ConnectionPoint> destConnPoints, out double minDist)
        {
            minDist = double.PositiveInfinity;
            double dist = 0;
            ConnectionPoint closestPoint = null;
            Point srcPoint = FreeFormPanel.GetLocationRelativeToOutmostPanel(srcConnPoint);
            foreach (ConnectionPoint destConnPoint in destConnPoints)
            {
                if (srcConnPoint != destConnPoint)
                {
                    dist = DesignerGeometryHelper.ManhattanDistanceBetweenPoints(srcPoint, FreeFormPanel.GetLocationRelativeToOutmostPanel(destConnPoint));
                    if (dist < minDist)
                    {
                        minDist = dist;
                        closestPoint = destConnPoint;
                    }
                }
            }
 
            return closestPoint;
        }
 
        static ConnectionPoint GetClosestConnectionPointNotOfType(ConnectionPoint srcConnectionPoint, List<ConnectionPoint> targetConnectionPoints, ConnectionPointKind illegalConnectionPointKind)
        {
            double minDist;
            List<ConnectionPoint> filteredConnectionPoints = new List<ConnectionPoint>();
            foreach (ConnectionPoint connPoint in targetConnectionPoints)
            {
                if (connPoint.PointType != illegalConnectionPointKind && !connPoint.Equals(srcConnectionPoint) && connPoint.AttachedConnectors.Count == 0)
                {
                    filteredConnectionPoints.Add(connPoint);
                }
            }
            return GetClosestConnectionPoint(srcConnectionPoint, filteredConnectionPoints, out minDist);
        }
 
        static void GetClosestConnectionPointPair(List<ConnectionPoint> srcConnPoints, List<ConnectionPoint> destConnPoints, out ConnectionPoint srcConnPoint, out ConnectionPoint destConnPoint)
        {
            double minDist = double.PositiveInfinity;
            double dist;
            ConnectionPoint tempConnPoint;
            srcConnPoint = null;
            destConnPoint = null;
            foreach (ConnectionPoint connPoint in srcConnPoints)
            {
                tempConnPoint = GetClosestConnectionPoint(connPoint, destConnPoints, out dist);
                if (dist < minDist)
                {
                    minDist = dist;
                    srcConnPoint = connPoint;
                    destConnPoint = tempConnPoint;
                }
            }
            Fx.Assert(srcConnPoint != null, "No ConnectionPoint found");
            Fx.Assert(destConnPoint != null, "No ConnectionPoint found");
        }
 
        static void GetEmptySrcDestConnectionPoints(UIElement source, UIElement dest, out ConnectionPoint srcConnPoint, out ConnectionPoint destConnPoint)
        {
            srcConnPoint = null;
            destConnPoint = null;
            List<ConnectionPoint> srcConnectionPoints = GetEmptyConnectionPoints(source);
            List<ConnectionPoint> destConnectionPoints = GetEmptyConnectionPoints(dest);
            if (srcConnectionPoints.Count > 0 && destConnectionPoints.Count > 0)
            {
                GetClosestConnectionPointPair(srcConnectionPoints, destConnectionPoints, out srcConnPoint, out destConnPoint);
            }
        }
 
        internal static List<ConnectionPoint> GetEmptyConnectionPoints(UIElement designer)
        {
            List<ConnectionPoint> connectionPoints = StateContainerEditor.GetConnectionPoints(designer);
            if (connectionPoints != null)
            {
                return new List<ConnectionPoint>(connectionPoints.Where<ConnectionPoint>(
                    (p) => { return p.AttachedConnectors == null || p.AttachedConnectors.Count == 0; }));
            }
            return new List<ConnectionPoint>();
        }
 
        //This returns the closest non-incoming connectionPoint on source. Return value will be different than destConnectionPoint.
        static ConnectionPoint GetClosestSrcConnectionPoint(UIElement src, ConnectionPoint destConnectionPoint)
        {
            ConnectionPoint srcConnectionPoint = null;
            if (destConnectionPoint.PointType != ConnectionPointKind.Outgoing)
            {
                srcConnectionPoint = GetClosestConnectionPointNotOfType(destConnectionPoint, StateContainerEditor.GetConnectionPoints(src), ConnectionPointKind.Incoming);
            }
            return srcConnectionPoint;
        }
 
        //This returns the closest non-outgoing connectionPoint on dest. Return value will be different than sourceConnectionPoint.
        static ConnectionPoint GetClosestDestConnectionPoint(ConnectionPoint sourceConnectionPoint, UIElement dest)
        {
            ConnectionPoint destConnectionPoint = null;
            if (sourceConnectionPoint.PointType != ConnectionPointKind.Incoming)
            {
                destConnectionPoint = GetClosestConnectionPointNotOfType(sourceConnectionPoint, StateContainerEditor.GetConnectionPoints(dest), ConnectionPointKind.Outgoing);
            }
            return destConnectionPoint;
        }
 
        static ConnectionPoint GetSrcConnectionPointForSharedTrigger(UIElement sourceDesigner, ModelItem connectorModelItem)
        {
            ConnectionPoint sourceConnectionPoint = null;
            List<Connector> connectors = StateContainerEditor.GetOutgoingConnectors(sourceDesigner);
            foreach (Connector connector in connectors)
            {
                ModelItem modelItem = StateContainerEditor.GetConnectorModelItem(connector);
                if (modelItem != null && modelItem.ItemType == typeof(Transition))
                {
                    if (modelItem.Properties[TransitionDesigner.TriggerPropertyName].Value == connectorModelItem.Properties[TransitionDesigner.TriggerPropertyName].Value)
                    {
                        sourceConnectionPoint = FreeFormPanel.GetSourceConnectionPoint(connector);
                    }
                }
            }
            return sourceConnectionPoint;
        }
 
        // Test if the transition is contained by any of the states or their descendants
        static bool IsTransitionModelItemContainedByStateModelItems(ModelItem transitionModelItem, IEnumerable<ModelItem> stateModelItems)
        {
            foreach (ModelItem stateModelItem in stateModelItems)
            {
                if (stateModelItem.Properties[StateDesigner.TransitionsPropertyName].Collection.Contains(transitionModelItem))
                {
                    return true;
                }
            }
            return false;
        }
 
        static bool IsTransitionDestinationWithinStates(Transition transition, IEnumerable<State> states)
        {
            foreach (State state in states)
            {
                if (transition.To == state)
                {
                    return true;
                }
            }
            return false;
        }
 
        // Remove dangling transitions that are not pointing to any of the input states or their descendants
        static void RemoveDanglingTransitions(IEnumerable<State> states)
        {
            Queue<State> statesToProcess = new Queue<State>(states);
            while (statesToProcess.Count > 0)
            {
                State state = statesToProcess.Dequeue();
 
                IEnumerable<Transition> toRemove = state.Transitions.Where<Transition>((p) =>
                    { return !IsTransitionDestinationWithinStates(p, states); }).Reverse();
                foreach (Transition transition in toRemove)
                {
                    state.Transitions.Remove(transition);
                }
 
            }
        }
 
        static List<ModelItem> GetTransitionModelItems(IEnumerable<ModelItem> stateModelItems)
        {
            List<ModelItem> transitionModelItems = new List<ModelItem>();
            foreach (ModelItem stateModelItem in stateModelItems)
            {
                transitionModelItems.AddRange(stateModelItem.Properties[StateDesigner.TransitionsPropertyName].Collection);
                //transitionModelItems.AddRange(GetTransitionModelItems(stateModelItem.Properties[ChildStatesPropertyName].Collection));
            }
            return transitionModelItems;
        }
 
        internal static bool IsFinalState(ModelItem modelItem)
        {
            return modelItem.ItemType == typeof(State) && (bool)modelItem.Properties[StateDesigner.IsFinalPropertyName].Value.GetCurrentValue();
        }
 
        static void ShowMessageBox(string message)
        {
            MessageBox.Show(message, SR.ErrorMessageBoxTitle, MessageBoxButton.OK, MessageBoxImage.Error);
        }
 
        static void ReportConnectorCreationError(ConnectorCreationResult result)
        {
            switch (result)
            {
                case ConnectorCreationResult.CannotCreateTransitionToCompositeState:
                    ShowMessageBox(SR.CannotCreateTransitionToCompositeState);
                    break;
                case ConnectorCreationResult.CannotCreateTransitionFromAncestorToDescendant:
                    ShowMessageBox(SR.CannotCreateTransitionFromAncestorToDescendant);
                    break;
                case ConnectorCreationResult.CannotSetCompositeStateAsInitialState:
                    ShowMessageBox(SR.CannotSetCompositeStateAsInitialState);
                    break;
                case ConnectorCreationResult.CannotSetFinalStateAsInitialState:
                    ShowMessageBox(SR.CannotSetFinalStateAsInitialState);
                    break;
                case ConnectorCreationResult.OtherFailure:
                    ShowMessageBox(SR.CannotCreateLink);
                    break;
            }
        }
 
        static bool IsConnectorFromInitialNode(Connector connector)
        {
            return GetConnectorModelItem(connector).ItemType == typeof(StateMachine);
        }
 
        internal static string GenerateTransitionName(ModelItem stateMachineModelItem)
        {
            Fx.Assert(stateMachineModelItem.ItemType == typeof(StateMachine), "ModelItem param should be a statemachine.");
            HashSet<String> existingTransitionNames = new HashSet<string>();
 
            foreach (ModelItem stateModelItem in stateMachineModelItem.Properties[StateMachineDesigner.StatesPropertyName].Collection)
            {
                foreach (ModelItem transitionModelItem in stateModelItem.Properties[StateDesigner.TransitionsPropertyName].Collection)
                {
                    existingTransitionNames.Add(((Transition)transitionModelItem.GetCurrentValue()).DisplayName);
                }
            }
 
            int suffix = 0;
            string name;
 
            do
            {
                name = string.Format(CultureInfo.InvariantCulture, "T{0}", ++suffix);
            } while (existingTransitionNames.Contains<string>(name));
 
            return name;
        }
    }
}