36edb8dc3d513593b08af4c2e97c48fcdbc3f53b
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / FreeFormEditing / AutoSplitHelper.cs
1 //----------------------------------------------------------------
2 // <copyright company="Microsoft Corporation">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //----------------------------------------------------------------
6
7 namespace System.Activities.Presentation.FreeFormEditing
8 {
9     using System.Collections.Generic;
10     using System.Windows;
11
12     internal static class AutoSplitHelper
13     {
14         private const double ShapeMargin = 40;
15
16         public static void CalculateEntryExitEdges(Point mousePosition, Connector connector, out EdgeLocation entryEdge, out EdgeLocation exitEdge)
17         {
18             UIElement srcShape = FreeFormPanel.GetSourceConnectionPoint(connector).ParentDesigner;
19             UIElement destShape = FreeFormPanel.GetDestinationConnectionPoint(connector).ParentDesigner;
20             Point srcLocation = FreeFormPanel.GetLocation(srcShape);
21             Point destLocation = FreeFormPanel.GetLocation(destShape);
22             Size srcSize = FreeFormPanel.GetChildSize(srcShape);
23             Size destSize = FreeFormPanel.GetChildSize(destShape);
24
25             Point srcCenter = new Point(srcLocation.X + (srcSize.Width / 2), srcLocation.Y + (srcSize.Height / 2));
26             Point destCenter = new Point(destLocation.X + (destSize.Width / 2), destLocation.Y + (destSize.Height / 2));
27
28             entryEdge = CalculateEdgeLocation(mousePosition, srcCenter);
29             exitEdge = CalculateEdgeLocation(mousePosition, destCenter);
30
31             if (exitEdge == entryEdge)
32             {
33                 switch (entryEdge)
34                 {
35                     case EdgeLocation.Top:
36                         exitEdge = EdgeLocation.Bottom;
37                         break;
38
39                     case EdgeLocation.Bottom:
40                         exitEdge = EdgeLocation.Top;
41                         break;
42
43                     case EdgeLocation.Left:
44                         exitEdge = EdgeLocation.Right;
45                         break;
46
47                     case EdgeLocation.Right:
48                         exitEdge = EdgeLocation.Left;
49                         break;
50                 }
51             }
52         }
53
54         public static Point CalculateDropLocation(Point mousePosition, Point originalDropLocation, Connector connector, Size droppedSize, HashSet<Point> shapeLocations)
55         {
56             UIElement srcShape = FreeFormPanel.GetSourceConnectionPoint(connector).ParentDesigner;
57             UIElement destShape = FreeFormPanel.GetDestinationConnectionPoint(connector).ParentDesigner;
58             Point srcLocation = FreeFormPanel.GetLocation(srcShape);
59             Point destLocation = FreeFormPanel.GetLocation(destShape);
60             Size srcSize = FreeFormPanel.GetChildSize(srcShape);
61             Size destSize = FreeFormPanel.GetChildSize(destShape);
62
63             return CalculateDropLocation(mousePosition, originalDropLocation, droppedSize, srcLocation, destLocation, srcSize, destSize, shapeLocations);
64         }
65
66         internal static Point CalculateDropLocation(Point mousePosition, Point originalDropLocation, Size droppedSize, Point srcLocation, Point destLocation, Size srcSize, Size destSize, HashSet<Point> shapeLocations)
67         {
68             Point dropLocation = originalDropLocation;
69
70             double distToSrc = DesignerGeometryHelper.ManhattanDistanceBetweenPoints(mousePosition, new Point(srcLocation.X + (srcSize.Width / 2), srcLocation.Y + (srcSize.Height / 2)));
71             double distToDest = DesignerGeometryHelper.ManhattanDistanceBetweenPoints(mousePosition, new Point(destLocation.X + (destSize.Width / 2), destLocation.Y + (destSize.Height / 2)));
72
73             AutoSplitAlignment srcAlignment = GetAlignment(mousePosition, srcLocation, srcSize);
74             AutoSplitAlignment destAlignment = GetAlignment(mousePosition, destLocation, destSize);
75
76             if ((distToSrc <= distToDest || destAlignment == AutoSplitAlignment.None) && srcAlignment == AutoSplitAlignment.Vertical)
77             {
78                 dropLocation = CalculateDropLocationToAlignVertically(dropLocation, droppedSize, srcLocation, srcSize);
79             }
80             else if ((distToSrc <= distToDest || destAlignment == AutoSplitAlignment.None) && srcAlignment == AutoSplitAlignment.Horizontal)
81             {
82                 dropLocation = CalculateDropLocationToAlignHorizontally(dropLocation, droppedSize, srcLocation, srcSize);
83             }
84             else if ((distToSrc >= distToDest || srcAlignment == AutoSplitAlignment.None) && destAlignment == AutoSplitAlignment.Vertical)
85             {
86                 dropLocation = CalculateDropLocationToAlignVertically(dropLocation, droppedSize, destLocation, destSize);
87             }
88             else if ((distToSrc >= distToDest || srcAlignment == AutoSplitAlignment.None) && destAlignment == AutoSplitAlignment.Horizontal)
89             {
90                 dropLocation = CalculateDropLocationToAlignHorizontally(dropLocation, droppedSize, destLocation, destSize);
91             }
92
93             dropLocation.X = dropLocation.X < 0 ? 0 : dropLocation.X;
94             dropLocation.Y = dropLocation.Y < 0 ? 0 : dropLocation.Y;
95
96             // To avoid overlaps with existing shapes
97             if (shapeLocations != null)
98             {
99                 while (shapeLocations.Contains(dropLocation))
100                 {
101                     dropLocation.Offset(FreeFormPanel.GridSize, FreeFormPanel.GridSize);
102                 }
103             }
104
105             return dropLocation;
106         }
107
108         internal static AutoSplitAlignment GetAlignment(Point mousePosition, Point targetLocation, Size targetSize)
109         {
110             AutoSplitAlignment alignment = AutoSplitAlignment.None;
111             if (mousePosition.X >= targetLocation.X && mousePosition.X <= targetLocation.X + targetSize.Width)
112             {
113                 alignment = AutoSplitAlignment.Vertical;
114             }
115             else if (mousePosition.Y >= targetLocation.Y && mousePosition.Y <= targetLocation.Y + targetSize.Height)
116             {
117                 alignment = AutoSplitAlignment.Horizontal;
118             }
119
120             return alignment;
121         }
122
123         internal static Point CalculateDropLocationToAlignVertically(Point originalDropLocation, Size droppedSize, Point targetLocation, Size targetSize)
124         {
125             Point dropLocation = originalDropLocation;
126             dropLocation.X = targetLocation.X + ((targetSize.Width - droppedSize.Width) / 2);
127             if (dropLocation.Y >= targetLocation.Y && dropLocation.Y <= targetLocation.Y + targetSize.Height + ShapeMargin)
128             {
129                 dropLocation.Y = targetLocation.Y + targetSize.Height + ShapeMargin;
130             }
131             else if (dropLocation.Y + droppedSize.Height + ShapeMargin >= targetLocation.Y && dropLocation.Y <= targetLocation.Y)
132             {
133                 dropLocation.Y = targetLocation.Y - droppedSize.Height - ShapeMargin;
134             }
135
136             return dropLocation;
137         }
138
139         internal static Point CalculateDropLocationToAlignHorizontally(Point originalDropLocation, Size droppedSize, Point targetLocation, Size targetSize)
140         {
141             Point dropLocation = originalDropLocation;
142             dropLocation.Y = targetLocation.Y + ((targetSize.Height - droppedSize.Height) / 2);
143             if (dropLocation.X >= targetLocation.X && dropLocation.X <= targetLocation.X + targetSize.Width + ShapeMargin)
144             {
145                 dropLocation.X = targetLocation.X + targetSize.Width + ShapeMargin;
146             }
147             else if (dropLocation.X + droppedSize.Width + ShapeMargin >= targetLocation.X && dropLocation.X <= targetLocation.X)
148             {
149                 dropLocation.X = targetLocation.X - droppedSize.Width - ShapeMargin;
150             }
151
152             return dropLocation;
153         }
154
155         internal static EdgeLocation CalculateEdgeLocation(Point mousePosition, Point shapeCenter)
156         {
157             double distX = Math.Abs(shapeCenter.X - mousePosition.X);
158             double distY = Math.Abs(shapeCenter.Y - mousePosition.Y);
159             if (distX > distY)
160             {
161                 return shapeCenter.X < mousePosition.X ? EdgeLocation.Left : EdgeLocation.Right;
162             }
163             else
164             {
165                 return shapeCenter.Y < mousePosition.Y ? EdgeLocation.Top : EdgeLocation.Bottom;
166             }
167         }
168     }
169 }