4d36b9ae5677eed089136d030affa42da7acf8c4
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Core.Presentation / System / Activities / Core / Presentation / StateContainerEditor.Utilities.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.Activities.Core.Presentation
6 {
7     using System;
8     using System.Activities.Presentation.FreeFormEditing;
9     using System.Activities.Presentation.Internal.PropertyEditing;
10     using System.Activities.Presentation.Model;
11     using System.Activities.Presentation.View;
12     using System.Activities.Statements;
13     using System.Collections.Generic;
14     using System.Globalization;
15     using System.Linq;
16     using System.Runtime;
17     using System.Windows;
18     using System.Windows.Controls;
19     using System.Windows.Data;
20     using System.Windows.Documents;
21     using System.Windows.Media;
22
23     partial class StateContainerEditor
24     {
25         internal static ModelItem GetConnectorModelItem(DependencyObject obj)
26         {
27             return (ModelItem)obj.GetValue(StateContainerEditor.ConnectorModelItemProperty);
28         }
29
30         static void SetConnectorModelItem(DependencyObject obj, ModelItem modelItem)
31         {
32             obj.SetValue(StateContainerEditor.ConnectorModelItemProperty, modelItem);
33         }
34
35         internal static List<ConnectionPoint> GetConnectionPoints(DependencyObject obj)
36         {
37             if (obj is StartSymbol)
38             {
39                 return (List<ConnectionPoint>)obj.GetValue(StateContainerEditor.ConnectionPointsProperty);
40             }
41             if (!(obj is VirtualizedContainerService.VirtualizingContainer))
42             {
43                 obj = VisualTreeUtils.FindVisualAncestor<VirtualizedContainerService.VirtualizingContainer>(obj);
44             }
45             return (List<ConnectionPoint>)obj.GetValue(StateContainerEditor.ConnectionPointsProperty);
46         }
47
48         static void SetConnectionPoints(DependencyObject obj, List<ConnectionPoint> connectionPoints)
49         {
50             obj.SetValue(StateContainerEditor.ConnectionPointsProperty, connectionPoints);
51         }
52
53         static void SetConnectorSrcDestConnectionPoints(Connector connector, ConnectionPoint srcConnectionPoint, ConnectionPoint destConnectionPoint)
54         {
55             FreeFormPanel.SetSourceConnectionPoint(connector, srcConnectionPoint);
56             FreeFormPanel.SetDestinationConnectionPoint(connector, destConnectionPoint);
57             srcConnectionPoint.AttachedConnectors.Add(connector);
58             destConnectionPoint.AttachedConnectors.Add(connector);
59         }
60
61         static void SetConnectorLabel(Connector connector, ModelItem connectorModelItem)
62         {
63
64             connector.SetBinding(Connector.LabelTextProperty,  new Binding()
65             {
66                 Source = connectorModelItem,
67                 Path = new PropertyPath("DisplayName")
68             });
69
70             TextBlock toolTip = new TextBlock();
71             toolTip.SetBinding(TextBlock.TextProperty, new Binding()
72             {
73                 Source = connectorModelItem,
74                 Path = new PropertyPath("DisplayName"),
75                 StringFormat = TransitionNameToolTip + Environment.NewLine + SR.EditTransitionTooltip + Environment.NewLine + SR.CopyTransitionToolTip
76             });
77
78             connector.SetLabelToolTip(toolTip);
79         }
80
81         static void SetConnectorStartDotToolTip(FrameworkElement startDot, ModelItem connectorModelItem)
82         {
83             ModelItem triggerModelItem = connectorModelItem.Properties[TransitionDesigner.TriggerPropertyName].Value as ModelItem;
84             string triggerName = null;
85             if (triggerModelItem == null)
86             {
87                 triggerName = "(null)";
88             }
89             else
90             {
91                 ModelItem displayNameModelItem = triggerModelItem.Properties["DisplayName"].Value;
92                 if (displayNameModelItem != null)
93                 {
94                     triggerName = displayNameModelItem.GetCurrentValue() as string;
95                 }
96             }
97             startDot.ToolTip = string.Format(CultureInfo.InvariantCulture, TriggerNameToolTip, triggerName) + Environment.NewLine + SR.SharedTriggerToolTip;
98         }
99
100
101         // Returns true if visual is on the visual tree for point p relative to the reference.
102         static bool IsVisualHit(UIElement visual, UIElement reference, Point point)
103         {
104             bool visualIsHit = false;
105             HitTestResult result = VisualTreeHelper.HitTest(reference, point);
106             if (result != null)
107             {
108                 DependencyObject obj = result.VisualHit;
109                 while (obj != null)
110                 {
111                     if (visual.Equals(obj))
112                     {
113                         visualIsHit = true;
114                         break;
115                     }
116                     obj = VisualTreeHelper.GetParent(obj);
117                 }
118             }
119             return visualIsHit;
120         }
121
122         //This snaps the center of the element to grid.
123         //Wherever shapeAnchorPoint is valid, it is made co-incident with the drop location.
124         static Point SnapVisualToGrid(UIElement element, Point location, Point shapeAnchorPoint, bool isAnchorPointValid)
125         {
126             Fx.Assert(element != null, "Input UIElement is null");
127             element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
128             Point oldCenter = location;
129             if (!isAnchorPointValid)
130             {
131                 //shapeAnchorPoint is invalid in case where it does not make sense (eg. toolbox drop).
132                 location.X -= InitialNodeWidth / 2;
133                 location.Y -= InitialNodeHeight / 2;
134             }
135             else
136             {
137                 location.X -= shapeAnchorPoint.X;
138                 location.Y -= shapeAnchorPoint.Y;
139                 oldCenter = new Point(location.X + element.DesiredSize.Width / 2, location.Y + element.DesiredSize.Height / 2);
140             }
141
142             Point newCenter = SnapPointToGrid(oldCenter);
143
144             location.Offset(newCenter.X - oldCenter.X, newCenter.Y - oldCenter.Y);
145
146             if (location.X < 0)
147             {
148                 double correction = FreeFormPanel.GridSize - ((location.X * (-1)) % FreeFormPanel.GridSize);
149                 location.X = (correction == FreeFormPanel.GridSize) ? 0 : correction;
150             }
151             if (location.Y < 0)
152             {
153                 double correction = FreeFormPanel.GridSize - ((location.Y * (-1)) % FreeFormPanel.GridSize);
154                 location.Y = (correction == FreeFormPanel.GridSize) ? 0 : correction;
155             }
156             return location;
157         }
158
159         static Point SnapPointToGrid(Point pt)
160         {
161             pt.X -= pt.X % FreeFormPanel.GridSize;
162             pt.Y -= pt.Y % FreeFormPanel.GridSize;
163             pt.X = pt.X < 0 ? 0 : pt.X;
164             pt.Y = pt.Y < 0 ? 0 : pt.Y;
165             return pt;
166         }
167
168         static IEnumerable<Adorner> RemoveAdorner(UIElement adornedElement, Type adornerType)
169         {
170             Fx.Assert(adornedElement != null, "Invalid argument");
171             Fx.Assert(typeof(Adorner).IsAssignableFrom(adornerType), "Invalid argument");
172             List<Adorner> adornersRemoved = new List<Adorner>();
173             AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(adornedElement);
174             if (adornerLayer != null)
175             {
176                 Adorner[] adorners = adornerLayer.GetAdorners(adornedElement);
177                 if (adorners != null)
178                 {
179                     foreach (Adorner adorner in adorners)
180                     {
181                         if (adornerType.IsAssignableFrom(adorner.GetType()))
182                         {
183                             adornerLayer.Remove(adorner);
184                             adornersRemoved.Add(adorner);
185                         }
186                     }
187                 }
188             }
189             return adornersRemoved;
190         }
191
192         internal static List<Connector> GetAttachedConnectors(UIElement shape)
193         {
194             HashSet<Connector> attachedConnectors = new HashSet<Connector>();
195             List<ConnectionPoint> allConnectionPoints = GetConnectionPoints(shape);
196             if (allConnectionPoints != null)
197             {
198                 foreach (ConnectionPoint connPoint in allConnectionPoints)
199                 {
200                     if (connPoint != null)
201                     {
202                         foreach (Connector connector in connPoint.AttachedConnectors)
203                         {
204                             attachedConnectors.Add(connector);
205                         }
206                     }
207                 }
208             }
209             return attachedConnectors.ToList<Connector>();
210         }
211
212         static List<Connector> GetOutgoingConnectors(UIElement shape)
213         {
214             List<Connector> outgoingConnectors = new List<Connector>();
215             List<ConnectionPoint> allConnectionPoints = GetConnectionPoints(shape);
216             foreach (ConnectionPoint connPoint in allConnectionPoints)
217             {
218                 if (connPoint != null)
219                 {
220                     outgoingConnectors.AddRange(connPoint.AttachedConnectors.Where(p => FreeFormPanel.GetSourceConnectionPoint(p).Equals(connPoint)));
221                 }
222             }
223             return outgoingConnectors;
224         }
225
226         static List<Connector> GetIncomingConnectors(UIElement shape)
227         {
228             List<Connector> incomingConnectors = new List<Connector>();
229             List<ConnectionPoint> allConnectionPoints = GetConnectionPoints(shape);
230             foreach (ConnectionPoint connPoint in allConnectionPoints)
231             {
232                 if (connPoint != null)
233                 {
234                     incomingConnectors.AddRange(connPoint.AttachedConnectors.Where(p => FreeFormPanel.GetDestinationConnectionPoint(p).Equals(connPoint)));
235                 }
236             }
237             return incomingConnectors;
238         }
239
240         static ConnectionPoint ConnectionPointHitTest(UIElement element, Point hitPoint)
241         {
242             FreeFormPanel panel = VisualTreeUtils.FindVisualAncestor<FreeFormPanel>(element);
243             List<ConnectionPoint> connectionPoints = StateContainerEditor.GetConnectionPoints(element);
244             return FreeFormPanel.ConnectionPointHitTest(hitPoint, connectionPoints, panel);
245         }
246
247         static ConnectionPoint GetConnectionPoint(UIElement element, Point location)
248         {
249             List<ConnectionPoint> connectionPoints = StateContainerEditor.GetConnectionPoints(element);
250             foreach (ConnectionPoint connectionPoint in connectionPoints)
251             {
252                 if (DesignerGeometryHelper.ManhattanDistanceBetweenPoints(location, connectionPoint.Location) <= ConnectorRouter.EndPointTolerance)
253                 {
254                     return connectionPoint;
255                 }
256             }
257             return null;
258         }
259
260         internal static ModelItem GetStateMachineModelItem(ModelItem modelItem)
261         {
262             ModelItem currentModelItem = modelItem;
263             while (currentModelItem != null && currentModelItem.ItemType != typeof(StateMachine))
264             {
265                 currentModelItem = currentModelItem.Parent;
266             }
267             return currentModelItem;
268         }
269
270         static bool AreInSameStateMachine(ModelItem modelItem1, ModelItem modelItem2)
271         {
272             return GetStateMachineModelItem(modelItem1) == GetStateMachineModelItem(modelItem2);
273         }
274
275         internal static ModelItem GetParentStateModelItemForTransition(ModelItem transitionModelItem)
276         {
277             ModelItem parent = transitionModelItem;
278             while (parent != null && parent.ItemType != typeof(State))
279             {
280                 parent = parent.Parent;
281             }
282             return parent;
283         }
284
285         internal static UIElement GetStateView(ModelItem stateModelItem)
286         {
287             ModelItem parent = GetStateMachineModelItem(stateModelItem);
288             if (parent.View is StateMachineDesigner)
289             {
290                 return ((StateMachineDesigner)parent.View).StateContainerEditor.modelItemToUIElement[stateModelItem];
291             }
292             return null;
293         }
294
295         static ModelItem GetModelItemFromView(UIElement element)
296         {
297             ModelItem modelItem = null;
298             if (element is StartSymbol)
299             {
300                 modelItem = ((StartSymbol)element).ModelItem;
301             }
302             else
303             {
304                 modelItem = ((VirtualizedContainerService.VirtualizingContainer)element).ModelItem;
305             }
306             return modelItem;
307         }
308
309         static internal ConnectionPoint GetClosestConnectionPoint(ConnectionPoint srcConnPoint, List<ConnectionPoint> destConnPoints, out double minDist)
310         {
311             minDist = double.PositiveInfinity;
312             double dist = 0;
313             ConnectionPoint closestPoint = null;
314             Point srcPoint = FreeFormPanel.GetLocationRelativeToOutmostPanel(srcConnPoint);
315             foreach (ConnectionPoint destConnPoint in destConnPoints)
316             {
317                 if (srcConnPoint != destConnPoint)
318                 {
319                     dist = DesignerGeometryHelper.ManhattanDistanceBetweenPoints(srcPoint, FreeFormPanel.GetLocationRelativeToOutmostPanel(destConnPoint));
320                     if (dist < minDist)
321                     {
322                         minDist = dist;
323                         closestPoint = destConnPoint;
324                     }
325                 }
326             }
327
328             return closestPoint;
329         }
330
331         static ConnectionPoint GetClosestConnectionPointNotOfType(ConnectionPoint srcConnectionPoint, List<ConnectionPoint> targetConnectionPoints, ConnectionPointKind illegalConnectionPointKind)
332         {
333             double minDist;
334             List<ConnectionPoint> filteredConnectionPoints = new List<ConnectionPoint>();
335             foreach (ConnectionPoint connPoint in targetConnectionPoints)
336             {
337                 if (connPoint.PointType != illegalConnectionPointKind && !connPoint.Equals(srcConnectionPoint) && connPoint.AttachedConnectors.Count == 0)
338                 {
339                     filteredConnectionPoints.Add(connPoint);
340                 }
341             }
342             return GetClosestConnectionPoint(srcConnectionPoint, filteredConnectionPoints, out minDist);
343         }
344
345         static void GetClosestConnectionPointPair(List<ConnectionPoint> srcConnPoints, List<ConnectionPoint> destConnPoints, out ConnectionPoint srcConnPoint, out ConnectionPoint destConnPoint)
346         {
347             double minDist = double.PositiveInfinity;
348             double dist;
349             ConnectionPoint tempConnPoint;
350             srcConnPoint = null;
351             destConnPoint = null;
352             foreach (ConnectionPoint connPoint in srcConnPoints)
353             {
354                 tempConnPoint = GetClosestConnectionPoint(connPoint, destConnPoints, out dist);
355                 if (dist < minDist)
356                 {
357                     minDist = dist;
358                     srcConnPoint = connPoint;
359                     destConnPoint = tempConnPoint;
360                 }
361             }
362             Fx.Assert(srcConnPoint != null, "No ConnectionPoint found");
363             Fx.Assert(destConnPoint != null, "No ConnectionPoint found");
364         }
365
366         static void GetEmptySrcDestConnectionPoints(UIElement source, UIElement dest, out ConnectionPoint srcConnPoint, out ConnectionPoint destConnPoint)
367         {
368             srcConnPoint = null;
369             destConnPoint = null;
370             List<ConnectionPoint> srcConnectionPoints = GetEmptyConnectionPoints(source);
371             List<ConnectionPoint> destConnectionPoints = GetEmptyConnectionPoints(dest);
372             if (srcConnectionPoints.Count > 0 && destConnectionPoints.Count > 0)
373             {
374                 GetClosestConnectionPointPair(srcConnectionPoints, destConnectionPoints, out srcConnPoint, out destConnPoint);
375             }
376         }
377
378         internal static List<ConnectionPoint> GetEmptyConnectionPoints(UIElement designer)
379         {
380             List<ConnectionPoint> connectionPoints = StateContainerEditor.GetConnectionPoints(designer);
381             if (connectionPoints != null)
382             {
383                 return new List<ConnectionPoint>(connectionPoints.Where<ConnectionPoint>(
384                     (p) => { return p.AttachedConnectors == null || p.AttachedConnectors.Count == 0; }));
385             }
386             return new List<ConnectionPoint>();
387         }
388
389         //This returns the closest non-incoming connectionPoint on source. Return value will be different than destConnectionPoint.
390         static ConnectionPoint GetClosestSrcConnectionPoint(UIElement src, ConnectionPoint destConnectionPoint)
391         {
392             ConnectionPoint srcConnectionPoint = null;
393             if (destConnectionPoint.PointType != ConnectionPointKind.Outgoing)
394             {
395                 srcConnectionPoint = GetClosestConnectionPointNotOfType(destConnectionPoint, StateContainerEditor.GetConnectionPoints(src), ConnectionPointKind.Incoming);
396             }
397             return srcConnectionPoint;
398         }
399
400         //This returns the closest non-outgoing connectionPoint on dest. Return value will be different than sourceConnectionPoint.
401         static ConnectionPoint GetClosestDestConnectionPoint(ConnectionPoint sourceConnectionPoint, UIElement dest)
402         {
403             ConnectionPoint destConnectionPoint = null;
404             if (sourceConnectionPoint.PointType != ConnectionPointKind.Incoming)
405             {
406                 destConnectionPoint = GetClosestConnectionPointNotOfType(sourceConnectionPoint, StateContainerEditor.GetConnectionPoints(dest), ConnectionPointKind.Outgoing);
407             }
408             return destConnectionPoint;
409         }
410
411         static ConnectionPoint GetSrcConnectionPointForSharedTrigger(UIElement sourceDesigner, ModelItem connectorModelItem)
412         {
413             ConnectionPoint sourceConnectionPoint = null;
414             List<Connector> connectors = StateContainerEditor.GetOutgoingConnectors(sourceDesigner);
415             foreach (Connector connector in connectors)
416             {
417                 ModelItem modelItem = StateContainerEditor.GetConnectorModelItem(connector);
418                 if (modelItem != null && modelItem.ItemType == typeof(Transition))
419                 {
420                     if (modelItem.Properties[TransitionDesigner.TriggerPropertyName].Value == connectorModelItem.Properties[TransitionDesigner.TriggerPropertyName].Value)
421                     {
422                         sourceConnectionPoint = FreeFormPanel.GetSourceConnectionPoint(connector);
423                     }
424                 }
425             }
426             return sourceConnectionPoint;
427         }
428
429         // Test if the transition is contained by any of the states or their descendants
430         static bool IsTransitionModelItemContainedByStateModelItems(ModelItem transitionModelItem, IEnumerable<ModelItem> stateModelItems)
431         {
432             foreach (ModelItem stateModelItem in stateModelItems)
433             {
434                 if (stateModelItem.Properties[StateDesigner.TransitionsPropertyName].Collection.Contains(transitionModelItem))
435                 {
436                     return true;
437                 }
438             }
439             return false;
440         }
441
442         static bool IsTransitionDestinationWithinStates(Transition transition, IEnumerable<State> states)
443         {
444             foreach (State state in states)
445             {
446                 if (transition.To == state)
447                 {
448                     return true;
449                 }
450             }
451             return false;
452         }
453
454         // Remove dangling transitions that are not pointing to any of the input states or their descendants
455         static void RemoveDanglingTransitions(IEnumerable<State> states)
456         {
457             Queue<State> statesToProcess = new Queue<State>(states);
458             while (statesToProcess.Count > 0)
459             {
460                 State state = statesToProcess.Dequeue();
461
462                 IEnumerable<Transition> toRemove = state.Transitions.Where<Transition>((p) =>
463                     { return !IsTransitionDestinationWithinStates(p, states); }).Reverse();
464                 foreach (Transition transition in toRemove)
465                 {
466                     state.Transitions.Remove(transition);
467                 }
468
469             }
470         }
471
472         static List<ModelItem> GetTransitionModelItems(IEnumerable<ModelItem> stateModelItems)
473         {
474             List<ModelItem> transitionModelItems = new List<ModelItem>();
475             foreach (ModelItem stateModelItem in stateModelItems)
476             {
477                 transitionModelItems.AddRange(stateModelItem.Properties[StateDesigner.TransitionsPropertyName].Collection);
478                 //transitionModelItems.AddRange(GetTransitionModelItems(stateModelItem.Properties[ChildStatesPropertyName].Collection));
479             }
480             return transitionModelItems;
481         }
482
483         internal static bool IsFinalState(ModelItem modelItem)
484         {
485             return modelItem.ItemType == typeof(State) && (bool)modelItem.Properties[StateDesigner.IsFinalPropertyName].Value.GetCurrentValue();
486         }
487
488         static void ShowMessageBox(string message)
489         {
490             MessageBox.Show(message, SR.ErrorMessageBoxTitle, MessageBoxButton.OK, MessageBoxImage.Error);
491         }
492
493         static void ReportConnectorCreationError(ConnectorCreationResult result)
494         {
495             switch (result)
496             {
497                 case ConnectorCreationResult.CannotCreateTransitionToCompositeState:
498                     ShowMessageBox(SR.CannotCreateTransitionToCompositeState);
499                     break;
500                 case ConnectorCreationResult.CannotCreateTransitionFromAncestorToDescendant:
501                     ShowMessageBox(SR.CannotCreateTransitionFromAncestorToDescendant);
502                     break;
503                 case ConnectorCreationResult.CannotSetCompositeStateAsInitialState:
504                     ShowMessageBox(SR.CannotSetCompositeStateAsInitialState);
505                     break;
506                 case ConnectorCreationResult.CannotSetFinalStateAsInitialState:
507                     ShowMessageBox(SR.CannotSetFinalStateAsInitialState);
508                     break;
509                 case ConnectorCreationResult.OtherFailure:
510                     ShowMessageBox(SR.CannotCreateLink);
511                     break;
512             }
513         }
514
515         static bool IsConnectorFromInitialNode(Connector connector)
516         {
517             return GetConnectorModelItem(connector).ItemType == typeof(StateMachine);
518         }
519
520         internal static string GenerateTransitionName(ModelItem stateMachineModelItem)
521         {
522             Fx.Assert(stateMachineModelItem.ItemType == typeof(StateMachine), "ModelItem param should be a statemachine.");
523             HashSet<String> existingTransitionNames = new HashSet<string>();
524
525             foreach (ModelItem stateModelItem in stateMachineModelItem.Properties[StateMachineDesigner.StatesPropertyName].Collection)
526             {
527                 foreach (ModelItem transitionModelItem in stateModelItem.Properties[StateDesigner.TransitionsPropertyName].Collection)
528                 {
529                     existingTransitionNames.Add(((Transition)transitionModelItem.GetCurrentValue()).DisplayName);
530                 }
531             }
532
533             int suffix = 0;
534             string name;
535
536             do
537             {
538                 name = string.Format(CultureInfo.InvariantCulture, "T{0}", ++suffix);
539             } while (existingTransitionNames.Contains<string>(name));
540
541             return name;
542         }
543     }
544 }