1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System.Activities.Core.Presentation
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;
18 using System.Windows.Controls;
19 using System.Windows.Data;
20 using System.Windows.Documents;
21 using System.Windows.Media;
23 partial class StateContainerEditor
25 internal static ModelItem GetConnectorModelItem(DependencyObject obj)
27 return (ModelItem)obj.GetValue(StateContainerEditor.ConnectorModelItemProperty);
30 static void SetConnectorModelItem(DependencyObject obj, ModelItem modelItem)
32 obj.SetValue(StateContainerEditor.ConnectorModelItemProperty, modelItem);
35 internal static List<ConnectionPoint> GetConnectionPoints(DependencyObject obj)
37 if (obj is StartSymbol)
39 return (List<ConnectionPoint>)obj.GetValue(StateContainerEditor.ConnectionPointsProperty);
41 if (!(obj is VirtualizedContainerService.VirtualizingContainer))
43 obj = VisualTreeUtils.FindVisualAncestor<VirtualizedContainerService.VirtualizingContainer>(obj);
45 return (List<ConnectionPoint>)obj.GetValue(StateContainerEditor.ConnectionPointsProperty);
48 static void SetConnectionPoints(DependencyObject obj, List<ConnectionPoint> connectionPoints)
50 obj.SetValue(StateContainerEditor.ConnectionPointsProperty, connectionPoints);
53 static void SetConnectorSrcDestConnectionPoints(Connector connector, ConnectionPoint srcConnectionPoint, ConnectionPoint destConnectionPoint)
55 FreeFormPanel.SetSourceConnectionPoint(connector, srcConnectionPoint);
56 FreeFormPanel.SetDestinationConnectionPoint(connector, destConnectionPoint);
57 srcConnectionPoint.AttachedConnectors.Add(connector);
58 destConnectionPoint.AttachedConnectors.Add(connector);
61 static void SetConnectorLabel(Connector connector, ModelItem connectorModelItem)
64 connector.SetBinding(Connector.LabelTextProperty, new Binding()
66 Source = connectorModelItem,
67 Path = new PropertyPath("DisplayName")
70 TextBlock toolTip = new TextBlock();
71 toolTip.SetBinding(TextBlock.TextProperty, new Binding()
73 Source = connectorModelItem,
74 Path = new PropertyPath("DisplayName"),
75 StringFormat = TransitionNameToolTip + Environment.NewLine + SR.EditTransitionTooltip + Environment.NewLine + SR.CopyTransitionToolTip
78 connector.SetLabelToolTip(toolTip);
81 static void SetConnectorStartDotToolTip(FrameworkElement startDot, ModelItem connectorModelItem)
83 ModelItem triggerModelItem = connectorModelItem.Properties[TransitionDesigner.TriggerPropertyName].Value as ModelItem;
84 string triggerName = null;
85 if (triggerModelItem == null)
87 triggerName = "(null)";
91 ModelItem displayNameModelItem = triggerModelItem.Properties["DisplayName"].Value;
92 if (displayNameModelItem != null)
94 triggerName = displayNameModelItem.GetCurrentValue() as string;
97 startDot.ToolTip = string.Format(CultureInfo.InvariantCulture, TriggerNameToolTip, triggerName) + Environment.NewLine + SR.SharedTriggerToolTip;
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)
104 bool visualIsHit = false;
105 HitTestResult result = VisualTreeHelper.HitTest(reference, point);
108 DependencyObject obj = result.VisualHit;
111 if (visual.Equals(obj))
116 obj = VisualTreeHelper.GetParent(obj);
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)
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)
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;
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);
142 Point newCenter = SnapPointToGrid(oldCenter);
144 location.Offset(newCenter.X - oldCenter.X, newCenter.Y - oldCenter.Y);
148 double correction = FreeFormPanel.GridSize - ((location.X * (-1)) % FreeFormPanel.GridSize);
149 location.X = (correction == FreeFormPanel.GridSize) ? 0 : correction;
153 double correction = FreeFormPanel.GridSize - ((location.Y * (-1)) % FreeFormPanel.GridSize);
154 location.Y = (correction == FreeFormPanel.GridSize) ? 0 : correction;
159 static Point SnapPointToGrid(Point pt)
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;
168 static IEnumerable<Adorner> RemoveAdorner(UIElement adornedElement, Type adornerType)
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)
176 Adorner[] adorners = adornerLayer.GetAdorners(adornedElement);
177 if (adorners != null)
179 foreach (Adorner adorner in adorners)
181 if (adornerType.IsAssignableFrom(adorner.GetType()))
183 adornerLayer.Remove(adorner);
184 adornersRemoved.Add(adorner);
189 return adornersRemoved;
192 internal static List<Connector> GetAttachedConnectors(UIElement shape)
194 HashSet<Connector> attachedConnectors = new HashSet<Connector>();
195 List<ConnectionPoint> allConnectionPoints = GetConnectionPoints(shape);
196 if (allConnectionPoints != null)
198 foreach (ConnectionPoint connPoint in allConnectionPoints)
200 if (connPoint != null)
202 foreach (Connector connector in connPoint.AttachedConnectors)
204 attachedConnectors.Add(connector);
209 return attachedConnectors.ToList<Connector>();
212 static List<Connector> GetOutgoingConnectors(UIElement shape)
214 List<Connector> outgoingConnectors = new List<Connector>();
215 List<ConnectionPoint> allConnectionPoints = GetConnectionPoints(shape);
216 foreach (ConnectionPoint connPoint in allConnectionPoints)
218 if (connPoint != null)
220 outgoingConnectors.AddRange(connPoint.AttachedConnectors.Where(p => FreeFormPanel.GetSourceConnectionPoint(p).Equals(connPoint)));
223 return outgoingConnectors;
226 static List<Connector> GetIncomingConnectors(UIElement shape)
228 List<Connector> incomingConnectors = new List<Connector>();
229 List<ConnectionPoint> allConnectionPoints = GetConnectionPoints(shape);
230 foreach (ConnectionPoint connPoint in allConnectionPoints)
232 if (connPoint != null)
234 incomingConnectors.AddRange(connPoint.AttachedConnectors.Where(p => FreeFormPanel.GetDestinationConnectionPoint(p).Equals(connPoint)));
237 return incomingConnectors;
240 static ConnectionPoint ConnectionPointHitTest(UIElement element, Point hitPoint)
242 FreeFormPanel panel = VisualTreeUtils.FindVisualAncestor<FreeFormPanel>(element);
243 List<ConnectionPoint> connectionPoints = StateContainerEditor.GetConnectionPoints(element);
244 return FreeFormPanel.ConnectionPointHitTest(hitPoint, connectionPoints, panel);
247 static ConnectionPoint GetConnectionPoint(UIElement element, Point location)
249 List<ConnectionPoint> connectionPoints = StateContainerEditor.GetConnectionPoints(element);
250 foreach (ConnectionPoint connectionPoint in connectionPoints)
252 if (DesignerGeometryHelper.ManhattanDistanceBetweenPoints(location, connectionPoint.Location) <= ConnectorRouter.EndPointTolerance)
254 return connectionPoint;
260 internal static ModelItem GetStateMachineModelItem(ModelItem modelItem)
262 ModelItem currentModelItem = modelItem;
263 while (currentModelItem != null && currentModelItem.ItemType != typeof(StateMachine))
265 currentModelItem = currentModelItem.Parent;
267 return currentModelItem;
270 static bool AreInSameStateMachine(ModelItem modelItem1, ModelItem modelItem2)
272 return GetStateMachineModelItem(modelItem1) == GetStateMachineModelItem(modelItem2);
275 internal static ModelItem GetParentStateModelItemForTransition(ModelItem transitionModelItem)
277 ModelItem parent = transitionModelItem;
278 while (parent != null && parent.ItemType != typeof(State))
280 parent = parent.Parent;
285 internal static UIElement GetStateView(ModelItem stateModelItem)
287 ModelItem parent = GetStateMachineModelItem(stateModelItem);
288 if (parent.View is StateMachineDesigner)
290 return ((StateMachineDesigner)parent.View).StateContainerEditor.modelItemToUIElement[stateModelItem];
295 static ModelItem GetModelItemFromView(UIElement element)
297 ModelItem modelItem = null;
298 if (element is StartSymbol)
300 modelItem = ((StartSymbol)element).ModelItem;
304 modelItem = ((VirtualizedContainerService.VirtualizingContainer)element).ModelItem;
309 static internal ConnectionPoint GetClosestConnectionPoint(ConnectionPoint srcConnPoint, List<ConnectionPoint> destConnPoints, out double minDist)
311 minDist = double.PositiveInfinity;
313 ConnectionPoint closestPoint = null;
314 Point srcPoint = FreeFormPanel.GetLocationRelativeToOutmostPanel(srcConnPoint);
315 foreach (ConnectionPoint destConnPoint in destConnPoints)
317 if (srcConnPoint != destConnPoint)
319 dist = DesignerGeometryHelper.ManhattanDistanceBetweenPoints(srcPoint, FreeFormPanel.GetLocationRelativeToOutmostPanel(destConnPoint));
323 closestPoint = destConnPoint;
331 static ConnectionPoint GetClosestConnectionPointNotOfType(ConnectionPoint srcConnectionPoint, List<ConnectionPoint> targetConnectionPoints, ConnectionPointKind illegalConnectionPointKind)
334 List<ConnectionPoint> filteredConnectionPoints = new List<ConnectionPoint>();
335 foreach (ConnectionPoint connPoint in targetConnectionPoints)
337 if (connPoint.PointType != illegalConnectionPointKind && !connPoint.Equals(srcConnectionPoint) && connPoint.AttachedConnectors.Count == 0)
339 filteredConnectionPoints.Add(connPoint);
342 return GetClosestConnectionPoint(srcConnectionPoint, filteredConnectionPoints, out minDist);
345 static void GetClosestConnectionPointPair(List<ConnectionPoint> srcConnPoints, List<ConnectionPoint> destConnPoints, out ConnectionPoint srcConnPoint, out ConnectionPoint destConnPoint)
347 double minDist = double.PositiveInfinity;
349 ConnectionPoint tempConnPoint;
351 destConnPoint = null;
352 foreach (ConnectionPoint connPoint in srcConnPoints)
354 tempConnPoint = GetClosestConnectionPoint(connPoint, destConnPoints, out dist);
358 srcConnPoint = connPoint;
359 destConnPoint = tempConnPoint;
362 Fx.Assert(srcConnPoint != null, "No ConnectionPoint found");
363 Fx.Assert(destConnPoint != null, "No ConnectionPoint found");
366 static void GetEmptySrcDestConnectionPoints(UIElement source, UIElement dest, out ConnectionPoint srcConnPoint, out ConnectionPoint destConnPoint)
369 destConnPoint = null;
370 List<ConnectionPoint> srcConnectionPoints = GetEmptyConnectionPoints(source);
371 List<ConnectionPoint> destConnectionPoints = GetEmptyConnectionPoints(dest);
372 if (srcConnectionPoints.Count > 0 && destConnectionPoints.Count > 0)
374 GetClosestConnectionPointPair(srcConnectionPoints, destConnectionPoints, out srcConnPoint, out destConnPoint);
378 internal static List<ConnectionPoint> GetEmptyConnectionPoints(UIElement designer)
380 List<ConnectionPoint> connectionPoints = StateContainerEditor.GetConnectionPoints(designer);
381 if (connectionPoints != null)
383 return new List<ConnectionPoint>(connectionPoints.Where<ConnectionPoint>(
384 (p) => { return p.AttachedConnectors == null || p.AttachedConnectors.Count == 0; }));
386 return new List<ConnectionPoint>();
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)
392 ConnectionPoint srcConnectionPoint = null;
393 if (destConnectionPoint.PointType != ConnectionPointKind.Outgoing)
395 srcConnectionPoint = GetClosestConnectionPointNotOfType(destConnectionPoint, StateContainerEditor.GetConnectionPoints(src), ConnectionPointKind.Incoming);
397 return srcConnectionPoint;
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)
403 ConnectionPoint destConnectionPoint = null;
404 if (sourceConnectionPoint.PointType != ConnectionPointKind.Incoming)
406 destConnectionPoint = GetClosestConnectionPointNotOfType(sourceConnectionPoint, StateContainerEditor.GetConnectionPoints(dest), ConnectionPointKind.Outgoing);
408 return destConnectionPoint;
411 static ConnectionPoint GetSrcConnectionPointForSharedTrigger(UIElement sourceDesigner, ModelItem connectorModelItem)
413 ConnectionPoint sourceConnectionPoint = null;
414 List<Connector> connectors = StateContainerEditor.GetOutgoingConnectors(sourceDesigner);
415 foreach (Connector connector in connectors)
417 ModelItem modelItem = StateContainerEditor.GetConnectorModelItem(connector);
418 if (modelItem != null && modelItem.ItemType == typeof(Transition))
420 if (modelItem.Properties[TransitionDesigner.TriggerPropertyName].Value == connectorModelItem.Properties[TransitionDesigner.TriggerPropertyName].Value)
422 sourceConnectionPoint = FreeFormPanel.GetSourceConnectionPoint(connector);
426 return sourceConnectionPoint;
429 // Test if the transition is contained by any of the states or their descendants
430 static bool IsTransitionModelItemContainedByStateModelItems(ModelItem transitionModelItem, IEnumerable<ModelItem> stateModelItems)
432 foreach (ModelItem stateModelItem in stateModelItems)
434 if (stateModelItem.Properties[StateDesigner.TransitionsPropertyName].Collection.Contains(transitionModelItem))
442 static bool IsTransitionDestinationWithinStates(Transition transition, IEnumerable<State> states)
444 foreach (State state in states)
446 if (transition.To == state)
454 // Remove dangling transitions that are not pointing to any of the input states or their descendants
455 static void RemoveDanglingTransitions(IEnumerable<State> states)
457 Queue<State> statesToProcess = new Queue<State>(states);
458 while (statesToProcess.Count > 0)
460 State state = statesToProcess.Dequeue();
462 IEnumerable<Transition> toRemove = state.Transitions.Where<Transition>((p) =>
463 { return !IsTransitionDestinationWithinStates(p, states); }).Reverse();
464 foreach (Transition transition in toRemove)
466 state.Transitions.Remove(transition);
472 static List<ModelItem> GetTransitionModelItems(IEnumerable<ModelItem> stateModelItems)
474 List<ModelItem> transitionModelItems = new List<ModelItem>();
475 foreach (ModelItem stateModelItem in stateModelItems)
477 transitionModelItems.AddRange(stateModelItem.Properties[StateDesigner.TransitionsPropertyName].Collection);
478 //transitionModelItems.AddRange(GetTransitionModelItems(stateModelItem.Properties[ChildStatesPropertyName].Collection));
480 return transitionModelItems;
483 internal static bool IsFinalState(ModelItem modelItem)
485 return modelItem.ItemType == typeof(State) && (bool)modelItem.Properties[StateDesigner.IsFinalPropertyName].Value.GetCurrentValue();
488 static void ShowMessageBox(string message)
490 MessageBox.Show(message, SR.ErrorMessageBoxTitle, MessageBoxButton.OK, MessageBoxImage.Error);
493 static void ReportConnectorCreationError(ConnectorCreationResult result)
497 case ConnectorCreationResult.CannotCreateTransitionToCompositeState:
498 ShowMessageBox(SR.CannotCreateTransitionToCompositeState);
500 case ConnectorCreationResult.CannotCreateTransitionFromAncestorToDescendant:
501 ShowMessageBox(SR.CannotCreateTransitionFromAncestorToDescendant);
503 case ConnectorCreationResult.CannotSetCompositeStateAsInitialState:
504 ShowMessageBox(SR.CannotSetCompositeStateAsInitialState);
506 case ConnectorCreationResult.CannotSetFinalStateAsInitialState:
507 ShowMessageBox(SR.CannotSetFinalStateAsInitialState);
509 case ConnectorCreationResult.OtherFailure:
510 ShowMessageBox(SR.CannotCreateLink);
515 static bool IsConnectorFromInitialNode(Connector connector)
517 return GetConnectorModelItem(connector).ItemType == typeof(StateMachine);
520 internal static string GenerateTransitionName(ModelItem stateMachineModelItem)
522 Fx.Assert(stateMachineModelItem.ItemType == typeof(StateMachine), "ModelItem param should be a statemachine.");
523 HashSet<String> existingTransitionNames = new HashSet<string>();
525 foreach (ModelItem stateModelItem in stateMachineModelItem.Properties[StateMachineDesigner.StatesPropertyName].Collection)
527 foreach (ModelItem transitionModelItem in stateModelItem.Properties[StateDesigner.TransitionsPropertyName].Collection)
529 existingTransitionNames.Add(((Transition)transitionModelItem.GetCurrentValue()).DisplayName);
538 name = string.Format(CultureInfo.InvariantCulture, "T{0}", ++suffix);
539 } while (existingTransitionNames.Contains<string>(name));