Mono exception to SocketException
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Core.Presentation / System / Activities / Core / Presentation / FlowchartDesigner.xaml.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4
5 namespace System.Activities.Core.Presentation
6 {
7     using System.Activities;
8     using System.Activities.Presentation;
9     using System.Activities.Presentation.FreeFormEditing;
10     using System.Activities.Presentation.Hosting;
11     using System.Activities.Presentation.Internal.PropertyEditing;
12     using System.Activities.Presentation.Metadata;
13     using System.Activities.Presentation.Model;
14     using System.Activities.Presentation.View;
15     using System.Activities.Presentation.View.OutlineView;
16     using System.Activities.Presentation.ViewState;
17     using System.Activities.Statements;
18     using System.Collections.Generic;
19     using System.Collections.ObjectModel;
20     using System.ComponentModel;
21     using System.Linq;
22     using System.Reflection;
23     using System.Runtime;
24     using System.Windows;
25     using System.Windows.Controls;
26     using System.Windows.Data;
27     using System.Windows.Documents;
28     using System.Windows.Input;
29     using System.Windows.Media;
30     using System.Windows.Threading;
31
32     [ActivityDesignerOptions(AlwaysCollapseChildren = true)]
33     partial class FlowchartDesigner : IAutoConnectContainer, IAutoSplitContainer
34     {
35         public static readonly DependencyProperty ConnectionPointsProperty = DependencyProperty.RegisterAttached("ConnectionPoints", typeof(List<ConnectionPoint>), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
36         public static readonly DependencyProperty LinkModelItemProperty = DependencyProperty.RegisterAttached("LinkModelItem", typeof(ModelItem), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
37         public static readonly DependencyProperty FlowElementModelItemProperty = DependencyProperty.RegisterAttached("FlowElementModelItem", typeof(ModelItem), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
38
39         public static readonly DependencyProperty FlowchartWidthProperty = DependencyProperty.Register("FlowchartWidth", typeof(double), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
40         public static readonly DependencyProperty FlowchartHeightProperty = DependencyProperty.Register("FlowchartHeight", typeof(double), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
41
42         public static readonly DependencyProperty TrueConnectionPointProperty = DependencyProperty.RegisterAttached("TrueConnectionPoint", typeof(ConnectionPoint), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
43         public static readonly DependencyProperty FalseConnectionPointProperty = DependencyProperty.RegisterAttached("FalseConnectionPoint", typeof(ConnectionPoint), typeof(FlowchartDesigner), new FrameworkPropertyMetadata());
44
45         public static readonly DependencyProperty ShowAllConditionsProperty = DependencyProperty.Register("ShowAllConditions", typeof(bool), typeof(FlowchartDesigner));
46
47         public static readonly RoutedCommand SetAsStartNodeCommand = new RoutedCommand("SetAsStartNode", typeof(FlowchartDesigner));
48         //public static readonly RoutedCommand ConnectNodesCommand = new RoutedCommand("ConnectNodes", typeof(FlowchartDesigner));
49         public static readonly RoutedCommand ShowAllConditionsCommand = new RoutedCommand("ShowAllConditionsCommand", typeof(FlowchartDesigner));
50         public static readonly RoutedCommand HideAllConditionsCommand = new RoutedCommand("HideAllConditionsCommand", typeof(FlowchartDesigner));
51
52         const double flowElementCaptionFontSize = 11;
53         const double DebugTimeMaxConnectorShapeDist = 10;
54         static readonly FontFamily flowElementCaptionFontFamily = new FontFamily("Tohoma");
55         static readonly FontStyle flowElementCaptionFontStyle = new FontStyle();
56         static readonly Typeface flowElementCaptionTypeface = new Typeface("Tohoma");
57         
58         internal Dictionary<ModelItem, UIElement> modelElement;
59         //Consider FlowStep.Action = SomeActivity. FlowStep modelItem is referred as FlowNodeMI, SomeActivity modelItem is shapeMI and the designer for SomeActivity is the shape on canvas.
60         //To go from the FlowNodeMI to the shape on canvas, we can use the path: FlowNodeMI(FlowStep.Action)-> shapeMI (modelElement Dictionary)-> Actual UIElement shape
61         //However this path does not always work.  For instance in delete case: FlowStep.Action is set to null to update the ModelItem.Parents property on the shapeMI
62         //flowNodeToUIElement dictionary is used to solve this problem.
63         Dictionary<ModelItem, UIElement> flowNodeToUIElement;
64
65         const double startSymbolTopMargin = 10.0;
66         const string shapeLocation = "ShapeLocation";
67         const string shapeSize = "ShapeSize";
68         const string TrueConnectorViewStateKey = "TrueConnector";
69         const string FalseConnectorViewStateKey = "FalseConnector";
70         const string CaseViewStateKeyAppendString = "Connector";
71         const string FlowSwitchDefaultViewStateKey = "Default";
72         const string ConnectorViewStateKey = "ConnectorLocation";
73         static Color ConnectionPointColor = Colors.LightGray;
74         UIElement lastConnectionPointMouseUpElement = null;
75         //shapeLocations is useful to avoid pasting on existing shapes.
76         //This is populated in 2 cases 1. When the shape with existing Viewstate is added 2. On ViewState changed.
77         HashSet<Point> shapeLocations = null;
78         //selectedConnector is a placeholder for the last connector selected.
79         //This removes the need for a dictionary mapping modelitem to connector for deletion.
80         //This will change if in future we plan to support multi-select + delete.
81         Connector selectedConnector;
82         //srcConnectionPoint is required for link addition gesture to store the source of the link.
83         ConnectionPoint srcConnectionPoint;
84         ConnectionPoint srcConnectionPointForAutoConnect;
85         ConnectionPoint srcConnectionPointForAutoSplit;
86         ConnectionPoint destConnectionPointForAutoSplit;
87         EdgeLocation entryEdgeForAutoSplit;
88         EdgeLocation exitEdgeForAutoSplit;
89         bool internalViewStateChange = false;
90         bool startNodeAdded = false;
91         bool updatingSelectedConnector;
92         internal FreeFormPanel panel = null;
93         AdornerLayer adornerLayer;
94         MenuItem setAsStartNode;
95         bool? isRightToLeft;
96         private bool isLoaded = false;
97
98         internal bool IsResizing { get; set; }
99
100         public FlowchartDesigner()
101         {
102             InitializeComponent();
103             this.modelElement = new Dictionary<ModelItem, UIElement>();
104             this.flowNodeToUIElement = new Dictionary<ModelItem, UIElement>();
105             this.shapeLocations = new HashSet<Point>();
106             this.selectedConnector = null;
107             ConstructSetAsStartNodeMenuItem();
108
109             this.Loaded += (s, e) =>
110             {
111                 this.isLoaded = true;
112
113                 if (this.ShowExpanded)
114                 {
115                     ((ICompositeViewEvents)this).RegisterDefaultCompositeView(this);
116                 }
117                 DesignerView designerView = this.Context.Services.GetService<DesignerView>() as DesignerView;
118                 if (!designerView.ContextMenu.Items.Contains(setAsStartNode))
119                 {
120                     designerView.ContextMenu.Items.Add(setAsStartNode);
121                 }
122
123                 WorkflowCommandExtensionItem item = this.Context.Items.GetValue<WorkflowCommandExtensionItem>();
124                 if (item != null)
125                 {
126                     if (item.CommandExtensionCallback is DefaultCommandExtensionCallback)
127                     {
128                         this.InputBindings.Add(new KeyBinding(FlowchartDesignerCommands.ConnectNodesCommand, new DefaultCommandExtensionCallback.ChordKeyGesture(Key.E, Key.F)));
129                     }
130                 }
131
132                 Selection.Subscribe(Context, OnSelectionChanged);
133             };
134
135             this.Unloaded += (s, e) =>
136             {
137                 this.isLoaded = false;
138
139                 if (object.Equals(this.DefaultCompositeView, this))
140                 {
141                     ((ICompositeViewEvents)this).UnregisterDefaultCompositeView(this);
142                 }
143                 DesignerView designerView = this.Context.Services.GetService<DesignerView>() as DesignerView;
144                 designerView.ContextMenu.Items.Remove(setAsStartNode);
145
146                 Selection.Unsubscribe(Context, OnSelectionChanged);
147             };
148         }
149
150         public static double FlowNodeCaptionFontSize
151         {
152             get { return flowElementCaptionFontSize; }
153         }
154
155         public static FontFamily FlowNodeCaptionFontFamily
156         {
157             get { return flowElementCaptionFontFamily; }
158         }
159
160         public static FontStyle FlowNodeCaptionFontStyle
161         {
162             get { return flowElementCaptionFontStyle; }
163         }
164
165         public static Typeface FlowElementCaptionTypeface
166         {
167             get { return flowElementCaptionTypeface; }
168         }
169
170         void OnSetAsStartNodeCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
171         {
172             //The condition is necessary so that the child flowchart inside a flowchart doesn't try to handle the event.
173             if (!object.Equals(e.Source, this))
174             {
175                 e.CanExecute = !this.IsReadOnly;
176                 e.Handled = true;
177             }
178         }
179
180         void OnShowAllConditionsMenuLoaded(object sender, RoutedEventArgs e)
181         {
182             MenuItem item = sender as MenuItem;
183             if (null != item)
184             {
185                 bool expanded = (bool)GetValue(WorkflowViewElement.ShowExpandedProperty);
186                 if (expanded)
187                 {
188                     item.Visibility = Visibility.Visible;
189                 }
190                 else
191                 {
192                     item.Visibility = Visibility.Collapsed;
193                 }
194             }
195             e.Handled = true;
196         }
197
198         void OnHideAllConditionsMenuLoaded(object sender, RoutedEventArgs e)
199         {
200             MenuItem item = sender as MenuItem;
201             if (null != item)
202             {
203                 bool expanded = (bool)GetValue(WorkflowViewElement.ShowExpandedProperty);
204                 if (expanded)
205                 {
206                     item.Visibility = Visibility.Visible;
207                 }
208                 else
209                 {
210                     item.Visibility = Visibility.Collapsed;
211                 }
212             }
213             e.Handled = true;
214         }
215
216         void OnSetAsStartNodeCommandExecuted(object sender, ExecutedRoutedEventArgs e)
217         {
218             ModelItem selection = this.Context.Items.GetValue<Selection>().PrimarySelection;
219             Fx.Assert(this.modelElement.ContainsKey(selection), "Selection is not contained in this container");
220             this.ModelItem.Properties["StartNode"].SetValue(this.GetFlowElementMI(selection));
221             e.Handled = true;
222         }
223
224         void ConstructSetAsStartNodeMenuItem()
225         {
226             setAsStartNode = new MenuItem();
227             setAsStartNode.Command = FlowchartDesigner.SetAsStartNodeCommand;
228             setAsStartNode.Header = this.SetAsStartNodeMenuItemHeader;
229             setAsStartNode.Visibility = Visibility.Collapsed;
230             setAsStartNode.Loaded += new RoutedEventHandler(OnSetAsStartNodeLoaded);
231             //AutomationProperties
232             setAsStartNode.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "SetAsStartNodeMenuItem");
233         }
234
235         string SetAsStartNodeMenuItemHeader
236         {
237             get { return (string)this.FindResource("SetAsStartNodeMenuItemHeader"); }
238         }
239
240         void OnSetAsStartNodeLoaded(object sender, RoutedEventArgs e)
241         {
242             MenuItem setAsStartNodeMenuItem = sender as MenuItem;
243             setAsStartNodeMenuItem.Visibility = Visibility.Collapsed;
244             Selection selection = this.Context.Items.GetValue<Selection>();
245             if (selection.SelectionCount == 1 && this.modelElement.ContainsKey(selection.PrimarySelection))
246             {
247                 setAsStartNodeMenuItem.Visibility = Visibility.Visible;
248             }
249             e.Handled = true;
250         }
251
252         public static void RegisterMetadata(AttributeTableBuilder builder)
253         {
254             Type type = typeof(Flowchart);
255             builder.AddCustomAttributes(type, new DesignerAttribute(typeof(FlowchartDesigner)));
256             builder.AddCustomAttributes(type, type.GetProperty("StartNode"), BrowsableAttribute.No);
257             builder.AddCustomAttributes(type, type.GetProperty("Nodes"), BrowsableAttribute.No);
258             builder.AddCustomAttributes(type, type.GetProperty("Variables"), BrowsableAttribute.No);
259             builder.AddCustomAttributes(type, new FeatureAttribute(typeof(FlowchartSizeFeature)));
260
261             PropertyInfo nodesProperty = type.GetProperty("Nodes");
262             builder.AddCustomAttributes(type, nodesProperty, new ShowPropertyInOutlineViewAttribute());
263
264             type = typeof(FlowStep);
265             builder.AddCustomAttributes(type, type.GetProperty("Action"), BrowsableAttribute.No);
266             builder.AddCustomAttributes(type, type.GetProperty("Next"), BrowsableAttribute.No);
267
268             builder.AddCustomAttributes(type, new ShowInOutlineViewAttribute() { PromotedProperty = "Action" });
269             builder.AddCustomAttributes(type, type.GetProperty("Next"), new ShowPropertyInOutlineViewAsSiblingAttribute());
270
271             builder.AddCustomAttributes(typeof(FlowNode), new ShowInOutlineViewAttribute());
272             builder.AddCustomAttributes(typeof(Collection<FlowNode>), new ShowInOutlineViewAttribute());
273
274             CutCopyPasteHelper.AddDisallowedTypeForCopy(typeof(StartNode));
275         }
276
277         //Unregister all events. Reset startNodeAdded to enable reuse of the designer.
278         void CleanupFlowchart()
279         {
280             this.startNodeAdded = false;
281             this.panel.Children.Clear();
282             this.flowNodeToUIElement.Clear();
283             // Cleaning up the designers as they might be re-used.
284             foreach (UIElement element in this.modelElement.Values)
285             {
286                 element.MouseEnter -= new MouseEventHandler(ChildElement_MouseEnter);
287                 element.MouseLeave -= new MouseEventHandler(ChildElement_MouseLeave);
288             }
289             this.panel.LocationChanged -= new LocationChangedEventHandler(OnFreeFormPanelLocationChanged);
290             this.panel.ConnectorMoved -= new ConnectorMovedEventHandler(OnFreeFormPanelConnectorMoved);
291             this.panel.LayoutUpdated -= new EventHandler(OnFreeFormPanelLayoutUpdated);
292             this.panel.RequiredSizeChanged -= new RequiredSizeChangedEventHandler(OnFreeFormPanelRequiredSizeChanged);
293             this.panel = null;
294             ModelTreeManager modelTreeManager = (this.ModelItem as IModelTreeItem).ModelTreeManager;
295             modelTreeManager.EditingScopeCompleted -= new EventHandler<EditingScopeEventArgs>(ModelTreeManager_EditingScopeCompleted);
296             this.ViewStateService.ViewStateChanged -= new ViewStateChangedEventHandler(OnViewStateChanged);
297         }
298
299         void OnFreeFormPanelLoaded(object sender, RoutedEventArgs eventArgs)
300         {
301             //Adding the following check because of 137896: Inside tab control multiple Loaded events happen without an Unloaded event.
302             if (this.panel != null)
303             {
304                 CleanupFlowchart();
305             }
306             this.panel = (FreeFormPanel)sender;
307             if (this.ShowExpanded)
308             {
309                 PopulateFlowchartChildren();
310             }
311         }
312
313         void OnFreeFormPanelUnLoaded(object sender, RoutedEventArgs eventArgs)
314         {
315             if (object.Equals(sender, this.panel))
316             {
317                 CleanupFlowchart();
318             }
319         }
320
321         void PopulateFlowchartChildren()
322         {
323             Fx.Assert(this.ShowExpanded, "This method should be called only when the flowchart designer is shown expanded.");
324             Fx.Assert(this.panel != null, "panel cannot be null");
325             this.panel.LocationChanged += new LocationChangedEventHandler(OnFreeFormPanelLocationChanged);
326             this.panel.ConnectorMoved += new ConnectorMovedEventHandler(OnFreeFormPanelConnectorMoved);
327             this.panel.LayoutUpdated += new EventHandler(OnFreeFormPanelLayoutUpdated);
328             this.panel.RequiredSizeChanged += new RequiredSizeChangedEventHandler(OnFreeFormPanelRequiredSizeChanged);
329
330             DesignerPerfEventProvider perfEventProvider = this.Context.Services.GetService<DesignerPerfEventProvider>();
331             perfEventProvider.FlowchartDesignerLoadStart();
332             ModelTreeManager modelTreeManager = (this.ModelItem as IModelTreeItem).ModelTreeManager;
333             modelTreeManager.EditingScopeCompleted += new EventHandler<EditingScopeEventArgs>(ModelTreeManager_EditingScopeCompleted);
334             this.ViewStateService.ViewStateChanged += new ViewStateChangedEventHandler(OnViewStateChanged);
335
336             this.startNodeAdded = false;
337             panel.Children.Clear();
338             this.modelElement.Clear();
339             this.flowNodeToUIElement.Clear();
340             this.shapeLocations.Clear();
341
342             this.FlowchartWidth = (double)TypeDescriptor.GetProperties(this.ModelItem)[FlowchartSizeFeature.WidthPropertyName].GetValue(this.ModelItem);
343             this.FlowchartHeight = (double)TypeDescriptor.GetProperties(this.ModelItem)[FlowchartSizeFeature.HeightPropertyName].GetValue(this.ModelItem);
344
345             CreateStartSymbol();
346             AddFlowElementsToDesigner(this.ModelItem.Properties["Nodes"].Collection, true);
347             perfEventProvider.FlowchartDesignerLoadEnd();
348         }
349
350         //This is to keep this.selectedConnector upto date.
351         //Eg. cases included 1. create a link, select it and undo, 2. Move a link from one shape to another.
352         void OnFreeFormPanelLayoutUpdated(object sender, EventArgs e)
353         {
354             if (!this.panel.Children.Contains(this.selectedConnector))
355             {
356                 this.selectedConnector = null;
357             }
358         }
359
360         public UIElement StartSymbol { get; set; }
361
362         internal static List<ConnectionPoint> GetConnectionPoints(DependencyObject obj)
363         {
364             return (List<ConnectionPoint>)obj.GetValue(FlowchartDesigner.ConnectionPointsProperty);
365         }
366
367         internal static ConnectionPoint GetFalseConnectionPoint(DependencyObject obj)
368         {
369             return (ConnectionPoint)obj.GetValue(FlowchartDesigner.FalseConnectionPointProperty);
370         }
371
372         internal static ModelItem GetLinkModelItem(DependencyObject obj)
373         {
374             return (ModelItem)obj.GetValue(FlowchartDesigner.LinkModelItemProperty);
375         }
376
377         internal static ModelItem GetFlowElementModelItem(DependencyObject obj)
378         {
379             return (ModelItem)obj.GetValue(FlowchartDesigner.FlowElementModelItemProperty);
380         }
381
382         internal static ConnectionPoint GetTrueConnectionPoint(DependencyObject obj)
383         {
384             return (ConnectionPoint)obj.GetValue(FlowchartDesigner.TrueConnectionPointProperty);
385         }
386
387         public double FlowchartWidth
388         {
389             get { return (double)this.GetValue(FlowchartDesigner.FlowchartWidthProperty); }
390             set { this.SetValue(FlowchartDesigner.FlowchartWidthProperty, value); }
391         }
392
393         public double FlowchartHeight
394         {
395             get { return (double)this.GetValue(FlowchartDesigner.FlowchartHeightProperty); }
396             set { this.SetValue(FlowchartDesigner.FlowchartHeightProperty, value); }
397         }
398
399         public bool ShowAllConditions
400         {
401             get { return (bool)GetValue(ShowAllConditionsProperty); }
402             set { SetValue(ShowAllConditionsProperty, value); }
403         }
404
405         ModelItem GetFlowElementMI(ModelItem shapeModelItem)
406         {
407             Fx.Assert(this.modelElement.ContainsKey(shapeModelItem), "The ModelItem does not exist.");
408             UIElement element = this.modelElement[shapeModelItem];
409             ModelItem flowElementMI = FlowchartDesigner.GetFlowElementModelItem(element);
410             Fx.Assert(flowElementMI != null, "FlowNode dependency property not set.");
411             return flowElementMI;
412         }
413
414         protected override void OnInitialized(EventArgs e)
415         {
416             base.OnInitialized(e);
417         }
418
419         //Returns actual link destination - Activity ModelItem in case of a FlowStep.
420         ModelItem GetCorrespondingElementOnCanvas(ModelItem model)
421         {
422             ModelItem destModelItem = model;
423             if (typeof(FlowStep).IsAssignableFrom(model.ItemType)
424                 && model.Properties["Action"].Value != null)
425             {
426                 destModelItem = model.Properties["Action"].Value;
427             }
428             if (typeof(Flowchart) == model.ItemType)
429             {
430                 destModelItem = flowStart;
431             }
432             return destModelItem;
433         }
434
435         private bool IsRightToLeft
436         {
437             get
438             {
439                 if (!this.isRightToLeft.HasValue)
440                 {
441                     this.isRightToLeft = FreeFormPanelUtilities.IsRightToLeft(this.flowchartContentPresenter);
442                 }
443
444                 return this.isRightToLeft.Value;
445             }
446         }
447
448         private void OnFlowchartGridMouseLeave(object sender, MouseEventArgs e)
449         {
450             bool endLinkCreation = !IsVisualHit(sender as UIElement, sender as UIElement, e.GetPosition(sender as IInputElement));
451             if (endLinkCreation)
452             {
453                 RemoveAdorner(this.panel, typeof(ConnectorCreationAdorner));
454                 this.srcConnectionPoint = null;
455             }
456         }
457
458         private void OnFlowchartGridMouseMove(object sender, MouseEventArgs e)
459         {
460             if (this.srcConnectionPoint != null)
461             {
462                 AutoScrollHelper.AutoScroll(e, this, 1);
463                 Point[] points = ConnectorRouter.Route(this.panel, this.srcConnectionPoint, e);
464                 if (points == null)
465                 {
466                     e.Handled = true;
467                     return;
468                 }
469                 List<Point> segments = new List<Point>(points);
470                 //Remove the previous adorner.
471                 RemoveAdorner(this.panel, typeof(ConnectorCreationAdorner));
472                 //Add new adorner.
473                 AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this.srcConnectionPoint.ParentDesigner);
474                 Fx.Assert(adornerLayer != null, "Adorner Layer does not exist");
475                 ConnectorCreationAdorner newAdorner = new ConnectorCreationAdorner(this.panel, segments);
476                 adornerLayer.Add(newAdorner);
477                 e.Handled = true;
478             }
479
480         }
481
482         private void OnFlowchartGridMouseUp(object sender, MouseButtonEventArgs e)
483         {
484             if (this.srcConnectionPoint != null)
485             {
486                 RemoveAdorner(this.panel, typeof(ConnectorCreationAdorner));
487                 this.srcConnectionPoint = null;
488             }
489         }
490
491
492         static void SetConnectionPoints(DependencyObject obj, List<ConnectionPoint> connectionPoints)
493         {
494             obj.SetValue(FlowchartDesigner.ConnectionPointsProperty, connectionPoints);
495         }
496
497
498         static void SetFalseConnectionPoint(DependencyObject obj, ConnectionPoint connectionPoint)
499         {
500             obj.SetValue(FlowchartDesigner.FalseConnectionPointProperty, connectionPoint);
501         }
502
503         static void SetLinkModelItem(DependencyObject obj, ModelItem modelItem)
504         {
505             obj.SetValue(FlowchartDesigner.LinkModelItemProperty, modelItem);
506         }
507
508         static void SetFlowElementModelItem(DependencyObject obj, ModelItem modelItem)
509         {
510             obj.SetValue(FlowchartDesigner.FlowElementModelItemProperty, modelItem);
511         }
512
513         static void SetTrueConnectionPoint(DependencyObject obj, ConnectionPoint connectionPoint)
514         {
515             obj.SetValue(FlowchartDesigner.TrueConnectionPointProperty, connectionPoint);
516         }
517
518         void ChildElement_MouseEnter(object sender, MouseEventArgs e)
519         {
520             Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
521             VirtualizedContainerService.VirtualizingContainer senderElement = sender as VirtualizedContainerService.VirtualizingContainer;
522             if ((senderElement != null || sender is StartSymbol) && !this.IsReadOnly)
523             {
524                 AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer((Visual)sender);
525                 Fx.Assert(adornerLayer != null, "Cannot get AdornerLayer.");
526                 ConnectionPointsAdorner adorner = null;
527                 if (sender is StartSymbol)
528                 {
529                     adorner = new FlowchartConnectionPointsAdorner((UIElement)sender, ConnectionPointsToShow((UIElement)sender, this.ModelItem), false, this.IsRightToLeft);
530                 }
531                 else
532                 {
533                     bool isSenderElementSelected = (((Selection)this.Context.Items.GetValue<Selection>()).SelectedObjects as ICollection<ModelItem>).Contains(senderElement.ModelItem);
534                     adorner = new FlowchartConnectionPointsAdorner(senderElement, ConnectionPointsToShow(senderElement, senderElement.ModelItem), isSenderElementSelected, this.IsRightToLeft);
535                 }
536                 adornerLayer.Add(adorner);
537                 adorner.MouseDown += new MouseButtonEventHandler(ConnectionPoint_MouseDown);
538                 adorner.MouseUp += new MouseButtonEventHandler(ConnectionPoint_MouseUp);
539                 adorner.MouseLeave += new MouseEventHandler(ConnectionPoint_MouseLeave);
540             }
541         }
542
543
544         //This method returns which connection points should be shown on hover of a shape.
545         List<ConnectionPoint> ConnectionPointsToShow(UIElement element, ModelItem model)
546         {
547             bool isInComingConnection = false;
548
549             //This condition checks if it is an incoming connection.
550             if (this.srcConnectionPoint != null || (this.panel.connectorEditor != null && this.panel.connectorEditor.IsConnectorEndBeingMoved))
551             {
552                 isInComingConnection = true;
553             }
554             List<ConnectionPoint> connectionPointsToShow = new List<ConnectionPoint>();
555
556             if (GenericFlowSwitchHelper.IsGenericFlowSwitch(model.ItemType))
557             {
558                 connectionPointsToShow.AddRange(FlowchartDesigner.GetConnectionPoints(element));
559             }
560             else if (typeof(FlowDecision).IsAssignableFrom(model.ItemType))
561             {
562                 if (isInComingConnection)
563                 {
564                     connectionPointsToShow.AddRange(FlowchartDesigner.GetConnectionPoints(element));
565                 }
566                 else
567                 {
568                     connectionPointsToShow.Add(FlowchartDesigner.GetTrueConnectionPoint(element));
569                     connectionPointsToShow.Add(FlowchartDesigner.GetFalseConnectionPoint(element));
570                     List<Connector> outGoingConnectors = GetOutGoingConnectors(element);
571                     if (this.panel.connectorEditor != null && this.panel.connectorEditor.IsConnectorStartBeingMoved)
572                     {
573                         //If the start of an outgoing connector is moved, its not an outgoing connector any more.
574                         outGoingConnectors.Remove(this.panel.connectorEditor.Connector);
575                     }
576                     //Do not show True/False connection point if a link already exists.
577                     foreach (Connector connector in outGoingConnectors)
578                     {
579                         connectionPointsToShow.Remove(FreeFormPanel.GetSourceConnectionPoint(connector));
580                     }
581                 }
582             }
583             else// Case where only one out going connector is allowed - Start and FlowStep.
584             {
585                 ConnectionPointKind allowedType = ConnectionPointKind.Default;
586                 bool isConnectionAllowed = false;
587                 if (isInComingConnection)
588                 {
589                     allowedType = ConnectionPointKind.Incoming;
590                     isConnectionAllowed = true;
591                 }
592                 else
593                 {
594                     List<Connector> outGoingConnectors = GetOutGoingConnectors(element);
595                     if (this.panel.connectorEditor != null && this.panel.connectorEditor.IsConnectorStartBeingMoved)
596                     {
597                         outGoingConnectors.Remove(this.panel.connectorEditor.Connector);
598                     }
599                     //Outgoing Connection is allowed only if there are no outgoing connectors already.
600                     if (outGoingConnectors.Count == 0)
601                     {
602                         allowedType = ConnectionPointKind.Outgoing;
603                         isConnectionAllowed = true;
604                     }
605                 }
606
607                 if (isConnectionAllowed)
608                 {
609                     foreach (ConnectionPoint connPoint in FlowchartDesigner.GetConnectionPoints(element))
610                     {
611                         if (connPoint.PointType == allowedType || connPoint.PointType == ConnectionPointKind.Default)
612                         {
613                             connectionPointsToShow.Add(connPoint);
614                         }
615                     }
616                 }
617             }
618             //Do not show the connection points of a selected connector.
619             if (this.selectedConnector != null)
620             {
621                 connectionPointsToShow.Remove(FreeFormPanel.GetSourceConnectionPoint(this.selectedConnector));
622                 connectionPointsToShow.Remove(FreeFormPanel.GetDestinationConnectionPoint(this.selectedConnector));
623             }
624             return connectionPointsToShow;
625         }
626
627
628         void ChildElement_MouseLeave(object sender, MouseEventArgs e)
629         {
630             Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
631             bool removeConnectionPointsAdorner = true;
632             if (Mouse.DirectlyOver != null)
633             {
634                 removeConnectionPointsAdorner = !typeof(ConnectionPointsAdorner).IsAssignableFrom(Mouse.DirectlyOver.GetType());
635             }
636             if (removeConnectionPointsAdorner)
637             {
638                 RemoveAdorner(sender as UIElement, typeof(ConnectionPointsAdorner));
639             }
640         }
641
642
643         void ChildSizeChanged(object sender, SizeChangedEventArgs e)
644         {
645             Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
646             VirtualizedContainerService.VirtualizingContainer container = sender as VirtualizedContainerService.VirtualizingContainer;
647             if (container != null || sender is StartSymbol)
648             {
649                 this.internalViewStateChange = true;
650                 //Initializing storageModelItem for the case of FlowchartStartNode.
651                 ModelItem storageModelItem = this.ModelItem;
652                 if (container != null)
653                 {
654                     storageModelItem = GetFlowElementMI(container.ModelItem);
655                 }
656                 this.ViewStateService.StoreViewState(storageModelItem, shapeSize, ((UIElement)sender).DesiredSize);
657                 this.internalViewStateChange = false;
658             }
659         }
660
661         void ConnectionPoint_MouseDown(object sender, MouseButtonEventArgs e)
662         {
663             UIElement srcElement = ((Adorner)sender).AdornedElement as UIElement;
664             this.srcConnectionPoint = ConnectionPointHitTest(srcElement, e.GetPosition(this.panel));
665             e.Handled = true;
666         }
667
668         void ConnectionPoint_MouseLeave(object sender, MouseEventArgs e)
669         {
670             Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
671             UIElement adornedElement = ((Adorner)sender).AdornedElement as UIElement;
672             RemoveAdorner(adornedElement, typeof(ConnectionPointsAdorner));
673         }
674
675
676         void ConnectionPoint_MouseUp(object sender, MouseButtonEventArgs e)
677         {
678             Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
679             UIElement dest = ((Adorner)sender).AdornedElement as UIElement;
680             Fx.Assert(dest != null, "Adorned element is not a UIElement");
681             if (this.srcConnectionPoint != null)
682             {
683                 ConnectionPoint destConnectionPoint = ConnectionPointHitTest(dest, e.GetPosition(this.panel));
684                 if (destConnectionPoint != null && !this.srcConnectionPoint.Equals(destConnectionPoint))
685                 {
686                     string errorMessage = string.Empty;
687                     if (!CreateLinkGesture(this.srcConnectionPoint, destConnectionPoint, out errorMessage, null) && !errorMessage.Equals(string.Empty))
688                     {
689                         ErrorReporting.ShowErrorMessage(errorMessage);
690                     }
691                 }
692                 this.srcConnectionPoint = null;
693                 RemoveAdorner(this.panel, typeof(ConnectorCreationAdorner));
694                 RemoveAdorner(dest, typeof(FlowchartConnectionPointsAdorner));
695             }
696             else
697             {
698                 //This will cause the FreeFormPanel to handle the event and is useful while moving connection end points of a connector.
699                 lastConnectionPointMouseUpElement = dest;
700                 dest.RaiseEvent(e);
701             }
702         }
703
704         void OnFreeFormPanelRequiredSizeChanged(object sender, RequiredSizeChangedEventArgs e)
705         {
706             Dispatcher.BeginInvoke(() =>
707             {
708                 // Access the view state dictionary directly to avoid generating an undo item because of ViewStateAttachedPropertyFeature implementation.
709                 Dictionary<string, object> viewState = WorkflowViewStateService.GetViewState(this.ModelItem.GetCurrentValue());
710                 if (viewState == null)
711                 {
712                     viewState = new Dictionary<string, object>();
713                     WorkflowViewStateService.SetViewState(this.ModelItem.GetCurrentValue(), viewState);
714                 }
715
716                 if (e.NewRequiredSize.Width > this.FlowchartWidth)
717                 {
718                     viewState[FlowchartSizeFeature.WidthPropertyName] = e.NewRequiredSize.Width;
719                     this.FlowchartWidth = e.NewRequiredSize.Width;
720                 }
721
722                 if (e.NewRequiredSize.Height > this.FlowchartHeight)
723                 {
724                     viewState[FlowchartSizeFeature.HeightPropertyName] = e.NewRequiredSize.Height;
725                     this.FlowchartHeight = e.NewRequiredSize.Height;
726                 }
727             });
728         }
729
730         void OnFreeFormPanelLocationChanged(object sender, System.Activities.Presentation.FreeFormEditing.LocationChangedEventArgs e)
731         {
732             Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
733             Fx.Assert(sender is UIElement, "Sender should be of type UIElement");
734             Connector movedConnector = sender as Connector;
735             if (movedConnector != null)
736             {
737                 //ViewState is undoable only when a user gesture moves a connector. If the freeformpanel routes a connector,
738                 //the change is not undoable.
739                 bool isUndoableViewState = false;
740                 ModelItem linkModelItem = FlowchartDesigner.GetLinkModelItem(movedConnector);
741                 ConnectionPoint source = FreeFormPanel.GetSourceConnectionPoint(movedConnector);
742                 string viewStateKey = GetConnectorViewStateKey(linkModelItem, source);
743                 ModelItem storageModelItem = GetConnectorViewStateStorageModelItem(linkModelItem);
744                 PointCollection existingVS = this.ViewStateService.RetrieveViewState(storageModelItem, viewStateKey) as PointCollection;
745                 if (existingVS != null && existingVS.Count > 0 && movedConnector.Points.Count > 0
746                     && existingVS[0].Equals(movedConnector.Points[0]) && existingVS[existingVS.Count - 1].Equals(movedConnector.Points[movedConnector.Points.Count - 1]))
747                 {
748                     isUndoableViewState = true;
749                 }
750                 StoreConnectorViewState(movedConnector, isUndoableViewState);
751             }
752             else
753             {
754                 //Save the location property of each shape on the CFx object for serialization and viewstate maintenance.
755                 //This is called only when a shape without viewstate is autolayed out by the freeform panel.
756                 VirtualizedContainerService.VirtualizingContainer container = sender as VirtualizedContainerService.VirtualizingContainer;
757                 if (container != null)
758                 {
759                     StoreShapeViewState(container, e.NewLocation);
760                 }
761             }
762         }
763
764
765         void UpdateFlowchartOnLinkVisualMoved(ConnectionPoint knownConnectionPoint, Point newPoint, Connector movedConnector, bool isSourceKnown)
766         {
767             Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
768             HitTestResult hitTestResult = VisualTreeHelper.HitTest(this.panel, newPoint);
769             if (hitTestResult == null)
770             {
771                 return;
772             }
773             //Test if the last connectionPoint hit, is the new location for the connector.
774             UIElement newViewElement = null;
775             ConnectionPoint newConnectionPoint = null;
776
777             //The case where the link is dropped on a connectionpoint.
778             if (this.lastConnectionPointMouseUpElement != null)
779             {
780                 newConnectionPoint = this.ConnectionPointHitTest(this.lastConnectionPointMouseUpElement, newPoint);
781                 if (newConnectionPoint != null)
782                 {
783                     newViewElement = this.lastConnectionPointMouseUpElement;
784                 }
785             }
786             //The case where the link is dropped on a shape.
787             if (newViewElement == null)
788             {
789                 newViewElement = VisualTreeUtils.FindVisualAncestor<StartSymbol>(hitTestResult.VisualHit);
790                 if (newViewElement == null)
791                 {
792                     newViewElement = VisualTreeUtils.FindVisualAncestor<VirtualizedContainerService.VirtualizingContainer>(hitTestResult.VisualHit);
793                 }
794             }
795             if (newViewElement != null)
796             {
797                 if (this.panel.Children.Contains(newViewElement))
798                 {
799                     using (EditingScope es = (EditingScope)this.ModelItem.BeginEdit(SR.FCLinkMove))
800                     {
801                         //Delete the existing link and keep the caseKey
802                         IFlowSwitchLink oldCaseKey = this.DeleteLink(movedConnector, true);
803
804                         //Create new link
805                         bool linkCreated = false;
806                         string errorMessage = string.Empty;
807                         if (isSourceKnown)
808                         {
809                             if (newConnectionPoint == null)
810                             {
811                                 linkCreated = CreateLinkGesture(knownConnectionPoint, newViewElement, newPoint, out errorMessage, true, oldCaseKey);
812                             }
813                             else
814                             {
815                                 linkCreated = CreateLinkGesture(knownConnectionPoint, newConnectionPoint, out errorMessage, true, oldCaseKey);
816                             }
817                         }
818                         else
819                         {
820                             //If the Link source is dropped onto itself, we need to set the isLinkValidDueToLinkMove flag.
821                             bool isLinkValidDueToLinkMove = FreeFormPanel.GetSourceConnectionPoint(movedConnector).ParentDesigner.Equals(newViewElement);
822                             if (newConnectionPoint == null)
823                             {
824                                 linkCreated = CreateLinkGesture(newViewElement, knownConnectionPoint, newPoint, out errorMessage, isLinkValidDueToLinkMove, oldCaseKey);
825                             }
826                             else
827                             {
828                                 linkCreated = CreateLinkGesture(newConnectionPoint, knownConnectionPoint, out errorMessage, isLinkValidDueToLinkMove, oldCaseKey);
829                             }
830                         }
831                         if (!linkCreated)
832                         {
833                             if (!errorMessage.Equals(string.Empty))
834                             {
835                                 ErrorReporting.ShowErrorMessage(errorMessage);
836                             }
837                             es.Revert();
838                         }
839                         else
840                         {
841                             es.Complete();
842                         }
843                     }
844                 }
845             }
846         }
847
848         void OnFreeFormPanelConnectorMoved(object sender, ConnectorMovedEventArgs e)
849         {
850             Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
851             Connector movedConnector = sender as Connector;
852             int movedEndConnectorPointIndex = movedConnector.Points.Count - 1;
853             int newEndConnectorPointIndex = e.NewConnectorLocation.Count - 1;
854
855             if (movedConnector != null)
856             {
857                 Fx.Assert(e.NewConnectorLocation.Count > 0, "Invalid connector editor");
858                 if (!e.NewConnectorLocation[0].Equals(movedConnector.Points[0]))
859                 {
860                     //srcMoved
861                     ConnectionPoint destConnPoint = FreeFormPanel.GetDestinationConnectionPoint(movedConnector);
862                     UpdateFlowchartOnLinkVisualMoved(destConnPoint, e.NewConnectorLocation[0], movedConnector, false);
863                 }
864                 else if (!e.NewConnectorLocation[newEndConnectorPointIndex].Equals(movedConnector.Points[movedEndConnectorPointIndex]))
865                 {
866                     //DestMoved
867                     ConnectionPoint srcConnPoint = FreeFormPanel.GetSourceConnectionPoint(movedConnector);
868                     Point destPoint = e.NewConnectorLocation[newEndConnectorPointIndex];
869                     UpdateFlowchartOnLinkVisualMoved(srcConnPoint, destPoint, movedConnector, true);
870                 }
871
872                 this.selectedConnector = movedConnector;
873             }
874         }
875
876         MultiBinding GetConnectionPointBinding(FrameworkElement element, double widthFraction, double heightFraction)
877         {
878             Fx.Assert(element != null, "FrameworkElement is null.");
879             MultiBinding bindings = new MultiBinding();
880             Binding sizeBinding = new Binding();
881             sizeBinding.Source = element;
882             sizeBinding.Path = new PropertyPath(FreeFormPanel.ChildSizeProperty);
883             Binding locationBinding = new Binding();
884             locationBinding.Source = element;
885             locationBinding.Path = new PropertyPath(FreeFormPanel.LocationProperty);
886             bindings.Bindings.Add(sizeBinding);
887             bindings.Bindings.Add(locationBinding);
888             bindings.Converter = new ConnectionPointConverter();
889             bindings.ConverterParameter = new List<Object> { widthFraction, heightFraction, element.Margin };
890             return bindings;
891         }
892
893
894         Connector GetConnector(ModelItem linkModelItem, ConnectionPoint srcConnPoint, ConnectionPoint destConnPoint)
895         {
896             Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
897             ConnectorWithoutStartDot connector = new ConnectorWithoutStartDot();
898             connector.FocusVisualStyle = null;
899             connector.Focusable = true;
900             DesignerView.SetCommandMenuMode(connector, CommandMenuMode.NoCommandMenu);
901             SetConnectorLabel(connector, srcConnPoint, linkModelItem);
902
903             connector.GotKeyboardFocus += new KeyboardFocusChangedEventHandler(OnConnectorGotKeyboardFocus);
904             connector.RequestBringIntoView += new RequestBringIntoViewEventHandler(OnConnectorRequestBringIntoView);
905             connector.MouseDown += new MouseButtonEventHandler(OnConnectorMouseDown);
906             connector.GotFocus += new RoutedEventHandler(OnConnectorGotFocus);
907             SetConnectorSrcDestConnectionPoints(connector, srcConnPoint, destConnPoint);
908             FlowchartDesigner.SetLinkModelItem(connector, linkModelItem);
909             connector.Unloaded += new RoutedEventHandler(OnConnectorUnloaded);
910             connector.AutoSplitContainer = this;
911             return connector;
912         }
913
914         // To prevent the parent FlowchartDesigner from handling mouse events and setting the selection to itself.
915         void OnConnectorMouseDown(object sender, MouseButtonEventArgs e)
916         {
917             e.Handled = true;
918         }
919
920         void SetConnectorLabel(Connector connector, ConnectionPoint srcConnPoint, ModelItem linkModelItem)
921         {
922             BindingBase labelBinding = null;
923
924             if (typeof(FlowDecision).IsAssignableFrom(linkModelItem.ItemType))
925             {
926                 if (FlowchartDesigner.GetTrueConnectionPoint(srcConnPoint.ParentDesigner).Equals(srcConnPoint))
927                 {
928                     labelBinding = new Binding { Source = linkModelItem, Path = new PropertyPath("TrueLabel") };
929                 }
930                 else
931                 {
932                     labelBinding = new Binding { Source = linkModelItem, Path = new PropertyPath("FalseLabel") };
933                 }
934
935                 SetConnectorLabelToolTip(connector, labelBinding);
936             }
937             else if (typeof(IFlowSwitchLink).IsAssignableFrom(linkModelItem.ItemType))
938             {
939                 IFlowSwitchLink flowSwitchLink = (IFlowSwitchLink)linkModelItem.GetCurrentValue();
940                 labelBinding = flowSwitchLink.CreateConnectorLabelTextBinding();
941                 SetConnectorLabelToolTip(connector, labelBinding);
942             }
943         }
944
945         void SetConnectorLabelToolTip(Connector connector, BindingBase binding)
946         {
947             connector.SetBinding(Connector.LabelTextProperty, binding);
948             ToolTip toolTip = new ToolTip();
949             toolTip.SetBinding(UserControl.ContentProperty, binding);
950             connector.SetLabelToolTip(toolTip);
951         }
952
953         void OnConnectorUnloaded(object sender, RoutedEventArgs e)
954         {
955             ModelItem primarySelection = this.Context.Items.GetValue<Selection>().PrimarySelection;
956             if (object.Equals(primarySelection, FlowchartDesigner.GetLinkModelItem(sender as DependencyObject)))
957             {
958                 if (primarySelection != null)
959                 {
960                     Selection.Toggle(this.Context, primarySelection);
961                 }
962             }
963         }
964
965         //Marking e.Handled = true to avoid scrolling in large workflows to bring the
966         //area of a connector in the center of the view region.
967         //Area covered by a connector includes the region between 0,0 of the panel and the edges of the connector.
968         void OnConnectorRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
969         {
970             e.Handled = true;
971         }
972
973
974
975         //Returns a new connector if viewstate exists, null otherwise.
976         Connector GetConnectorViewState(UIElement source, UIElement dest, ModelItem linkModelItem, ConnectionPoint sourceConnectionPoint)
977         {
978             Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
979             Connector connector = null;
980             object connectorLocation = null;
981             if (typeof(FlowDecision).IsAssignableFrom(linkModelItem.ItemType))
982             {
983                 Fx.Assert(sourceConnectionPoint != null, "Source connection point is null.");
984                 if (sourceConnectionPoint.Equals(FlowchartDesigner.GetTrueConnectionPoint(this.modelElement[linkModelItem])))
985                 {
986                     connectorLocation = this.ViewStateService.RetrieveViewState(linkModelItem, TrueConnectorViewStateKey);
987                 }
988                 else
989                 {
990                     connectorLocation = this.ViewStateService.RetrieveViewState(linkModelItem, FalseConnectorViewStateKey);
991                 }
992             }
993             else if (typeof(IFlowSwitchLink).IsAssignableFrom(linkModelItem.ItemType))
994             {
995                 string key = null;
996                 IFlowSwitchLink link = (IFlowSwitchLink)linkModelItem.GetCurrentValue();
997                 if (link.IsDefaultCase)
998                 {
999                     key = FlowSwitchDefaultViewStateKey;
1000                 }
1001                 else
1002                 {
1003                     key = link.CaseName + CaseViewStateKeyAppendString;
1004                 }
1005                 //Transitioning from fake ModelItem world to real ModelItem world.
1006                 ModelItem realFSModelItem = (this.ModelItem as IModelTreeItem).ModelTreeManager.WrapAsModelItem(link.ParentFlowSwitch);
1007                 connectorLocation = this.ViewStateService.RetrieveViewState(realFSModelItem, key);
1008             }
1009             else
1010             {
1011                 connectorLocation = this.ViewStateService.RetrieveViewState(linkModelItem, ConnectorViewStateKey);
1012             }
1013             PointCollection locationPts = connectorLocation as PointCollection;
1014             if (locationPts != null)
1015             {
1016                 ConnectionPoint srcConnPoint, destConnPoint;
1017                 System.Diagnostics.Debug.WriteLine(this.isLoaded ? "About to call ConnectionPointHitTest - Loaded" : "About to call ConnectionPointHitTest - Not Loaded");
1018                 srcConnPoint = ConnectionPointHitTest(source, locationPts[0]);
1019                 destConnPoint = ConnectionPointHitTest(dest, locationPts[locationPts.Count - 1]);
1020                 //In Debug mode, the size of the designer changes due to the debug adorner(border). Because of this connection points will move and
1021                 //won't coincide with the viewstate.
1022                 //The following code path is added for the scenario where we reload the flowchart designer by navigating back and forth on breadcrumb
1023                 //when one of the flowchart activities has the debug border.
1024                 //In this scenario we try to find the closest connection point from the end point stored in viewstate. If the distance between the two
1025                 //is within the acceptable range, we will reuse the viewstate and avoid re-drawing the connector.
1026                 if (this.IsReadOnly)
1027                 {
1028                     ConnectionPoint pt;
1029                     double dist;
1030                     if (srcConnPoint == null)
1031                     {
1032                         pt = FindClosestConnectionPoint(locationPts[0], FlowchartDesigner.GetConnectionPoints(source), out dist);
1033                         if (pt != null && pt.PointType != ConnectionPointKind.Incoming && dist <= DebugTimeMaxConnectorShapeDist)
1034                         {
1035                             srcConnPoint = pt;
1036                         }
1037                     }
1038                     if (destConnPoint == null)
1039                     {
1040                         pt = FindClosestConnectionPoint(locationPts[locationPts.Count - 1], FlowchartDesigner.GetConnectionPoints(dest), out dist);
1041                         if (pt != null && pt.PointType != ConnectionPointKind.Outgoing && dist <= DebugTimeMaxConnectorShapeDist)
1042                         {
1043                             destConnPoint = pt;
1044                         }
1045                     }
1046                 }
1047                 if (srcConnPoint != null && destConnPoint != null)
1048                 {
1049                     connector = GetConnector(linkModelItem, srcConnPoint, destConnPoint);
1050                     connector.Points = locationPts;
1051                 }
1052             }
1053             return connector;
1054         }
1055
1056
1057         //Marking e.Handled true for the case where a connector is clicked on.
1058         //This is to prevent WorkflowViewElement class from making Flowchart as the current selection.
1059         void OnConnectorGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
1060         {
1061             e.Handled = true;
1062         }
1063
1064         void OnConnectorGotFocus(object sender, RoutedEventArgs e)
1065         {
1066             Connector clickedLine = e.Source as Connector;
1067             DesignerView designerView = this.Context.Services.GetService<DesignerView>();
1068
1069             if (!designerView.IsMultipleSelectionMode)
1070             {
1071
1072                 if (this.panel.connectorEditor == null || !clickedLine.Equals(this.panel.connectorEditor.Connector))
1073                 {
1074                     this.panel.RemoveConnectorEditor();
1075                     this.panel.connectorEditor = new ConnectorEditor(this.panel, clickedLine);
1076                 }
1077
1078                 if (this.panel.Children.Contains(clickedLine))
1079                 {
1080                     this.updatingSelectedConnector = true;
1081                     ModelItem lineModelItem = FlowchartDesigner.GetLinkModelItem(clickedLine);
1082                     Selection newSelection = new Selection();
1083                     // If the linkModelItem is FlowDecision or Flowchart, we don't want to add it to the selection
1084                     if (IsLinkModelItemSelectable(lineModelItem))
1085                     {
1086                         newSelection = new Selection(lineModelItem);
1087                     }
1088                     this.Context.Items.SetValue(newSelection);
1089                     this.selectedConnector = clickedLine;
1090                     this.updatingSelectedConnector = false;
1091                     e.Handled = true;
1092                 }
1093             }
1094         }
1095
1096         private void OnSelectionChanged(Selection selection)
1097         {
1098             // If selection changed, remove ConnectorEditor if existed.
1099             // Only if the selection changed is caused by adding ConnectorEditor when OnConnectorGotFocus, ignore.
1100             if (!this.updatingSelectedConnector && this.panel != null && this.panel.connectorEditor != null)
1101             {
1102                 this.panel.RemoveConnectorEditor();
1103             }
1104         }
1105
1106         //widthFraction, heightFraction determine location of connectionpoint on the shape.
1107         ConnectionPoint CreateConnectionPoint(UIElement element, double widthFraction, double heightFraction, EdgeLocation location)
1108         {
1109             ConnectionPoint connectionPoint = new ConnectionPoint();
1110             connectionPoint.EdgeLocation = location;
1111             connectionPoint.PointType = ConnectionPointKind.Default;
1112             connectionPoint.ParentDesigner = element;
1113             BindingOperations.SetBinding(connectionPoint, ConnectionPoint.LocationProperty, GetConnectionPointBinding(element as FrameworkElement, widthFraction, heightFraction));
1114             return connectionPoint;
1115         }
1116
1117         void PopulateConnectionPoints(UIElement element, ModelItem model)
1118         {
1119             element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
1120             List<ConnectionPoint> connectionPoints;
1121             if (model != null &&
1122                 GenericFlowSwitchHelper.IsGenericFlowSwitch(model.ItemType))
1123             {
1124                 connectionPoints = new List<ConnectionPoint>
1125                     {
1126                         //Top edge
1127                         CreateConnectionPoint(element, 0.25, 0, EdgeLocation.Top),
1128                         CreateConnectionPoint(element, 0.5, 0, EdgeLocation.Top),
1129                         CreateConnectionPoint(element, 0.75, 0, EdgeLocation.Top),
1130                         //Right edge
1131                         CreateConnectionPoint(element, 1, 0.25, EdgeLocation.Right),
1132                         CreateConnectionPoint(element, 1, 0.50, EdgeLocation.Right),
1133                         CreateConnectionPoint(element, 1, 0.75, EdgeLocation.Right),
1134                         //Bottom edge
1135                         CreateConnectionPoint(element, 0.25, 1, EdgeLocation.Bottom),
1136                         CreateConnectionPoint(element, 0.5, 1, EdgeLocation.Bottom),
1137                         CreateConnectionPoint(element, 0.75, 1, EdgeLocation.Bottom),
1138                         //Left edge
1139                         CreateConnectionPoint(element, 0, 0.25, EdgeLocation.Left),
1140                         CreateConnectionPoint(element, 0, 0.50, EdgeLocation.Left),
1141                         CreateConnectionPoint(element, 0, 0.75, EdgeLocation.Left),
1142                     };
1143             }
1144             else if (model != null && typeof(FlowDecision).IsAssignableFrom(model.ItemType))
1145             {
1146                 ConnectionPoint trueConnectionPoint = CreateConnectionPoint(element, 0, 0.50, EdgeLocation.Left);
1147                 trueConnectionPoint.PointType = ConnectionPointKind.Outgoing;
1148                 FlowchartDesigner.SetTrueConnectionPoint(element, trueConnectionPoint);
1149
1150                 ConnectionPoint falseConnectionPoint = CreateConnectionPoint(element, 1, 0.50, EdgeLocation.Right);
1151                 falseConnectionPoint.PointType = ConnectionPointKind.Outgoing;
1152                 FlowchartDesigner.SetFalseConnectionPoint(element, falseConnectionPoint);
1153
1154                 connectionPoints = new List<ConnectionPoint>
1155                     {
1156                         //Top edge
1157                         CreateConnectionPoint(element, 0.25, 0, EdgeLocation.Top),
1158                         CreateConnectionPoint(element, 0.5, 0, EdgeLocation.Top),
1159                         CreateConnectionPoint(element, 0.75, 0, EdgeLocation.Top),
1160                         //Bottom edge
1161                         CreateConnectionPoint(element, 0.5, 1, EdgeLocation.Bottom),
1162                     };
1163                 connectionPoints.ForEach((point) => point.PointType = ConnectionPointKind.Incoming);
1164             }
1165             else
1166             {
1167                 //First adding top, right, bottom and left default connection points in that order on all shapes other than flowswitch.
1168                 //For shapes that do not need any of the points, we will remove that point explicitly.
1169                 connectionPoints = new List<ConnectionPoint>
1170                     {
1171                         CreateConnectionPoint(element, 0.5, 0, EdgeLocation.Top),
1172                         CreateConnectionPoint(element, 1, 0.5, EdgeLocation.Right),
1173                         CreateConnectionPoint(element, 0.5, 1, EdgeLocation.Bottom),
1174                         CreateConnectionPoint(element, 0, 0.5, EdgeLocation.Left)
1175                     };
1176             }
1177
1178             if (model == null) // Start symbol: model = null
1179             {
1180                 foreach (ConnectionPoint connPoint in connectionPoints)
1181                 {
1182                     connPoint.PointType = ConnectionPointKind.Outgoing;
1183                 }
1184             }
1185             FlowchartDesigner.SetConnectionPoints(element, connectionPoints);
1186         }
1187
1188         void SetFlowElementModelItem(UIElement view, ModelItem model)
1189         {
1190             ModelItem flowElementMI = model;
1191             if (flowElementMI != null && !IsFlowNode(flowElementMI))
1192             {
1193                 ModelItem flowStepMI = null;
1194                 //Select the right FlowStep ModelItem out of view.ModelItem.Parents.
1195                 foreach (ModelItem parentModelItem in flowElementMI.Parents)
1196                 {
1197                     if (IsFlowNode(parentModelItem)
1198                         && this.ModelItem.Properties["Nodes"].Collection.Contains(parentModelItem))
1199                     {
1200                         flowStepMI = parentModelItem;
1201                         break;
1202                     }
1203                 }
1204                 flowElementMI = flowStepMI;
1205             }
1206             Fx.Assert(flowElementMI != null, "Non FlowNode present on Flowchart");
1207             FlowchartDesigner.SetFlowElementModelItem(view as DependencyObject, flowElementMI);
1208             this.flowNodeToUIElement[flowElementMI] = view;
1209
1210         }
1211
1212         UIElement ProcessAndGetModelView(ModelItem model)
1213         {
1214             Fx.Assert(this.panel != null, "This code should not be hit if panel is null");
1215             UIElement container;
1216             if (!this.modelElement.TryGetValue(model, out container))
1217             {
1218                 VirtualizedContainerService containerService = this.Context.Services.GetService<VirtualizedContainerService>();
1219                 Fx.Assert(this.ViewService != null, "ViewService is null");
1220                 container = containerService.GetContainer(model, this);
1221                 //WorkflowViewElement view = (WorkflowViewElement)this.ViewService.GetView(model);
1222                 //Fx.Assert(view != null, "View does not exist for a model Item");
1223                 //DragDropHelper.SetSourceContainer(view, this);
1224                 //element = (UIElement)view;
1225                 //element.MouseEnter += new MouseEventHandler(ChildElement_MouseEnter);
1226                 //element.MouseLeave += new MouseEventHandler(ChildElement_MouseLeave);
1227                 container.MouseEnter += new MouseEventHandler(ChildElement_MouseEnter);
1228                 container.MouseLeave += new MouseEventHandler(ChildElement_MouseLeave);
1229
1230                 ((FrameworkElement)container).SizeChanged += new SizeChangedEventHandler(ChildSizeChanged);
1231                 this.modelElement.Add(model, container);
1232                 PopulateConnectionPoints(container, model);
1233                 this.SetFlowElementModelItem(container, model);
1234
1235                 //Getting the View state information.
1236                 ModelItem flowElementMI = GetFlowElementMI(model);
1237                 object locationOfShape = this.ViewStateService.RetrieveViewState(flowElementMI, shapeLocation);
1238                 object sizeOfShape = this.ViewStateService.RetrieveViewState(flowElementMI, shapeSize);
1239                 if (locationOfShape != null)
1240                 {
1241                     Point locationPt = (Point)locationOfShape;
1242                     FreeFormPanel.SetLocation(container, locationPt);
1243                     this.shapeLocations.Add(locationPt);
1244                 }
1245                 if (sizeOfShape != null)
1246                 {
1247                     Size size = (Size)sizeOfShape;
1248                     FreeFormPanel.SetChildSize(container, size);
1249                     VirtualizedContainerService.VirtualizingContainer virtualizingContainer = container as VirtualizedContainerService.VirtualizingContainer;
1250                     if (virtualizingContainer != null)
1251                     {
1252                         virtualizingContainer.MinWidth = size.Width;
1253                         virtualizingContainer.MinHeight = size.Height;
1254                     }
1255
1256                 }
1257             }
1258             return container;
1259         }
1260
1261         void GetSrcDestConnectionPoints(UIElement source, UIElement dest, out ConnectionPoint srcConnPoint, out ConnectionPoint destConnPoint, out string errorMessage)
1262         {
1263             srcConnPoint = null;
1264             destConnPoint = null;
1265             errorMessage = string.Empty;
1266             VirtualizedContainerService.VirtualizingContainer sourceContainer = source as VirtualizedContainerService.VirtualizingContainer;
1267             if (sourceContainer != null && typeof(FlowDecision).IsAssignableFrom(sourceContainer.ModelItem.ItemType))
1268             {
1269                 srcConnPoint = FindFlowDecisionSrcConnectionPoint(source, out errorMessage);
1270                 if (srcConnPoint != null)
1271                 {
1272                     destConnPoint = FindBestMatchDestConnectionPoint(srcConnPoint, dest, out errorMessage);
1273                 }
1274             }
1275             else
1276             {
1277
1278                 List<ConnectionPoint> srcConnectionPoints = FlowchartDesigner.GetConnectionPoints(source);
1279                 List<ConnectionPoint> destConnectionPoints = FlowchartDesigner.GetConnectionPoints(dest);
1280                 if (sourceContainer != null && GenericFlowSwitchHelper.IsGenericFlowSwitch(sourceContainer.ModelItem.ItemType))
1281                 {
1282                     FindBestMatchConnectionPointPair(srcConnectionPoints, destConnectionPoints, out srcConnPoint, out destConnPoint);
1283                 }
1284                 else
1285                 {
1286                     // Flowstep
1287                     FindBestMatchConnectionPointPair(srcConnectionPoints, destConnectionPoints, out srcConnPoint, out destConnPoint);
1288                 }
1289             }
1290         }
1291
1292         //This returns the closest non-outgoing connectionPoint on dest. Return value will be different than sourceConnectionPoint.
1293         ConnectionPoint ClosestDestConnectionPoint(ConnectionPoint sourceConnectionPoint, UIElement dest, out string errorMessage)
1294         {
1295             ConnectionPoint destConnectionPoint = null;
1296             errorMessage = string.Empty;
1297             if (sourceConnectionPoint.PointType != ConnectionPointKind.Incoming)
1298             {
1299                 destConnectionPoint = FindClosestConnectionPointNotOfType(sourceConnectionPoint, FlowchartDesigner.GetConnectionPoints(dest), ConnectionPointKind.Outgoing);
1300             }
1301             else
1302             {
1303                 errorMessage = SR.FCInvalidLink;
1304             }
1305             return destConnectionPoint;
1306
1307         }
1308
1309         //This returns the closest non-Incoming connectionPoint on source. Return value will be different than destConnectionPoint.
1310         ConnectionPoint ClosestSrcConnectionPoint(UIElement src, ConnectionPoint destConnectionPoint, out string errorMessage)
1311         {
1312             ConnectionPoint sourceConnectionPoint = null;
1313             errorMessage = string.Empty;
1314             if (destConnectionPoint.PointType != ConnectionPointKind.Outgoing)
1315             {
1316                 VirtualizedContainerService.VirtualizingContainer srcContainer = src as VirtualizedContainerService.VirtualizingContainer;
1317                 if (srcContainer != null && typeof(FlowDecision).IsAssignableFrom(srcContainer.ModelItem.ItemType))
1318                 {
1319                     sourceConnectionPoint = FindFlowDecisionSrcConnectionPoint(src, out errorMessage);
1320                 }
1321                 else
1322                 {
1323                     sourceConnectionPoint = FindClosestConnectionPointNotOfType(destConnectionPoint, FlowchartDesigner.GetConnectionPoints(src), ConnectionPointKind.Incoming);
1324                 }
1325             }
1326             else
1327             {
1328                 errorMessage = SR.FCInvalidLink;
1329             }
1330             return sourceConnectionPoint;
1331         }
1332
1333         //Priority of selection: 1st true then false.
1334         ConnectionPoint FindFlowDecisionSrcConnectionPoint(UIElement decisionDesigner, out string errorMessage)
1335         {
1336             ConnectionPoint sourceConnectionPoint = null;
1337             errorMessage = string.Empty;
1338             ConnectionPoint trueConnPoint = FlowchartDesigner.GetTrueConnectionPoint(decisionDesigner);
1339             ConnectionPoint falseConnPoint = FlowchartDesigner.GetFalseConnectionPoint(decisionDesigner);
1340             if (trueConnPoint.AttachedConnectors.Count == 0)
1341             {
1342                 sourceConnectionPoint = trueConnPoint;
1343             }
1344             else if (falseConnPoint.AttachedConnectors.Count == 0)
1345             {
1346                 sourceConnectionPoint = falseConnPoint;
1347             }
1348             else
1349             {
1350                 errorMessage = SR.FCFlowConditionLinksExist;
1351             }
1352             return sourceConnectionPoint;
1353         }
1354
1355         void SetConnectorSrcDestConnectionPoints(Connector connector, ConnectionPoint sourceConnectionPoint, ConnectionPoint destConnectionPoint)
1356         {
1357             FreeFormPanel.SetSourceConnectionPoint(connector, sourceConnectionPoint);
1358             FreeFormPanel.SetDestinationConnectionPoint(connector, destConnectionPoint);
1359             sourceConnectionPoint.AttachedConnectors.Add(connector);
1360             destConnectionPoint.AttachedConnectors.Add(connector);
1361         }
1362
1363         //Save the connector.Points property on the CFx object for serialization and viewstate maintenance.
1364         void StoreConnectorViewState(ModelItem linkModelItem, PointCollection viewState, ConnectionPoint srcConnPoint, bool isUndoableViewState)
1365         {
1366             ModelItem storageModelItem = GetConnectorViewStateStorageModelItem(linkModelItem);
1367             string viewStateKey = GetConnectorViewStateKey(linkModelItem, srcConnPoint);
1368             StoreConnectorViewState(storageModelItem, viewStateKey, viewState, isUndoableViewState);
1369         }
1370
1371         void StoreConnectorViewState(ModelItem storageModelItem, string viewStateKey, PointCollection viewState, bool isUndoableViewState)
1372         {
1373             if (isUndoableViewState)
1374             {
1375                 this.ViewStateService.StoreViewStateWithUndo(storageModelItem, viewStateKey, viewState);
1376             }
1377             else
1378             {
1379                 this.ViewStateService.StoreViewState(storageModelItem, viewStateKey, viewState);
1380             }
1381         }
1382
1383         void StoreConnectorViewState(ModelItem linkModelItem, PointCollection viewState, ConnectionPoint srcConnPoint)
1384         {
1385             StoreConnectorViewState(linkModelItem, viewState, srcConnPoint, true);
1386         }
1387
1388         void StoreConnectorViewState(Connector connector, bool isUndoableViewState)
1389         {
1390             //This method will be called whenever the FreeFormPanel raises a location changed event on a connector.
1391             //Such location changed events are a result of changes already commited in the UI. Hence we do not want to react to such view state changes.
1392             //Using internalViewStateChange flag for that purpose.
1393             this.internalViewStateChange = true;
1394             this.StoreConnectorViewState(FlowchartDesigner.GetLinkModelItem(connector), connector.Points, FreeFormPanel.GetSourceConnectionPoint(connector), isUndoableViewState);
1395             this.internalViewStateChange = false;
1396         }
1397
1398         string GetConnectorViewStateKey(ModelItem linkModelItem, ConnectionPoint srcConnPoint)
1399         {
1400             string viewStateKey = ConnectorViewStateKey;
1401             if ((typeof(FlowDecision).IsAssignableFrom(linkModelItem.ItemType)))
1402             {
1403                 if (srcConnPoint.Equals(FlowchartDesigner.GetTrueConnectionPoint(this.modelElement[linkModelItem])))
1404                 {
1405                     viewStateKey = TrueConnectorViewStateKey;
1406                 }
1407                 else
1408                 {
1409                     viewStateKey = FalseConnectorViewStateKey;
1410                 }
1411             }
1412             else if (typeof(IFlowSwitchLink).IsAssignableFrom(linkModelItem.ItemType))
1413             {
1414                 IFlowSwitchLink link = (IFlowSwitchLink)linkModelItem.GetCurrentValue();
1415                 if (link.IsDefaultCase)
1416                 {
1417                     viewStateKey = FlowSwitchDefaultViewStateKey;
1418                 }
1419                 else
1420                 {
1421                     viewStateKey = link.CaseName + CaseViewStateKeyAppendString;
1422                 }
1423             }
1424             return viewStateKey;
1425         }
1426
1427         ModelItem GetConnectorViewStateStorageModelItem(ModelItem linkModelItem)
1428         {
1429             ModelItem storageModelItem = linkModelItem;
1430             if (typeof(IFlowSwitchLink).IsAssignableFrom(linkModelItem.ItemType))
1431             {
1432                 IFlowSwitchLink link = (IFlowSwitchLink)linkModelItem.GetCurrentValue();
1433                 //Getting FlowSwitch ModelItem since there is no CFx object for linkModelItem.
1434                 IModelTreeItem modelTreeItem = this.ModelItem as IModelTreeItem;
1435                 storageModelItem = modelTreeItem.ModelTreeManager.WrapAsModelItem(link.ParentFlowSwitch);
1436             }
1437             return storageModelItem;
1438         }
1439
1440
1441         //Save the shape location on the CFx object for serialization and viewstate maintenance.
1442         void StoreShapeViewState(UIElement movedElement, Point newLocation)
1443         {
1444             ModelItem storageModelItem;
1445             if (movedElement is StartSymbol)
1446             {
1447                 storageModelItem = this.ModelItem;
1448             }
1449             else
1450             {
1451                 ModelItem model = ((VirtualizedContainerService.VirtualizingContainer)movedElement).ModelItem;
1452                 storageModelItem = GetFlowElementMI(model);
1453             }
1454             StoreShapeViewState(storageModelItem, newLocation);
1455         }
1456
1457         void StoreShapeViewState(ModelItem storageModelItem, Point newLocation)
1458         {
1459             if (this.ViewStateService.RetrieveViewState(storageModelItem, shapeLocation) != null)
1460             {
1461                 this.ViewStateService.StoreViewStateWithUndo(storageModelItem, shapeLocation, newLocation);
1462             }
1463             else
1464             {
1465                 this.ViewStateService.StoreViewState(storageModelItem, shapeLocation, newLocation);
1466             }
1467         }
1468
1469         void PerformInternalMove(UIElement movedElement, Point newPoint, Point? shapeAnchorPoint,
1470             AutoConnectDirections autoConnectDirection, Connector connectorToSplit)
1471         {
1472             using (EditingScope es = (EditingScope)this.ModelItem.BeginEdit(SR.FCLinkMove))
1473             {
1474                 RemoveAdorner(movedElement, typeof(ConnectionPointsAdorner));
1475                 Point shapeLocation;
1476                 Size size = FreeFormPanel.GetChildSize(movedElement);
1477                 if (autoConnectDirection != AutoConnectDirections.None)
1478                 {
1479                     shapeLocation = this.CalculateDropLocationForAutoConnect(autoConnectDirection, size);
1480                 }
1481                 else if (shapeAnchorPoint.HasValue)
1482                 {
1483                     shapeLocation = SnapVisualToGrid(movedElement, newPoint, shapeAnchorPoint.Value, true);
1484                 }
1485                 else
1486                 {
1487                     Fx.Assert(newPoint.X.IsNoLessThan(0) && newPoint.Y.IsNoLessThan(0),
1488                         "newPoint is negative");
1489                     shapeLocation = newPoint;
1490                 }
1491                 if (connectorToSplit != null)
1492                 {
1493                     shapeLocation = this.CalculateDropLocationForAutoSplit(newPoint, shapeLocation, connectorToSplit, size);
1494                 }
1495                 StoreShapeViewState(movedElement, shapeLocation);
1496                 RerouteAttachedConnectors(movedElement);
1497                 es.Complete();
1498             }
1499         }
1500
1501         void RerouteAttachedConnectors(UIElement movedElement)
1502         {
1503             foreach (Connector connector in GetAttachedConnectors(movedElement))
1504             {
1505                 Reroute(connector, true);
1506             }
1507         }
1508
1509         void Reroute(Connector connector, bool withUndo)
1510         {
1511             ConnectionPoint source = FreeFormPanel.GetSourceConnectionPoint(connector);
1512             ConnectionPoint destination = FreeFormPanel.GetDestinationConnectionPoint(connector);
1513
1514             //Nulling out the PointCollection so that it doesn't interfere in line routing.
1515             connector.Points = new PointCollection();
1516             PointCollection viewState = new PointCollection(ConnectorRouter.Route(this.panel, source, destination));
1517             StoreConnectorViewState(FlowchartDesigner.GetLinkModelItem(connector), viewState, source, withUndo);
1518         }
1519
1520
1521
1522         // Returns the last dropped item - used for auto-connect and auto-split where only one item is allowed
1523         ModelItem DoFlowchartGridDrop(DragEventArgs e, AutoConnectDirections autoConnectDirection, Connector connectorToSplit)
1524         {
1525             ModelItem droppedModelItem = null;
1526             ModelItem newFlowStepMI = null;
1527             e.Effects = DragDropEffects.None;
1528             IEnumerable<object> droppedObjects = DragDropHelper.GetDroppedObjects(this, e, Context);
1529             //Marking the event as being handled. In whichever case we want to route the event, it will be unmarked explicitly.
1530             e.Handled = true;
1531             List<WorkflowViewElement> movedViewElements = new List<WorkflowViewElement>();
1532             ShapeOffsetter shapeOffsetter = new ShapeOffsetter();
1533             Dictionary<WorkflowViewElement, Point> relativeLocations = DragDropHelper.GetDraggedViewElementRelativeLocations(e);
1534             ModelItem modelItemDroppedFromToolBox = null;
1535             Dictionary<object, FlowNode> objToNewFlowNodeMap = null;
1536             Dictionary<FlowNode, ModelItem> flowNodeModelItemMap = null;
1537             Dictionary<FlowNode, FlowNode> oldNewFlowNodeMap = null;
1538             this.PrepareForDrop(droppedObjects,
1539                 out objToNewFlowNodeMap,
1540                 out flowNodeModelItemMap,
1541                 out oldNewFlowNodeMap);
1542             bool shouldStoreCurrentSizeViewState = true;
1543             foreach (object droppedObject in droppedObjects)
1544             {
1545                 if (droppedObject == null)
1546                 {
1547                     continue;
1548                 }
1549                 droppedModelItem = droppedObject as ModelItem;
1550
1551                 // archor point
1552                 Point anchorPoint = DragDropHelper.GetDragDropAnchorPoint(e);
1553
1554
1555                 ICompositeView srcContainer = droppedModelItem != null
1556                     ? DragDropHelper.GetCompositeView(droppedModelItem.View as WorkflowViewElement) as ICompositeView
1557                     : null;
1558                 bool keepRelativePosition = srcContainer is FlowchartDesigner;
1559                 // This is the case of dragging from toolbox
1560                 if (anchorPoint.X < 0 && anchorPoint.Y < 0)
1561                 {
1562                     keepRelativePosition = false;
1563                 }
1564
1565                 // This is the case of dragging from the designer surface
1566                 else if (droppedModelItem != null)
1567                 {
1568                     WorkflowViewElement view = (WorkflowViewElement)droppedModelItem.View;
1569                     anchorPoint.Offset(-relativeLocations[view].X, -relativeLocations[view].Y);
1570                 }
1571
1572
1573                 if (droppedModelItem != null && srcContainer != null && srcContainer.Equals(this))
1574                 {
1575                     if (shouldStoreCurrentSizeViewState)
1576                     {
1577                         // Moving may change the size of flowchart; need this to undo the size change.
1578                         this.StoreCurrentSizeViewStateWithUndo();
1579                         shouldStoreCurrentSizeViewState = false;
1580                     }
1581                     //InternalMove
1582                     PerformInternalMove(modelElement[droppedModelItem], e.GetPosition(this.panel), anchorPoint, autoConnectDirection, connectorToSplit);
1583                 }
1584                 else
1585                 {
1586                     //External model Item drop.
1587                     if (droppedModelItem != null)
1588                     {
1589                         if ((IsFlowStepAction(droppedModelItem)
1590                             || IsFlowNode(droppedModelItem))
1591                             && !IsParentOf(droppedModelItem, this.ModelItem))
1592                         {
1593                             if (shouldStoreCurrentSizeViewState)
1594                             {
1595                                 // Drop may change the size of flowchart; need this to undo the size change.
1596                                 this.StoreCurrentSizeViewStateWithUndo();
1597                                 shouldStoreCurrentSizeViewState = false;
1598                             }
1599
1600                             FlowNode flowElement = objToNewFlowNodeMap[droppedObject];
1601                             ModelItem flowElementMI;
1602                             if (flowNodeModelItemMap.TryGetValue(flowElement, out flowElementMI))
1603                             {
1604                                 // FlowNode comes from some other flowchart. 
1605                                 this.ModelItem.Properties["Nodes"].Collection.Add(flowElementMI);
1606                             }
1607                             else
1608                             {
1609                                 // FlowNode is a new created one, which means this is an Activity dragged
1610                                 // from somewhere else, outside of Flowchart.
1611                                 flowElementMI = this.ModelItem.Properties["Nodes"].Collection.Add(flowElement);
1612                                 flowNodeModelItemMap[flowElement] = flowElementMI;
1613                             }
1614                             newFlowStepMI = flowElementMI;
1615                         }
1616                         else
1617                         {
1618                             //We want to route the event in the case that the flowchart is dropped upon itself.
1619                             if (droppedModelItem.Equals(this.ModelItem))
1620                             {
1621                                 e.Handled = false;
1622                             }
1623                             //Don't add anything for what is neither a Activity nor a flowlink.
1624                             continue;
1625                         }
1626
1627                         if (droppedModelItem != null && droppedModelItem.View != null)
1628                         {
1629                             movedViewElements.Add((WorkflowViewElement)droppedModelItem.View);
1630                         }
1631
1632                         // the external item may come from other panel (sequence) which is already given
1633                         // a size by its previous layout panel.  That might give an inaccurate size to the
1634                         // dropped object (i.e. Bug 198290).  Therefore, when the object is dropped externally
1635                         // the FC should erases its previous hint size, forcing the FC to recompute an appropriate
1636                         // size based on the workflowelementview size.
1637                         VirtualizedContainerService.SetHintSize(droppedModelItem.GetCurrentValue(), null);
1638                     }
1639                     //Tool box drop.
1640                     else
1641                     {
1642                         if (typeof(Activity).IsAssignableFrom(droppedObject.GetType()))
1643                         {
1644                             FlowStep flowStep = new FlowStep();
1645                             flowStep.Action = (Activity)droppedObject;
1646                             if (shouldStoreCurrentSizeViewState)
1647                             {
1648                                 // Drop may change the size of flowchart; need this to undo the size change.
1649                                 this.StoreCurrentSizeViewStateWithUndo();
1650                                 shouldStoreCurrentSizeViewState = false;
1651                             }
1652
1653                             newFlowStepMI = this.ModelItem.Properties["Nodes"].Collection.Add(flowStep);
1654                             droppedModelItem = newFlowStepMI.Properties["Action"].Value;
1655                         }
1656                         else if (typeof(FlowNode).IsAssignableFrom(droppedObject.GetType()))
1657                         {
1658                             if (shouldStoreCurrentSizeViewState)
1659                             {
1660                                 // Drop may change the size of flowchart; need this to undo the size change.
1661                                 this.StoreCurrentSizeViewStateWithUndo();
1662                                 shouldStoreCurrentSizeViewState = false;
1663                             }
1664                             droppedModelItem = this.ModelItem.Properties["Nodes"].Collection.Add(droppedObject);
1665                             newFlowStepMI = droppedModelItem;
1666                         }
1667
1668                         // Now,  toolbox drop doesn't support multiple drop
1669                         // If multi-drop from tool box, use an array here.
1670                         modelItemDroppedFromToolBox = droppedModelItem;
1671                         keepRelativePosition = false;
1672                     } // tool box 
1673
1674                     WorkflowViewElement view = droppedModelItem.View as WorkflowViewElement;
1675                     if (view == null || view.ExpandState)
1676                     {
1677                         //Creating a new view to get the size of collapsed view.
1678                         view = this.ViewService.GetView(droppedModelItem) as WorkflowViewElement;
1679                         ViewUtilities.MeasureView(view, true);
1680                     }
1681
1682                     if (view != null)
1683                     {
1684                         PostDropUpdateViewState(view,
1685                             newFlowStepMI,
1686                             autoConnectDirection,
1687                             connectorToSplit,
1688                             e.GetPosition(this.panel),
1689                             anchorPoint,
1690                             keepRelativePosition,
1691                             shapeOffsetter);
1692                     }
1693                 } // external move
1694             } // foreach
1695
1696             // Remap references.
1697             // The re-map here is different from the remaping in copy/paste.
1698             // In copy paste, all the values are copied. but here, some value are
1699             // set by Properties["key"].SetValue().
1700             // Don't move this into PrepareMove. Some value setting is added to 
1701             // Change. So the operation here will decide the order of Change.Apply().
1702             // PropertyChange in some case, must happen after ModelItem is moved to 
1703             // new places.
1704             foreach (FlowNode flowNode in oldNewFlowNodeMap.Keys)
1705             {
1706                 UpdateCloneReferenceByModelItem(flowNode, flowNodeModelItemMap, oldNewFlowNodeMap);
1707             }
1708
1709             DragDropHelper.SetDragDropMovedViewElements(e, movedViewElements);
1710             
1711             //Backward compatibility for 4.0
1712             if (droppedObjects.Count() == 1 && movedViewElements.Count == 1)
1713             {
1714                 #pragma warning disable 618
1715                 DragDropHelper.SetDragDropCompletedEffects(e, DragDropEffects.Move);
1716                 #pragma warning restore 618
1717             }
1718
1719             if (modelItemDroppedFromToolBox != null)
1720             {
1721                 // if it is dropped from toolbox, select
1722                 this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)(() =>
1723                 {
1724                     UIElement view = (UIElement)(modelItemDroppedFromToolBox.View);
1725                     if (view != null)
1726                     {
1727                         Keyboard.Focus(view);
1728                         Selection.SelectOnly(this.Context, modelItemDroppedFromToolBox);
1729                     }
1730                 }));
1731             }
1732
1733             if (droppedModelItem != null)
1734             {
1735                 if (IsFlowNode(droppedModelItem))
1736                 {
1737                     return droppedModelItem;
1738                 }
1739                 else if (IsFlowStepAction(droppedModelItem))
1740                 {
1741                     if (newFlowStepMI != null)
1742                     {
1743                         return newFlowStepMI;
1744                     }
1745                     else
1746                     {
1747                         return this.GetParentFlowStepModelItem(droppedModelItem);
1748                     }
1749                 }
1750                 return null;
1751             }
1752
1753             return null;
1754         }
1755
1756         Point CalculateDropLocationForAutoConnect(AutoConnectDirections autoConnectDirection, Size droppedSize)
1757         {
1758             return AutoConnectHelper.CalculateDropLocation(droppedSize, this.panel.CurrentAutoConnectTarget, autoConnectDirection, this.shapeLocations);
1759         }
1760
1761         Point CalculateDropLocationForAutoSplit(Point mousePosition, Point originalDropLocation, Connector connector, Size droppedSize)
1762         {
1763             return AutoSplitHelper.CalculateDropLocation(mousePosition, originalDropLocation, connector, droppedSize, this.shapeLocations);
1764         }
1765
1766         private void OnFlowchartGridDrop(object sender, DragEventArgs e)
1767         {
1768             ModelItemHelper.TryCreateImmediateEditingScopeAndExecute(this.ModelItem.GetEditingContext(), System.Activities.Presentation.SR.CollectionAddEditingScopeDescription, (es) =>
1769                 {
1770                     this.DoFlowchartGridDrop(e, AutoConnectDirections.None, null);
1771                     if (es != null)
1772                     {
1773                         es.Complete();
1774                     }
1775                 });
1776         }
1777
1778         // Prepare some maps for drag/drop in Flowchart
1779         // objToNewFlowNodeMap : 
1780         //    <DroppedObject, FlowNodeToBeDropInDestFlowchart>
1781         // flowNodeNewModelItemMap : 
1782         //    <OldFlowNode, NewModelItemInDestFlowchart>
1783         //    For FlowSwitch/FlowDecision: NewModelItemINDestFlowchart is the source ModelItem.
1784         //    For FlowStep, leeve NewModeItemInDestFlowchart Null, this value could not be decided here.
1785         // flowNodeMovingMap: 
1786         //    <OldFlowNode, NewFlowNode>
1787         //    If a droppedObject is not a flownode, say it is from tool box, this value is empty.
1788         private void PrepareForDrop(IEnumerable<object> objects,
1789                 out Dictionary<object, FlowNode> objToNewFlowNodeMap,
1790                 out Dictionary<FlowNode, ModelItem> flowNodeNewModelItemMap,
1791                 out Dictionary<FlowNode, FlowNode> oldNewFlowNodeMap)
1792         {
1793             Fx.Assert(objects != null, "dropping null objects");
1794             objToNewFlowNodeMap = new Dictionary<object, FlowNode>();
1795             oldNewFlowNodeMap = new Dictionary<FlowNode, FlowNode>();
1796             flowNodeNewModelItemMap = new Dictionary<FlowNode, ModelItem>();
1797             // 1) Get flow node and Composite view
1798             foreach (object obj in objects)
1799             {
1800                 if (obj == null)
1801                 {
1802                     Fx.Assert("obj == null");
1803                     continue;
1804                 }
1805
1806                 objToNewFlowNodeMap[obj] = null;
1807                 ModelItem modelItem = obj as ModelItem;
1808                 if (modelItem == null)
1809                 {
1810                     // if not a model item, return
1811                     continue;
1812                 }
1813
1814                 ICompositeView compositeView = DragDropHelper.GetCompositeView(modelItem.View as WorkflowViewElement) as ICompositeView;
1815                 if (compositeView == null)
1816                 {
1817                     continue;
1818                 }
1819
1820                 // This means a internal move, no extra thing needed.
1821                 if (compositeView.Equals(this))
1822                 {
1823                     // internal move.
1824                     continue;
1825                 }
1826
1827                 if (!IsFlowStepAction(modelItem)
1828                     && !IsFlowNode(modelItem))
1829                 {
1830
1831                     Fx.Assert(modelItem.ItemType.Equals(typeof(StartNode)),
1832                         "should not happen. Not a Activity, FlowNode or StartSymbol");
1833                     // don't do anything if the dropped object is either an Activity or
1834                     // Flownode.
1835                     continue;
1836                 }
1837
1838                 FlowNode flowNode = null;
1839                 FlowchartDesigner fcDesigner = compositeView as FlowchartDesigner;
1840                 if (fcDesigner != null)
1841                 {
1842                     // If the source view is a FlowchartDesigner, we need to do some to prepare
1843                     // the remap.
1844                     ModelItem flowElementMI = fcDesigner.GetFlowElementMI(modelItem);
1845                     Fx.Assert(flowElementMI != null, "flowElementMI != null");
1846                     FlowNode oldFlowNode = flowElementMI.GetCurrentValue() as FlowNode;
1847                     Fx.Assert(oldFlowNode != null, "oldFlowNode != null");
1848                     flowNode = oldFlowNode;
1849                     flowNodeNewModelItemMap[oldFlowNode] = flowElementMI;
1850                     oldNewFlowNodeMap[oldFlowNode] = flowNode;
1851                 }
1852                 else
1853                 {
1854                     // The object is moved from somewhere else, say Sequence. 
1855                     // We create a FlowStep for it.
1856                     FlowStep flowStep = new FlowStep();
1857                     flowStep.Action = (Activity)modelItem.GetCurrentValue();
1858                     flowNode = flowStep;
1859                 }
1860                 objToNewFlowNodeMap[obj] = flowNode;
1861             }
1862         }
1863
1864         // Move the object to correct position after drop
1865         private void PostDropUpdateViewState(WorkflowViewElement view,
1866             ModelItem flownodeMI,
1867             AutoConnectDirections autoConnectDirection,
1868             Connector connectorToSplit,
1869             Point newPoint,
1870             Point anchorPoint,
1871             bool keepRelativePosition,
1872             ShapeOffsetter shapeOffsetter)
1873         {
1874             Fx.Assert((view != null && flownodeMI != null),
1875             "movedItem != null && flownodeMI != null");
1876             Point shapeLocationPtr;
1877             if (autoConnectDirection != AutoConnectDirections.None)
1878             {
1879                 shapeLocationPtr = this.CalculateDropLocationForAutoConnect(autoConnectDirection, view.DesiredSize);
1880             }
1881             else
1882             {
1883                 shapeLocationPtr = SnapVisualToGrid(view, newPoint, anchorPoint, keepRelativePosition);
1884                 if (!keepRelativePosition)
1885                 {
1886                     // To avoid overlaps
1887                     shapeLocationPtr = shapeOffsetter.OffsetShapeLocation(shapeLocationPtr);
1888                 }
1889             }
1890
1891             if (connectorToSplit != null)
1892             {
1893                 shapeLocationPtr = this.CalculateDropLocationForAutoSplit(newPoint, shapeLocationPtr, connectorToSplit, view.DesiredSize);
1894             }
1895
1896             // 
1897             if (keepRelativePosition)
1898             {
1899                 this.OffsetDroppedItemToNewPosition(flownodeMI, shapeLocationPtr);
1900             }
1901             else
1902             {
1903                 this.StoreShapeViewState(flownodeMI, shapeLocationPtr);
1904             }
1905         }
1906
1907         private void OffsetDroppedItemToNewPosition(ModelItem flownodeMI, Point newLocationPtr)
1908         {
1909             object locationOfShape = this.ViewStateService.RetrieveViewState(flownodeMI, shapeLocation);
1910             if (locationOfShape == null)
1911             {
1912                 return;
1913             }
1914             Point oldLocationPoint = (Point)locationOfShape;
1915             Vector offset = newLocationPtr - oldLocationPoint;
1916             this.OffSetViewState(offset, flownodeMI, true);
1917         }
1918
1919         private void OnFlowchartGridDragEnter(object sender, DragEventArgs e)
1920         {
1921             OnFlowchartGridDrag(sender, e);
1922         }
1923
1924         private void OnFlowchartGridDragOver(object sender, DragEventArgs e)
1925         {
1926             OnFlowchartGridDrag(sender, e);
1927         }
1928
1929         private bool IsDropAllowed(DragEventArgs e)
1930         {
1931             return DragDropHelper.AllowDrop(e.Data, this.Context, typeof(Activity), typeof(FlowNode), typeof(StartNode));
1932         }
1933
1934         private void OnFlowchartGridDrag(object sender, DragEventArgs e)
1935         {
1936             if (!e.Handled)
1937             {
1938                 if (!this.IsDropAllowed(e))
1939                 {
1940                     e.Effects = DragDropEffects.None;
1941                 }
1942                 e.Handled = true;
1943             }
1944         }
1945
1946         static bool IsLinkModelItemSelectable(ModelItem linkModelItem)
1947         {
1948             return linkModelItem != null &&
1949                 // link from FlowDecision
1950                    !typeof(FlowDecision).IsAssignableFrom(linkModelItem.ItemType) &&
1951                 // link from StartNode
1952                    !typeof(Flowchart).IsAssignableFrom(linkModelItem.ItemType);
1953         }
1954
1955         private void OnFlowchartGridKeyDown(object sender, KeyEventArgs e)
1956         {
1957             if (srcConnectionPoint != null)
1958             {
1959                 // Ignore KeyBoard input when creating connector.
1960                 e.Handled = true;
1961                 return;
1962             }
1963
1964             Selection currentSelection = this.Context.Items.GetValue<Selection>();
1965             if (e.Key == Key.Delete && this.selectedConnector != null && currentSelection.SelectionCount <= 1)
1966             {
1967                 // process the delete if only the connector is selected
1968                 ModelItem primarySelection = currentSelection.PrimarySelection;
1969                 //Delete connector
1970                 ModelItem linkModelItem = FlowchartDesigner.GetLinkModelItem(this.selectedConnector);
1971                 if ((primarySelection == null && !IsLinkModelItemSelectable(linkModelItem)) ||
1972                     object.Equals(primarySelection, linkModelItem))
1973                 {
1974                     DeleteLink(this.selectedConnector);
1975                     this.selectedConnector = null;
1976                     e.Handled = true;
1977                 }
1978             }
1979             else if ((new List<Key> { Key.Left, Key.Right, Key.Up, Key.Down }).Contains(e.Key)
1980                 && currentSelection.SelectedObjects.All<ModelItem>((p) => { return this.modelElement.ContainsKey(p); }))
1981             {
1982                 KeyboardMove(e.Key);
1983                 e.Handled = true;
1984             }
1985         }
1986
1987         private void FlowchartDesignerKeyDown(object sender, KeyEventArgs e)
1988         {
1989             // Ignore KeyBoard input when in resizing mode.
1990             e.Handled = IsResizing;
1991         }
1992
1993         private void FlowchartDesignerPreviewKeyDown(object sender, KeyEventArgs e)
1994         {
1995             // Enter cannot be captured in KeyDown, so handle it in PreviewKeyDown event.
1996             e.Handled = IsResizing && e.Key == Key.Enter;
1997         }
1998
1999         void KeyboardMove(Key key)
2000         {
2001             Vector moveDir = FreeFormPanel.CalculateMovement(key, this.IsRightToLeft);
2002
2003             using (EditingScope es = (EditingScope)this.ModelItem.BeginEdit(SR.ItemMove))
2004             {
2005                 bool shouldStoreCurrentSizeViewState = true;
2006                 foreach (ModelItem selectedModelItem in this.Context.Items.GetValue<Selection>().SelectedObjects)
2007                 {
2008                     UIElement shapeToMove = this.modelElement[selectedModelItem];
2009                     Point currentLocation = FreeFormPanel.GetLocation(shapeToMove);
2010                     Point newLocation = Point.Add(currentLocation, moveDir);
2011
2012                     // Make sure the newLocation is positive.
2013                     newLocation.X = FreeFormPanel.ZeroIfNegative(newLocation.X);
2014                     newLocation.Y = FreeFormPanel.ZeroIfNegative(newLocation.Y);
2015
2016                     if (newLocation == currentLocation)
2017                     {
2018                         continue;
2019                     }
2020
2021                     if (shouldStoreCurrentSizeViewState)
2022                     {
2023                         // Moving may change the size of flowchart; need this to undo the size change.
2024                         this.StoreCurrentSizeViewStateWithUndo();
2025                         shouldStoreCurrentSizeViewState = false;
2026                     }
2027                     PerformInternalMove(shapeToMove, newLocation, null, AutoConnectDirections.None, null);
2028                 }
2029                 es.Complete();
2030             }
2031         }
2032
2033         private void OnFlowchartGridPreviewMouseDown(object sender, MouseButtonEventArgs e)
2034         {
2035             this.selectedConnector = null;
2036         }
2037
2038         private void OnFlowchartGridPreviewMouseUp(object sender, MouseButtonEventArgs e)
2039         {
2040             if (this.srcConnectionPoint != null)
2041             {
2042                 UIElement destElement = VisualTreeUtils.FindVisualAncestor<VirtualizedContainerService.VirtualizingContainer>(e.OriginalSource as DependencyObject);
2043                 if (destElement != null && this.panel.Children.Contains(destElement))
2044                 {
2045                     string errorMessage = string.Empty;
2046                     Point mouseUpLocation = e.GetPosition(sender as IInputElement);
2047
2048                     if (!CreateLinkGesture(this.srcConnectionPoint, destElement, mouseUpLocation, out errorMessage, false, null) && !errorMessage.Equals(string.Empty))
2049                     {
2050                         ErrorReporting.ShowErrorMessage(errorMessage);
2051                     }
2052                     this.srcConnectionPoint = null;
2053                     RemoveAdorner(this.panel, typeof(ConnectorCreationAdorner));
2054                     RemoveAdorner(destElement, typeof(FlowchartConnectionPointsAdorner));
2055                     // Simulate a MouseEnter to show connection points
2056                     ChildElement_MouseEnter(destElement, null);
2057                 }
2058             }
2059         }
2060
2061         enum ConnectorType
2062         {
2063             Default = 0, ErrorConnector = 1
2064         };
2065
2066         void OnConnectNodesCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
2067         {
2068             e.CanExecute = false;
2069             Selection selection = this.Context.Items.GetValue<Selection>();
2070             if (selection.SelectionCount > 1)
2071             {
2072                 e.CanExecute = true;
2073                 foreach (ModelItem item in selection.SelectedObjects)
2074                 {
2075                     if (!this.modelElement.ContainsKey(item))
2076                     {
2077                         e.CanExecute = false;
2078                         break;
2079                     }
2080                 }
2081             }
2082             e.Handled = true;
2083         }
2084
2085         void OnConnectNodesCommandExecuted(object sender, ExecutedRoutedEventArgs e)
2086         {
2087             using (EditingScope es = (EditingScope)this.ModelItem.BeginEdit(SR.FCCreateLink))
2088             {
2089                 List<ModelItem> selectedFlowchartItems = new List<ModelItem>(this.Context.Items.GetValue<Selection>().SelectedObjects);
2090                 selectedFlowchartItems.Reverse();
2091                 CreateLinks(selectedFlowchartItems);
2092                 es.Complete();
2093             }
2094         }
2095
2096         void OnShowAllConditionsCommandExecuted(object sender, ExecutedRoutedEventArgs e)
2097         {
2098             this.ShowAllConditions = false;
2099             this.ShowAllConditions = true;
2100         }
2101
2102         void OnShowAllConditionsCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
2103         {
2104             e.CanExecute = true;
2105             e.Handled = true;
2106         }
2107
2108         void OnHideAllConditionsCommandExecuted(object sender, ExecutedRoutedEventArgs e)
2109         {
2110             this.ShowAllConditions = true;
2111             this.ShowAllConditions = false;
2112         }
2113
2114         void OnHideAllConditionsCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
2115         {
2116             e.CanExecute = true;
2117             e.Handled = true;
2118         }
2119
2120         internal static void DropActivityBelow(ViewStateService viewStateService, ModelItem modelItem, Activity activity, double interval)
2121         {
2122             // Extracting information
2123             ModelItem flowStep = modelItem.Parent;
2124             ModelItemCollection nodes = flowStep.Parent as ModelItemCollection;
2125             ModelItem flowchart = nodes.Parent;
2126             FlowchartDesigner flowchartDesigner = ((FlowchartDesigner)flowchart.View);
2127
2128             // Creating FlowStep ModelItem
2129             ModelTreeManager modelTreeManager = (modelItem as IModelTreeItem).ModelTreeManager;
2130             FlowStep step = new FlowStep() { Action = activity };
2131             ModelItem activityModelItem = modelTreeManager.WrapAsModelItem(step);
2132
2133             // Compute the 'correct' location
2134             Point point = (Point)viewStateService.RetrieveViewState(flowStep, shapeLocation);
2135             point.Y += (((WorkflowViewElement)modelItem.View).ActualHeight + interval);
2136             viewStateService.StoreViewState(activityModelItem, shapeLocation, point);
2137             flowchartDesigner.UpdateViewStateToAvoidOverlapOnPaste(new List<ModelItem> { activityModelItem });
2138
2139             // Add it to the model tree
2140             nodes.Add(activityModelItem);
2141         }
2142
2143         void OnAdornerDecoratorLoaded(object sender, RoutedEventArgs e)
2144         {
2145             this.adornerLayer = ((AdornerDecorator)sender).AdornerLayer;
2146             // This might not be the best event to handle, ideally we would like to have the event when the list of adorner changes.
2147             this.adornerLayer.LayoutUpdated += new EventHandler(OnAdornerLayerLayoutUpdated);
2148         }
2149
2150         void OnAdornerLayerLayoutUpdated(object sender, EventArgs e)
2151         {
2152             // Extract the set of all adorners
2153             List<Adorner> adornerList = new List<Adorner>();
2154             foreach (object logicalChild in LogicalTreeHelper.GetChildren(adornerLayer))
2155             {
2156                 Fx.Assert(logicalChild is Adorner, "What else could an adornerLayer hold?");
2157                 adornerList.Add((Adorner)logicalChild);
2158             }
2159             Adorner[] adorners = adornerList.ToArray();
2160             if (FlowchartDesigner.Pack(adorners, (adorner => adorner is FlowchartExpressionAdorner)))
2161             {
2162                 foreach (Adorner adorner in adorners)
2163                 {
2164                     adornerLayer.Remove(adorner);
2165                 }
2166                 foreach (Adorner adorner in adorners)
2167                 {
2168                     adornerLayer.Add(adorner);
2169                 }
2170             }
2171         }
2172
2173         // do not proprogate up to FlowchartDesigner, because designer will set selection to itself on GotFocus event.
2174         private void OnAdornerLayerGotFocus(object sender, RoutedEventArgs e)
2175         {
2176             e.Handled = true;
2177         }
2178
2179         // Within the connection points with least number of connectors, get the one closest to the midpoint.
2180         private static ConnectionPoint GetConnectionPointForAutoConnect(List<ConnectionPoint> availableConnectionPoints)
2181         {
2182             int minConnectorCount = availableConnectionPoints.Min<ConnectionPoint>((p) =>
2183                 {
2184                     return p.AttachedConnectors.Count;
2185                 });
2186
2187             List<ConnectionPoint> connectionPoints = new List<ConnectionPoint>(availableConnectionPoints.Where<ConnectionPoint>((p) =>
2188                 {
2189                     return p.AttachedConnectors.Count == minConnectorCount;
2190                 }));
2191
2192             ConnectionPoint midPoint = availableConnectionPoints[availableConnectionPoints.Count / 2];
2193             if (connectionPoints.Contains(midPoint))
2194             {
2195                 return midPoint;
2196             }
2197             double dist;
2198             return ConnectionPoint.GetClosestConnectionPoint(connectionPoints, midPoint.Location, out dist);
2199         }
2200
2201         private ConnectionPoint GetSourceConnectionPointForAutoConnect(UIElement designer, EdgeLocation edgeLocation)
2202         {
2203             List<ConnectionPoint> connectionPoints = FlowchartDesigner.GetAllConnectionPoints(designer);
2204             connectionPoints = new List<ConnectionPoint>(connectionPoints.Where<ConnectionPoint>((p) =>
2205             {
2206                 return p != null && p.PointType != ConnectionPointKind.Incoming && p.EdgeLocation == edgeLocation;
2207             }));
2208             Fx.Assert(connectionPoints.Count > 0, "There should be at least one src connection point available");
2209             return FlowchartDesigner.GetConnectionPointForAutoConnect(connectionPoints);
2210         }
2211
2212         internal static ConnectionPoint GetDestinationConnectionPointForAutoConnect(UIElement dest, ConnectionPoint srcConnPoint)
2213         {
2214             EdgeLocation destEdgeLocation = EdgeLocation.Top;
2215             if (!((dest is VirtualizedContainerService.VirtualizingContainer) && ((VirtualizedContainerService.VirtualizingContainer)dest).ModelItem.ItemType == typeof(FlowDecision)))
2216             {
2217                 switch (srcConnPoint.EdgeLocation)
2218                 {
2219                     case EdgeLocation.Top:
2220                         destEdgeLocation = EdgeLocation.Bottom;
2221                         break;
2222                     case EdgeLocation.Bottom:
2223                         destEdgeLocation = EdgeLocation.Top;
2224                         break;
2225                     case EdgeLocation.Left:
2226                         destEdgeLocation = EdgeLocation.Right;
2227                         break;
2228                     case EdgeLocation.Right:
2229                         destEdgeLocation = EdgeLocation.Left;
2230                         break;
2231                 }
2232             }
2233             List<ConnectionPoint> destConnectionPoints = new List<ConnectionPoint>(FlowchartDesigner.GetConnectionPoints(dest).Where<ConnectionPoint>((p) =>
2234             {
2235                 return p.PointType != ConnectionPointKind.Outgoing && p.EdgeLocation == destEdgeLocation;
2236             }));
2237             Fx.Assert(destConnectionPoints.Count > 0, "There should be at least one dest connection point available");
2238             return FlowchartDesigner.GetConnectionPointForAutoConnect(destConnectionPoints);
2239         }
2240
2241         private ModelItem GetSourceModelItemForAutoConnect(UIElement sourceElement)
2242         {
2243             ModelItem sourceModelItem = null;
2244             if (sourceElement is WorkflowViewElement)
2245             {
2246                 sourceModelItem = ((WorkflowViewElement)sourceElement).ModelItem;
2247             }
2248             else if (sourceElement is VirtualizedContainerService.VirtualizingContainer)
2249             {
2250                 sourceModelItem = ((VirtualizedContainerService.VirtualizingContainer)sourceElement).ModelItem;
2251             }
2252             if (sourceModelItem != null && IsFlowStepAction(sourceModelItem))
2253             {
2254                 sourceModelItem = this.GetParentFlowStepModelItem(sourceModelItem);
2255                 Fx.Assert(typeof(FlowStep).IsAssignableFrom(sourceModelItem.ItemType), "The parent should be FlowNode");
2256             }
2257             return sourceModelItem;
2258         }
2259
2260         private ModelItem GetParentFlowStepModelItem(ModelItem activityModelItem)
2261         {
2262             foreach (ModelItem flowNodeModelItem in this.ModelItem.Properties["Nodes"].Collection)
2263             {
2264                 if (typeof(FlowStep).IsAssignableFrom(flowNodeModelItem.ItemType))
2265                 {
2266                     if (flowNodeModelItem.Properties["Action"].Value == activityModelItem)
2267                     {
2268                         return flowNodeModelItem;
2269                     }
2270                 }
2271             }
2272             return null;
2273         }
2274
2275         public void DoAutoConnect(DragEventArgs e, UIElement targetElement, AutoConnectDirections direction)
2276         {
2277             UIElement sourceElement = targetElement;
2278             bool immediatelyCommit = ModelItemHelper.CanCreateImmediateEditingScope(this.ModelItem);
2279
2280             using (EditingScope scope = (EditingScope)this.ModelItem.BeginEdit(SR.AutoConnect, immediatelyCommit))
2281             {
2282                 ModelItem droppedModelItem = this.DoFlowchartGridDrop(e, direction, null);
2283                 bool autoConnected = false;
2284                 if (droppedModelItem != null)
2285                 {
2286                     ModelItem sourceModelItem = this.GetSourceModelItemForAutoConnect(sourceElement);
2287                     if (sourceModelItem != null)
2288                     {
2289                         if (sourceModelItem.ItemType == typeof(FlowStep))
2290                         {
2291                             sourceModelItem.Properties["Next"].SetValue(droppedModelItem);
2292                             autoConnected = true;
2293                         }
2294                         else if (sourceModelItem.ItemType == typeof(FlowDecision))
2295                         {
2296                             if (direction == AutoConnectDirections.Left)
2297                             {
2298                                 sourceModelItem.Properties["True"].SetValue(droppedModelItem);
2299                                 autoConnected = true;
2300                             }
2301                             else if (direction == AutoConnectDirections.Right)
2302                             {
2303                                 sourceModelItem.Properties["False"].SetValue(droppedModelItem);
2304                                 autoConnected = true;
2305                             }
2306                         }
2307                         else if (GenericFlowSwitchHelper.IsGenericFlowSwitch(sourceModelItem.ItemType))
2308                         {
2309                             string message = string.Empty;
2310                             autoConnected = this.CreateFlowSwitchLink(this.srcConnectionPointForAutoConnect, sourceModelItem, droppedModelItem, null, null, ref message);
2311                         }
2312                         else if (sourceModelItem.ItemType == typeof(StartNode))
2313                         {
2314                             this.ModelItem.Properties["StartNode"].SetValue(droppedModelItem);
2315                             autoConnected = true;
2316                         }
2317                     }
2318                 }
2319                 if (autoConnected)
2320                 {
2321                     this.srcConnectionPointForAutoConnect = this.GetSourceConnectionPointForAutoConnect(sourceElement, AutoConnectHelper.AutoConnectDirection2EdgeLocation(direction));
2322                     scope.Complete();
2323                 }
2324                 else
2325                 {
2326                     scope.Revert();
2327                 }
2328             }
2329         }
2330
2331         public AutoConnectDirections GetDirectionsAllowed(DragEventArgs e, UIElement targetElement)
2332         {
2333             List<Type> draggedTypes = DragDropHelper.GetDraggedTypes(e.Data);
2334             if (draggedTypes.Count != 1)
2335             {
2336                 return AutoConnectDirections.None;
2337             }
2338             Type draggedType = draggedTypes[0];
2339             if (!typeof(Activity).IsAssignableFrom(draggedType) &&
2340                   !typeof(FlowNode).IsAssignableFrom(draggedType) &&
2341                   !IsActivityTemplateFactory(draggedType))
2342             {
2343                 return AutoConnectDirections.None;
2344             }
2345             ModelItem sourceModelItem = this.GetSourceModelItemForAutoConnect(targetElement);
2346             if (sourceModelItem != null)
2347             {
2348                 if (sourceModelItem.ItemType == typeof(FlowStep))
2349                 {
2350                     if (sourceModelItem.Properties["Next"].Value != null)
2351                     {
2352                         return AutoConnectDirections.None;
2353                     }
2354                 }
2355                 else if (sourceModelItem.ItemType == typeof(FlowDecision))
2356                 {
2357                     AutoConnectDirections directions = AutoConnectDirections.None;
2358                     if (sourceModelItem.Properties["True"].Value == null)
2359                     {
2360                         directions |= AutoConnectDirections.Left;
2361                     }
2362                     if (sourceModelItem.Properties["False"].Value == null)
2363                     {
2364                         directions |= AutoConnectDirections.Right;
2365                     }
2366                     return directions;
2367                 }
2368                 else if (sourceModelItem.ItemType == typeof(StartNode))
2369                 {
2370                     if (this.ModelItem.Properties["StartNode"].Value != null)
2371                     {
2372                         return AutoConnectDirections.None;
2373                     }
2374                 }
2375             }
2376             return AutoConnectDirections.Top | AutoConnectDirections.Bottom | AutoConnectDirections.Left | AutoConnectDirections.Right;
2377         }
2378
2379         public bool CanAutoSplit(DragEventArgs e)
2380         {
2381             if (!this.IsDropAllowed(e))
2382             {
2383                 return false;
2384             }
2385             ModelItem draggedModelItem = e.Data.GetData(DragDropHelper.ModelItemDataFormat) as ModelItem;
2386
2387             // The start node
2388             if (draggedModelItem is FakeModelItemImpl)
2389             {
2390                 return false;
2391             }
2392
2393             if (draggedModelItem != null && this.modelElement.ContainsKey(draggedModelItem))
2394             {
2395                 if (this.GetAttachedConnectors(this.modelElement[draggedModelItem]).Count > 0)
2396                 {
2397                     return false;
2398                 }
2399             }
2400             return true;
2401         }
2402
2403         internal static int GetConnectionPointIndex(UIElement element, ConnectionPoint pnt)
2404         {
2405             List<ConnectionPoint> list = FlowchartDesigner.GetAllConnectionPoints(element);
2406             return list.IndexOf(pnt);
2407         }
2408
2409         internal static ConnectionPoint GetConnectionPointFromIndex(UIElement element, int index)
2410         {
2411             List<ConnectionPoint> list = FlowchartDesigner.GetAllConnectionPoints(element);
2412             if (index >= 0 && index < list.Count)
2413             {
2414                 return list[index];
2415             }
2416             return null;
2417         }
2418
2419         internal UIElement GetView(ModelItem item)
2420         {
2421             if (item == this.ModelItem)
2422             {
2423                 return this.StartSymbol;
2424             }
2425             return this.modelElement[item];
2426         }
2427
2428         public void DoAutoSplit(DragEventArgs e, Connector connector)
2429         {
2430             bool immediatelyCommit = ModelItemHelper.CanCreateImmediateEditingScope(this.ModelItem);
2431
2432             using (EditingScope scope = (EditingScope)this.ModelItem.BeginEdit(SR.AutoSplit, immediatelyCommit))
2433             {
2434                 ModelItem droppedModelItem = this.DoFlowchartGridDrop(e, AutoConnectDirections.None, connector);
2435                 bool autoSplit = false;
2436                 ModelItem sourceModelItem = null;
2437                 ModelItem destinationModelItem = null;
2438                 if (droppedModelItem != null)
2439                 {
2440                     this.StoreConnectorViewState(connector, true);
2441                     IFlowSwitchLink flowSwitchLink = this.DeleteLink(connector, true);
2442
2443                     bool linkCreated = true;
2444                     string message = string.Empty;
2445                     UIElement srcDesigner = FreeFormPanel.GetSourceConnectionPoint(connector).ParentDesigner;
2446                     UIElement destDesigner = FreeFormPanel.GetDestinationConnectionPoint(connector).ParentDesigner;
2447                     if (srcDesigner is StartSymbol)
2448                     {
2449                         sourceModelItem = this.ModelItem;
2450                         this.ModelItem.Properties["StartNode"].SetValue(droppedModelItem);
2451                     }
2452                     else if (srcDesigner is VirtualizedContainerService.VirtualizingContainer)
2453                     {
2454                         sourceModelItem = ((VirtualizedContainerService.VirtualizingContainer)srcDesigner).ModelItem;
2455                         ModelItem srcFlowNodeModelItem = sourceModelItem;
2456                         if (!IsFlowNode(srcFlowNodeModelItem))
2457                         {
2458                             srcFlowNodeModelItem = this.GetParentFlowStepModelItem(srcFlowNodeModelItem);
2459                         }
2460                         Fx.Assert(IsFlowNode(srcFlowNodeModelItem), "srcFlowNodeModelItem should be a FlowNode");
2461
2462                         if (typeof(FlowStep) == srcFlowNodeModelItem.ItemType)
2463                         {
2464                             srcFlowNodeModelItem.Properties["Next"].SetValue(droppedModelItem);
2465                         }
2466                         else if (typeof(FlowDecision) == srcFlowNodeModelItem.ItemType && FreeFormPanel.GetSourceConnectionPoint(connector).Equals(GetTrueConnectionPoint(srcDesigner)))
2467                         {
2468                             srcFlowNodeModelItem.Properties["True"].SetValue(droppedModelItem);
2469                         }
2470                         else if (typeof(FlowDecision) == srcFlowNodeModelItem.ItemType && FreeFormPanel.GetSourceConnectionPoint(connector).Equals(GetFalseConnectionPoint(srcDesigner)))
2471                         {
2472                             srcFlowNodeModelItem.Properties["False"].SetValue(droppedModelItem);
2473                         }
2474                         else if (GenericFlowSwitchHelper.IsGenericFlowSwitch(srcFlowNodeModelItem.ItemType))
2475                         {
2476                             linkCreated = CreateFlowSwitchLink(FreeFormPanel.GetSourceConnectionPoint(connector), srcFlowNodeModelItem, droppedModelItem, flowSwitchLink, null, ref message);
2477                         }
2478                     }
2479
2480                     if (linkCreated && string.IsNullOrEmpty(message))
2481                     {
2482                         destinationModelItem = ((VirtualizedContainerService.VirtualizingContainer)destDesigner).ModelItem;
2483                         ModelItem destFlowNodeModelItem = destinationModelItem;
2484                         if (!IsFlowNode(destFlowNodeModelItem))
2485                         {
2486                             destFlowNodeModelItem = this.GetParentFlowStepModelItem(destFlowNodeModelItem);
2487                         }
2488
2489                         Fx.Assert(IsFlowNode(destFlowNodeModelItem), "destFlowNodeModelItem should be a FlowNode");
2490
2491                         if (droppedModelItem.ItemType == typeof(FlowStep))
2492                         {
2493                             droppedModelItem.Properties["Next"].SetValue(destFlowNodeModelItem);
2494                             autoSplit = true;
2495                         }
2496                         else if (GenericFlowSwitchHelper.IsGenericFlowSwitch(droppedModelItem.ItemType))
2497                         {
2498                             droppedModelItem.Properties["Default"].SetValue(destFlowNodeModelItem);
2499                             autoSplit = true;
2500                         }
2501                         else if (droppedModelItem.ItemType == typeof(FlowDecision))
2502                         {
2503                             droppedModelItem.Properties["True"].SetValue(destFlowNodeModelItem);
2504                             autoSplit = true;
2505                         }
2506                     }
2507                 }
2508
2509                 if (autoSplit)
2510                 {
2511                     Fx.Assert(sourceModelItem != null, "sourceModelItem != null");
2512                     Fx.Assert(destinationModelItem != null, "destinationModelItem != null");
2513
2514                     int srcIndex = GetConnectionPointIndex(this.GetView(sourceModelItem), FreeFormPanel.GetSourceConnectionPoint(connector));
2515                     int desIndex = GetConnectionPointIndex(this.GetView(destinationModelItem), FreeFormPanel.GetDestinationConnectionPoint(connector));
2516
2517                     EdgeLocation entryEdgeForAutoSplit;
2518                     EdgeLocation exitEdgeForAutoSplit;
2519                     AutoSplitHelper.CalculateEntryExitEdges(e.GetPosition(this.panel),
2520                         connector, out entryEdgeForAutoSplit, out exitEdgeForAutoSplit);
2521
2522                     FlowchartDesigner.SetAutoSplitDataWithUndo(
2523                         this.ModelItem, sourceModelItem, destinationModelItem, srcIndex, desIndex, entryEdgeForAutoSplit, exitEdgeForAutoSplit);
2524
2525                     scope.Complete();
2526                 }
2527                 else
2528                 {
2529                     scope.Revert();
2530                 }
2531             }
2532         }
2533
2534         private ConnectionPoint GetEmptyEdgeMidConnectionPointNotOfType(UIElement designer, EdgeLocation edgeLocation, ConnectionPointKind invalidType)
2535         {
2536             List<ConnectionPoint> connectionPoints = FlowchartDesigner.GetAllConnectionPoints(designer);
2537             connectionPoints = new List<ConnectionPoint>(connectionPoints.Where<ConnectionPoint>((p) =>
2538             {
2539                 return p != null && p.PointType != invalidType && p.AttachedConnectors.Count == 0 && p.EdgeLocation == edgeLocation;
2540             }));
2541
2542             if (connectionPoints.Count > 0)
2543             {
2544                 return connectionPoints[connectionPoints.Count / 2];
2545             }
2546
2547             return null;
2548         }
2549
2550         private ConnectionPoint GetDestinationConnectionPointForAutoSplit(ConnectionPoint srcConnPoint, UIElement destDesigner)
2551         {
2552             this.MeasureView(destDesigner);
2553             ConnectionPoint point = this.GetEmptyEdgeMidConnectionPointNotOfType(destDesigner, this.entryEdgeForAutoSplit, ConnectionPointKind.Outgoing);
2554             if (point == null)
2555             {
2556                 point = this.FindClosestConnectionPointNotOfType(srcConnPoint, new List<ConnectionPoint>(FlowchartDesigner.GetConnectionPoints(destDesigner).Where<ConnectionPoint>(p =>
2557                 {
2558                     return p.AttachedConnectors.Count == 0;
2559                 })), ConnectionPointKind.Outgoing);
2560             }
2561
2562             return point;
2563         }
2564
2565         private ConnectionPoint GetSourceConnectionPointForAutoSplit(ConnectionPoint destConnPoint, UIElement srcDesigner)
2566         {
2567             this.MeasureView(srcDesigner);
2568             ConnectionPoint point = this.GetEmptyEdgeMidConnectionPointNotOfType(srcDesigner, this.exitEdgeForAutoSplit, ConnectionPointKind.Incoming);
2569             if (point == null)
2570             {
2571                 point = this.FindClosestConnectionPointNotOfType(destConnPoint, new List<ConnectionPoint>(FlowchartDesigner.GetConnectionPoints(srcDesigner).Where<ConnectionPoint>(p =>
2572                 {
2573                     return p.AttachedConnectors.Count == 0;
2574                 })), ConnectionPointKind.Incoming);
2575             }
2576
2577             return point;
2578         }
2579
2580         private void MeasureView(UIElement view)
2581         {
2582             if (this.panel.Children.Contains(view))
2583             {
2584                 this.panel.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
2585             }
2586             else if (VisualTreeHelper.GetParent(view) == null)
2587             {
2588                 StackPanel stackPanel = new StackPanel();
2589                 stackPanel.Children.Add(view);
2590                 if (view is VirtualizedContainerService.VirtualizingContainer)
2591                 {
2592                     ((VirtualizedContainerService.VirtualizingContainer)view).Populate();
2593                 }
2594                 stackPanel.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
2595                 stackPanel.UpdateLayout();
2596                 stackPanel.Children.Remove(view);
2597                 FreeFormPanel.SetChildSize(view, view.DesiredSize);
2598             }
2599             else
2600             {
2601                 Fx.Assert(false, "The view should either be un-rooted or contained in the FreeFormPanel");
2602             }
2603         }
2604
2605         private void StoreCurrentSizeViewStateWithUndo()
2606         {
2607             this.ViewStateService.StoreViewStateWithUndo(
2608                 this.ModelItem,
2609                 FlowchartSizeFeature.WidthPropertyName,
2610                 this.ViewStateService.RetrieveViewState(this.ModelItem, FlowchartSizeFeature.WidthPropertyName));
2611
2612             this.ViewStateService.StoreViewStateWithUndo(
2613                 this.ModelItem,
2614                 FlowchartSizeFeature.HeightPropertyName,
2615                 this.ViewStateService.RetrieveViewState(this.ModelItem, FlowchartSizeFeature.HeightPropertyName));
2616         }
2617
2618         static private bool IsFlowStepAction(ModelItem modelItem)
2619         {
2620             // modelItem.CurrentValue is an Activity which is an Action of a FlowStep
2621             return typeof(Activity).IsAssignableFrom(modelItem.ItemType);
2622         }
2623
2624         static private bool IsFlowNode(ModelItem modelItem)
2625         {
2626             return typeof(FlowNode).IsAssignableFrom(modelItem.ItemType);
2627         }
2628
2629         sealed private class ShapeOffsetter
2630         {
2631             private Point lastShape;
2632             private bool isFirstShape = true;
2633
2634             public Point OffsetShapeLocation(Point shapeLocation)
2635             {
2636                 if (this.isFirstShape)
2637                 {
2638                     this.lastShape = shapeLocation;
2639                     this.isFirstShape = false;
2640                     return shapeLocation;
2641                 }
2642
2643                 // the shapeLocation must be at least at right-down of lastShape
2644                 Double detX = shapeLocation.X - (lastShape.X + FreeFormPanel.GridSize);
2645                 Double detY = shapeLocation.Y - (lastShape.Y + FreeFormPanel.GridSize);
2646                 if (detX < 0 || detY < 0)
2647                 {
2648                     // overlapped
2649                     // then offset shapeLocation. 
2650                     // offsetX and offsetY must be from Integer * FreeFormPanel.GridSize, because
2651                     // shapeLocation is aligned to grid, and we expect after the offset, it is 
2652                     // still aligned.
2653                     Double offsetX = Math.Ceiling(-detX / FreeFormPanel.GridSize) * FreeFormPanel.GridSize;
2654                     Double offsetY = Math.Ceiling(-detY / FreeFormPanel.GridSize) * FreeFormPanel.GridSize;
2655                     shapeLocation.Offset(offsetX, offsetY);
2656                 }
2657                 this.lastShape = shapeLocation;
2658                 return this.lastShape;
2659             }
2660         }
2661
2662         private static void SetAutoSplitDataWithUndo(
2663             ModelItem fcModelItem,
2664             ModelItem srcModelItem,
2665             ModelItem destModelItem,
2666             int srcIndex,
2667             int destIndex,
2668             EdgeLocation entryEdgeForAutoSplit,
2669             EdgeLocation exitEdgeForAutoSplit)
2670         {
2671             using (EditingScope es = (EditingScope)fcModelItem.BeginEdit(SR.AutoSplit, false))
2672             {
2673                 es.Changes.Add(
2674                     new SetAutoSplitConnectionPointChange(
2675                         fcModelItem,
2676                         srcModelItem,
2677                         destModelItem,
2678                         srcIndex,
2679                         destIndex,
2680                         entryEdgeForAutoSplit,
2681                         exitEdgeForAutoSplit
2682                         ));
2683                 es.Complete();
2684             }
2685         }
2686
2687         private static bool IsActivityTemplateFactory(Type type)
2688         {
2689             return type.GetInterface(typeof(IActivityTemplateFactory).FullName) != null ||
2690                    type.GetInterface(typeof(IActivityTemplateFactory<>).FullName) != null;
2691         }
2692
2693         // In AutoSplit, a mark,this.SrcConnPntAutoSplit & this.DestConnPntAutoSplit,
2694         // is set telling the CreateLink, which is called later in Complete(), to use AutoSplit
2695         // way to route the connector. 
2696         // SetAutoSplitConnectionPointChange makes sure the mark is set during Redo.
2697         // This Change does nothing in Undo.
2698         private class SetAutoSplitConnectionPointChange : Change
2699         {
2700             private ModelItem Owner { get; set; }
2701             private EdgeLocation EntryEdgeForAutoSplit { get; set; }
2702             private EdgeLocation ExitEdgeForAutoSplit { get; set; }
2703             private ModelItem SrcModelItem { get; set; }
2704             private ModelItem DestModelItem { get; set; }
2705             private int SrcConnPntIndex { get; set; }
2706             private int DestConnPntIndex { get; set; }
2707
2708             private bool IsUndo { get; set; }
2709
2710             public SetAutoSplitConnectionPointChange(
2711                 ModelItem fcModelItem,
2712                 ModelItem srcModelItem,
2713                 ModelItem destModelItem,
2714                 int srcIndex,
2715                 int destIndex,
2716                 EdgeLocation entryEdgeForAutoSplit,
2717                 EdgeLocation exitEdgeForAutoSplit
2718                 )
2719             {
2720                 this.Owner = fcModelItem;
2721                 this.SrcModelItem = srcModelItem;
2722                 this.DestModelItem = destModelItem;
2723                 this.SrcConnPntIndex = srcIndex;
2724                 this.DestConnPntIndex = destIndex;
2725                 this.EntryEdgeForAutoSplit = entryEdgeForAutoSplit;
2726                 this.ExitEdgeForAutoSplit = exitEdgeForAutoSplit;
2727                 this.IsUndo = false;
2728             }
2729
2730             private SetAutoSplitConnectionPointChange()
2731             {
2732             }
2733
2734             public override string Description
2735             {
2736                 get { return SR.AutoSplit; }
2737             }
2738
2739             public override bool Apply()
2740             {
2741                 if (this.IsUndo)
2742                 {
2743                     return true;
2744                 }
2745                 FlowchartDesigner designer = this.Owner.View as FlowchartDesigner;
2746                 Fx.Assert(designer != null, "null designer");
2747                 UIElement srcElem = designer.GetView(this.SrcModelItem);
2748                 UIElement desElem = designer.GetView(this.DestModelItem);
2749                 ConnectionPoint srcConnPnt = GetConnectionPointFromIndex(srcElem, this.SrcConnPntIndex);
2750                 ConnectionPoint desConnPnt = GetConnectionPointFromIndex(desElem, this.DestConnPntIndex);
2751                 Fx.Assert(srcConnPnt != null, "srcConnPnt");
2752                 Fx.Assert(desConnPnt != null, "desConnPnt");
2753
2754                 // setting values
2755                 designer.srcConnectionPointForAutoSplit = srcConnPnt;
2756                 designer.destConnectionPointForAutoSplit = desConnPnt;
2757                 designer.entryEdgeForAutoSplit = this.EntryEdgeForAutoSplit;
2758                 designer.exitEdgeForAutoSplit = this.ExitEdgeForAutoSplit;
2759                 return true;
2760             }
2761
2762             public override Change GetInverse()
2763             {
2764                 return new SetAutoSplitConnectionPointChange
2765                 {
2766                     Owner = this.Owner,
2767                     IsUndo = !this.IsUndo,
2768                     EntryEdgeForAutoSplit = this.EntryEdgeForAutoSplit,
2769                     ExitEdgeForAutoSplit = this.ExitEdgeForAutoSplit,
2770                     SrcModelItem = this.SrcModelItem,
2771                     DestModelItem = this.DestModelItem,
2772                     SrcConnPntIndex = this.SrcConnPntIndex,
2773                     DestConnPntIndex = this.DestConnPntIndex
2774                 };
2775             }
2776         }
2777     }
2778 }