[reflection] Coop handles icalls in System.Reflection and System.RuntimeTypeHandle...
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / WorkflowItemPresenter.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 namespace System.Activities.Presentation
6 {
7     using System.Activities.Presentation.Hosting;
8     using System.Activities.Presentation.Internal.PropertyEditing;
9     using System.Activities.Presentation.Model;
10     using System.Activities.Presentation.Services;
11     using System.Activities.Presentation.View;
12     using System.Activities.Statements;
13     using System.Collections.Generic;
14     using System.Linq;
15     using System.Runtime;
16     using System.Windows;
17     using System.Windows.Automation.Peers;
18     using System.Windows.Controls;
19     using System.Windows.Data;
20     using System.Windows.Input;
21     using System.Windows.Media;
22     using System.Windows.Shapes;
23     using System.Windows.Threading;
24     using System.Timers;
25
26     enum InsertionPosition
27     {
28         Before,
29         After,
30         None,
31     }
32
33     class AutoWrapEventArgs : EventArgs
34     {
35         public Activity ExistingActivity { get; set; }
36         public InsertionPosition InsertionPosition { get; set; }
37         public List<Activity> ActivitiesToBeInserted { get; set; }
38     }
39
40     // This class provides a visual edit box to edit ModelItems. Textbox offers to edit strings, ints as a TextBlock and a cursor visually,
41     // The workflowitempresenter edits modelitems by picking their view using the view service. It presents s the visual for the modelitem
42     // pointe by Item property if it is set, it shows the hint text if the property is not set. It allows the associated item to be deleted 
43     // visually , and removes the reference to Item when deleted. It also allows droping ModelItems, to set the Item property to the dropped 
44     // item.
45     public sealed class WorkflowItemPresenter : ContentControl, ICompositeView
46     {
47         public static readonly DependencyProperty HintTextProperty =
48             DependencyProperty.Register("HintText", typeof(string), typeof(WorkflowItemPresenter), new UIPropertyMetadata(String.Empty));
49
50         public static readonly DependencyProperty ItemProperty =
51             DependencyProperty.Register("Item", typeof(ModelItem), typeof(WorkflowItemPresenter), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(WorkflowItemPresenter.OnItemChanged)));
52
53         public static readonly DependencyProperty AllowedItemTypeProperty =
54             DependencyProperty.Register("AllowedItemType", typeof(Type), typeof(WorkflowItemPresenter), new UIPropertyMetadata(typeof(object)));
55
56         public static readonly DependencyProperty IsDefaultContainerProperty =
57             DependencyProperty.Register("IsDefaultContainer", typeof(bool), typeof(WorkflowItemPresenter), new UIPropertyMetadata(false));
58
59         public static readonly DependencyProperty DroppingTypeResolvingOptionsProperty =
60             DependencyProperty.Register("DroppingTypeResolvingOptions", typeof(TypeResolvingOptions), typeof(WorkflowItemPresenter));
61
62         SpacerHelper spacerHelper;
63         Grid contentGrid;
64         StackPanel stackPanel;
65         Grid containerGrid;
66         TextBlock text;
67         EditingContext context = null;
68         bool shouldSetFocus = false;
69         bool isItemPastedOrDropped = false;
70
71         public WorkflowItemPresenter()
72         {
73             this.text = new TextBlock();
74             this.text.SetBinding(TextBlock.TextProperty, "HintText");
75             this.text.DataContext = this;
76             this.text.HorizontalAlignment = HorizontalAlignment.Center;
77             this.text.VerticalAlignment = VerticalAlignment.Center;
78             this.text.Foreground = new SolidColorBrush(SystemColors.GrayTextColor);
79             this.text.FontStyle = FontStyles.Italic;
80
81             this.contentGrid = new Grid();
82             this.contentGrid.Background = Brushes.Transparent;
83             this.contentGrid.VerticalAlignment = VerticalAlignment.Center;
84             this.contentGrid.Children.Add(text);
85
86             this.stackPanel = new StackPanel();
87             this.stackPanel.HorizontalAlignment = HorizontalAlignment.Center;
88             this.stackPanel.VerticalAlignment = VerticalAlignment.Center;
89             this.stackPanel.Children.Add(contentGrid);
90
91             this.containerGrid = new Grid();
92             this.containerGrid.Children.Add(stackPanel);
93             this.containerGrid.Background = Brushes.Transparent;
94         }
95         
96         internal bool AutoWrapInSequenceEnabled
97         {
98             get
99             {
100                 // Don't allow auto wrap in sequence if allowed item isn't of type of Activity
101                 return this.Context != null
102                     && this.Context.Services.GetService<DesignerConfigurationService>().AutoSurroundWithSequenceEnabled
103                     && typeof(Activity).IsAssignableFrom(this.AllowedItemType); 
104             }
105         }
106
107         Activity MyActivity
108         {
109             get
110             {
111                 return this.Item == null ? null :
112                     this.Item.GetCurrentValue() as Activity;
113             }
114         }
115
116         List<Activity> ObjectList2ActivityList(IEnumerable<object> droppedObjects)
117         {
118             List<Activity> activityList = new List<Activity>();
119             foreach (object droppedObject in droppedObjects)
120             {
121                 object modelObject = droppedObject;
122                 if (modelObject is ModelItem)
123                 {
124                     modelObject = ((ModelItem)droppedObject).GetCurrentValue();
125                 }
126                 if (modelObject is Activity)
127                 {
128                     activityList.Add(modelObject as Activity);
129                 }
130                 else
131                 {
132                     Fx.Assert("A non-activity is found in the list, there must be something seriously wrong!");
133                 }
134             }
135             return activityList;
136         }
137
138         private List<WorkflowViewElement> ObjectList2WorkflowViewElementList(IEnumerable<object> droppedObjects)
139         {
140             List<WorkflowViewElement> movedViewElements = new List<WorkflowViewElement>();
141             foreach (object droppedObject in droppedObjects)
142             {
143                 if (droppedObject is ModelItem && ((ModelItem)droppedObject).View != null)
144                 {
145                     WorkflowViewElement view = (WorkflowViewElement)((ModelItem)droppedObject).View;
146                     WorkflowItemPresenter container = DragDropHelper.GetCompositeView(view) as WorkflowItemPresenter;
147                     if (container != this)
148                     {
149                         movedViewElements.Add(view);
150                     }
151                 }
152             }
153             return movedViewElements;
154         }
155
156         // return true if really something is dropped, otherwise, false.
157         bool DoAutoWrapDrop(InsertionPosition insertionPos, IEnumerable<object> droppedObjects)
158         {
159             List<Activity> activityList = ObjectList2ActivityList(droppedObjects);
160             if (activityList.Count == 0)
161             {
162                 return false;
163             }
164
165             AutoWrapEventArgs args = new AutoWrapEventArgs()
166             {
167                 InsertionPosition = insertionPos,
168                 ExistingActivity = this.MyActivity,
169                 ActivitiesToBeInserted = activityList
170             };
171
172             using (ModelEditingScope scope = this.Context.Services.GetService<ModelService>().Root.BeginEdit(SR.WrapInSequenceDescription))
173             {
174                 ModelItem sequenceActivity = WorkflowItemPresenter.AutoWrapInSequenceHandler(this.Context, args);
175                 if (this.UpdateItem(sequenceActivity, true))
176                 {
177                     scope.Complete();
178                     return true;
179                 }
180                 else
181                 {
182                     scope.Revert();
183                     return false;
184                 }
185             }
186         }
187
188         bool DoAutoWrapDrop(InsertionPosition insertionPos, DragEventArgs e, IList<object> droppedObjects = null)
189         {
190             if (droppedObjects == null)
191             {
192                 ModelTreeManager manager = this.Context.Services.GetRequiredService<ModelTreeManager>();
193                 EditingScope editingScope = null;
194
195                 try
196                 {
197                     editingScope = ModelItemHelper.TryCreateImmediateEditingScope(manager, SR.WrapInSequenceDescription);
198
199                     droppedObjects = this.GetSortedObjectList(e);
200
201                     if (!this.DoAutoWrapDrop(insertionPos, droppedObjects))
202                     {
203                         return false;
204                     }
205
206                     if (editingScope != null)
207                     {
208                         editingScope.Complete();
209                     }
210                 }
211                 finally
212                 {
213                     if (editingScope != null)
214                     {
215                         editingScope.Dispose();
216                         editingScope = null;
217                     }
218                 }
219             }
220             else
221             {
222                 if (!this.DoAutoWrapDrop(insertionPos, droppedObjects))
223                 {
224                     return false;
225                 }
226             }
227
228             if (!DragDropHelper.IsDraggingFromToolbox(e))
229             {
230                 List<WorkflowViewElement> movedViewElements = ObjectList2WorkflowViewElementList(droppedObjects);
231                 DragDropHelper.SetDragDropMovedViewElements(e, movedViewElements);
232
233                 //Backward compatibility for 4.0
234                 if (droppedObjects.Count == 1 && movedViewElements.Count == 1)
235                 {
236                     #pragma warning disable 618
237                     DragDropHelper.SetDragDropCompletedEffects(e, DragDropEffects.Move);
238                     #pragma warning restore 618
239                 }
240             }
241             else
242             {
243                 Fx.Assert(droppedObjects.Count == 1, "Dropping from Toolbox with count != 1");
244
245                 // Set focus if it is dropping from ToolBox.
246                 // In common drag/drop, the selection setting is done at the end of
247                 // StartDragging().
248                 if (this.Item == null)
249                 {
250                     return true;
251                 }
252                 
253                 Fx.Assert(typeof(Sequence).IsAssignableFrom(this.Item.ItemType), 
254                     "Auto Wrap didn't add a sequence. Is Item.Properties[\"Activities\"] still correct?");
255                 foreach (ModelItem item in this.Item.Properties["Activities"].Collection)
256                 {
257                     // Find the ModelItem whose value is an activity from Toolbox.
258                     if (item.GetCurrentValue() == droppedObjects[0])
259                     {
260                         this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)(() =>
261                         {
262                             item.Focus();
263                         }));
264                         break;
265                     }
266                 }
267             }
268
269             return true;
270         }
271
272         protected override void OnInitialized(EventArgs e)
273         {
274             base.OnInitialized(e);
275             this.AllowDrop = true;
276             this.Content = containerGrid;
277             ICompositeViewEvents containerEvents = null;
278             bool isDefault = false;
279
280             this.Loaded += (s, eventArgs) =>
281             {
282                 isDefault = this.IsDefaultContainer;
283                 DependencyObject parent = VisualTreeHelper.GetParent(this);
284                 while (null != parent && !typeof(ICompositeViewEvents).IsAssignableFrom(parent.GetType()))
285                 {
286                     parent = VisualTreeHelper.GetParent(parent);
287                 }
288                 containerEvents = parent as ICompositeViewEvents;
289                 if (null != containerEvents)
290                 {
291                     if (isDefault)
292                     {
293                         containerEvents.RegisterDefaultCompositeView(this);
294                     }
295                     else
296                     {
297                         containerEvents.RegisterCompositeView(this);
298                     }
299                 }
300                 this.shouldSetFocus = true;
301
302                 if (this.AutoWrapInSequenceEnabled)
303                 {
304                     // spacer and placer holder
305                     this.spacerHelper = new SpacerHelper(this);
306                 }
307             };
308
309             this.Unloaded += (s, eventArgs) =>
310             {
311                 if (null != containerEvents)
312                 {
313                     if (isDefault)
314                     {
315                         containerEvents.UnregisterDefaultCompositeView(this);
316                     }
317                     else
318                     {
319                         containerEvents.UnregisterCompositeView(this);
320                     }
321                 }
322                 this.shouldSetFocus = false;
323
324                 if (this.AutoWrapInSequenceEnabled)
325                 {
326                     if (this.spacerHelper != null)
327                     {
328                         this.spacerHelper.Unload();
329                         this.spacerHelper = null;
330                     }
331                 }
332             };
333         }
334
335         public string HintText
336         {
337             get { return (string)GetValue(HintTextProperty); }
338             set { SetValue(HintTextProperty, value); }
339         }
340
341         [Fx.Tag.KnownXamlExternal]
342         public ModelItem Item
343         {
344             get { return (ModelItem)GetValue(ItemProperty); }
345             set { SetValue(ItemProperty, value); }
346         }
347
348         public Type AllowedItemType
349         {
350             get { return (Type)GetValue(AllowedItemTypeProperty); }
351             set { SetValue(AllowedItemTypeProperty, value); }
352         }
353
354         [Fx.Tag.KnownXamlExternal]
355         public TypeResolvingOptions DroppingTypeResolvingOptions
356         {
357             get { return (TypeResolvingOptions)GetValue(DroppingTypeResolvingOptionsProperty); }
358             set { SetValue(DroppingTypeResolvingOptionsProperty, value); }
359         }
360
361         EditingContext Context
362         {
363             get
364             {
365                 if (context == null)
366                 {
367                     IModelTreeItem modelTreeItem = this.Item as IModelTreeItem;
368                     if (modelTreeItem != null)
369                     {
370                         this.context = modelTreeItem.ModelTreeManager.Context;
371                     }
372                     else // There is no ModelItem yet, try to walk up the tree to find a WorkflowViewElement.
373                     {
374                         WorkflowViewElement parentViewElement = GetParentWorkflowViewElement();
375                         if (parentViewElement != null)
376                         {
377                             this.context = parentViewElement.Context;
378                         }
379                     }
380                 }
381                 return context;
382             }
383         }
384
385
386         public bool IsDefaultContainer
387         {
388             get { return (bool)GetValue(IsDefaultContainerProperty); }
389             set { SetValue(IsDefaultContainerProperty, value); }
390         }
391
392         protected override void OnRender(DrawingContext drawingContext)
393         {
394             CutCopyPasteHelper.RegisterWithParentViewElement(this);
395             base.OnRender(drawingContext);
396         }
397
398         private WorkflowViewElement GetParentWorkflowViewElement()
399         {
400             // Walk the logic tree first.
401             FrameworkElement parent = (FrameworkElement)this.Parent;
402             while (parent != null && !(parent is WorkflowViewElement))
403             {
404                 parent = parent.Parent as FrameworkElement;
405             }
406             WorkflowViewElement result = parent as WorkflowViewElement;
407             // If not found, walk the visual tree.
408             if (null == result)
409             {
410                 parent = VisualTreeHelper.GetParent(this) as FrameworkElement;
411                 while (parent != null && !(parent is WorkflowViewElement))
412                 {
413                     parent = VisualTreeHelper.GetParent(parent) as FrameworkElement;
414                 }
415                 result = parent as WorkflowViewElement;
416             }
417             return result;
418         }
419
420
421         static void OnItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
422         {
423             WorkflowItemPresenter control = (WorkflowItemPresenter)dependencyObject;
424             control.OnItemChanged(e);
425         }
426
427         void OnItemChanged(DependencyPropertyChangedEventArgs e)
428         {
429             object newItem = e.NewValue;
430             PopulateContent();
431
432             if (newItem != null)
433             {
434                 //We want to set the selection only if the item is dropped or pasted. 
435                 //We cannot set the selection in UpdateItem, since while pasting that would still be in EditingScope and this.Item will be null.
436                 if (this.isItemPastedOrDropped)
437                 {
438                     Fx.Assert(this.Item != null, "Item cannot be null");
439                     // If we are currently moving from somewhere else to a WorkflowItemPresenter, the currently 
440                     // focusing view element will be removed, we need to set the keyboard focus explicitly to 
441                     // avoid WPF FocusManager to focus on an element, leading to flashing effect.
442                     Keyboard.Focus((UIElement)this.Item.View);
443                     this.isItemPastedOrDropped = false;
444                 }
445                 if (this.shouldSetFocus)
446                 {
447                     this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)(() =>
448                     {
449                         // check for Item == null, we found an interesting bug, where the user 
450                         // could drop something in here, and undo the change before the code below
451                         // could execute
452                         if (this.Item != null)
453                         {
454                             UIElement view = (UIElement)(this.Item.View);
455                             if (view != null)
456                             {
457                                 Keyboard.Focus(view);
458                                 Selection.SelectOnly(this.Context, this.Item);
459                             }
460                         }
461                         //this.shouldSetFocus = false;
462                     }));
463                 }
464             }
465             else
466             {
467                 // remove the selection if the previous value was selected.
468                 if (this.Context != null)
469                 {
470                     if (this.Context.Items.GetValue<Selection>().SelectedObjects.Contains(e.OldValue))
471                     {
472                         this.Context.Items.SetValue(new Selection(new ModelItem[] { }));
473                     }
474                 }
475             }
476         }
477
478
479         void PopulateContent()
480         {
481             if (this.Item != null)
482             {
483                 VirtualizedContainerService containerService = this.Context.Services.GetService<VirtualizedContainerService>();
484                 UIElement itemView = containerService.GetContainer(this.Item, this);
485                 this.contentGrid.Children.Clear();
486                 this.contentGrid.Children.Add(itemView);
487             }
488             else
489             {
490                 contentGrid.Children.Clear();
491                 contentGrid.Children.Add(text);
492             }
493         }
494
495         bool UpdateItem(object newItem)
496         {
497             return UpdateItem(newItem, false);
498         }
499
500         bool UpdateItem(object newItem, bool allowReplaceExistingActivity)
501         {
502             bool updateSucceeded = false;
503             ModelItem newModelItem = newItem as ModelItem;
504             if (this.Item == null || allowReplaceExistingActivity)
505             {
506                 if (newModelItem == null && newItem != null)
507                 {
508                     // try to wrap the droppedObject in  a ModelItem.
509                     ModelServiceImpl modelService = (ModelServiceImpl)this.Context.Services.GetService<ModelService>();
510                     newModelItem = modelService.WrapAsModelItem(newItem);
511                 }
512                 if (this.CanUpdateItem(newModelItem))
513                 {
514                     // In order to allow for model updates that happens during the model item is drop, this is all done in an atomic unit.
515                     using (ModelEditingScope editingScope = this.Context.Services.GetService<ModelService>().Root.BeginEdit(SR.PropertyChangeEditingScopeDescription))
516                     {
517                         this.Item = newModelItem;
518                         editingScope.Complete();
519                     }
520                     updateSucceeded = true;
521                     this.isItemPastedOrDropped = true;
522                 }
523             }
524             return updateSucceeded;
525
526         }
527
528         bool CanUpdateItem(ModelItem newModelItem)
529         {
530             return null != newModelItem
531                 && TypeUtilities.IsTypeCompatible(newModelItem.ItemType, this.AllowedItemType)
532                 && !this.IsInParentChain(newModelItem);
533         }
534
535         List<object> GetSortedObjectList(DragEventArgs args)
536         {
537             IEnumerable<object> droppedObjects = DragDropHelper.GetDroppedObjects(this, args, this.context);
538             return DragDropHelper.SortSelectedObjects(droppedObjects);
539         }
540
541         bool DoSingleDrop(object droppedObject, DragEventArgs args)
542         {
543             if (UpdateItem(droppedObject))
544             {
545                 args.Handled = true;
546                 #pragma warning disable 618
547                 DragDropHelper.SetDragDropCompletedEffects(args, DragDropEffects.Move);
548                 #pragma warning restore 618
549                 if (droppedObject is ModelItem && ((ModelItem)droppedObject).View != null)
550                 {
551                     DragDropHelper.SetDragDropMovedViewElements(args, new WorkflowViewElement[] { ((ModelItem)droppedObject).View as WorkflowViewElement });
552                 }
553
554                 return true;
555             }
556
557             return false;
558         }
559
560         protected override void OnDrop(DragEventArgs e)
561         {
562             ModelTreeManager manager = this.Context.Services.GetService<ModelTreeManager>();
563
564             // When dragging from toolbox:
565             //     editingScope should not be null
566             //     there should only be one item
567             // When dragging from canvas:
568             //     editingScope should be null
569             // Call editingScope.Complete() to commit changes, otherwise the editing scope will be aborted
570             using (EditingScope editingScope = ModelItemHelper.TryCreateImmediateEditingScope(manager, SR.PropertyChangeEditingScopeDescription))
571             {
572                 List<object> droppedObjects = this.GetSortedObjectList(e);
573 #pragma warning disable 618
574                 DragDropHelper.SetDragDropCompletedEffects(e, DragDropEffects.None);
575 #pragma warning restore 618
576                 if (droppedObjects == null || droppedObjects.Count == 0)
577                 {
578                     return;
579                 }
580                 if (droppedObjects.Count == 1)
581                 {
582                     if (this.DoSingleDrop(droppedObjects[0], e))
583                     {
584                         if (editingScope != null)
585                         {
586                             editingScope.Complete();
587                         }
588                     }
589                     return;
590                 }
591                 else
592                 {
593                     // multi drop
594                     Fx.Assert(editingScope == null, "editingScope should be null for dragging from canvas.");
595                     this.DoAutoWrapDrop(InsertionPosition.None, e, droppedObjects);
596                 }
597                 base.OnDrop(e);
598             }
599         }
600
601         void OnDrag(DragEventArgs e)
602         {
603             if (!e.Handled)
604             {
605                 this.UpdateEffects(e);
606                 e.Handled = true;
607             }
608         }
609
610         void UpdateEffects(DragEventArgs args)
611         {
612             if (!DragDropHelper.AllowDrop(args.Data, this.Context, this.AllowedItemType))
613             {
614                 args.Effects = DragDropEffects.None;
615             }
616         }
617
618         protected override void OnDragEnter(DragEventArgs e)
619         {
620             this.OnDrag(e);
621             base.OnDragEnter(e);
622         }
623
624         protected override void OnDragOver(DragEventArgs e)
625         {
626             this.OnDrag(e);
627             base.OnDragOver(e);
628         }
629
630         protected override void OnMouseDown(MouseButtonEventArgs e)
631         {
632             // do not move focus if it's a ctrl right click.
633             if (e.RightButton == MouseButtonState.Pressed)
634             {
635                 if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
636                 {
637                     e.Handled = true;
638                     base.OnMouseDown(e);
639                     return;
640                 }
641             }
642
643             // Schedule the Keyboard.Focus command to let it execute later than WorkflowViewElement.OnMouseDown, 
644             // where WorkflowViewElement will move the keyboard focus on itself
645             this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
646             {
647                 Keyboard.Focus((FrameworkElement)this);
648             }));
649             base.OnMouseDown(e);
650         }
651
652         private bool IsInParentChain(ModelItem droppedModelItem)
653         {
654             bool isInParentChain = false;
655             // start with immediate workflowviewElement outside this.
656             WorkflowViewElement parentViewElement = GetParentWorkflowViewElement();
657             if (parentViewElement != null)
658             {
659                 ModelItem parentModelItem = parentViewElement.ModelItem;
660                 while (parentModelItem != null)
661                 {
662                     if (parentModelItem == droppedModelItem)
663                     {
664                         isInParentChain = true;
665                         break;
666                     }
667                     parentModelItem = parentModelItem.Parent;
668                 }
669             }
670             return isInParentChain;
671         }
672
673         void DeleteItem()
674         {
675             this.Item = null;
676             this.PopulateContent();
677         }
678
679         void ICompositeView.OnItemMoved(ModelItem modelItem)
680         {
681             if (this.Item == modelItem)
682             {
683                 this.Item = null;
684             }
685         }
686
687         protected override AutomationPeer OnCreateAutomationPeer()
688         {
689             return new WorkflowItemPresenterAutomationPeer(this);
690         }
691
692
693         object ICompositeView.OnItemsCut(List<ModelItem> itemsToCut)
694         {
695             Fx.Assert(itemsToCut.Count == 1, "Only one item can be cut");
696             Fx.Assert(itemsToCut[0].Equals(this.Item), "Only one item can be cut.");
697             this.DeleteItem();
698             return null;
699         }
700
701         object ICompositeView.OnItemsCopied(List<ModelItem> itemsToCopy)
702         {
703             return null;
704         }
705
706         void ICompositeView.OnItemsPasted(List<object> itemsToPaste, List<object> metaData, Point pastePoint, WorkflowViewElement pastePointReference)
707         {
708             if (itemsToPaste.Count == 1)
709             {
710                 // Single Paste
711                 UpdateItem(itemsToPaste[0]);
712             }
713             else
714             {
715                 // Mutiple Paste.
716                 IList<object> sortedList = CutCopyPasteHelper.SortFromMetaData(itemsToPaste, metaData);
717                 Fx.Assert(this.Item == null, "multi-paste on item != null is not supported now");
718                 this.DoAutoWrapDrop(InsertionPosition.None, sortedList); 
719             }
720         }
721
722         void ICompositeView.OnItemsDelete(List<ModelItem> itemsToDelete)
723         {
724             if (null != itemsToDelete && itemsToDelete.Contains(this.Item))
725             {
726                 this.DeleteItem();
727             }
728         }
729
730
731         bool ICompositeView.CanPasteItems(List<object> itemsToPaste)
732         {
733             return null != itemsToPaste &&
734                 itemsToPaste.Count > 0 &&
735                 null != itemsToPaste[0] &&
736                 null == this.Item &&
737                 ((itemsToPaste[0] is ModelItem && this.CanUpdateItem((ModelItem)itemsToPaste[0])) ||
738                 (itemsToPaste[0] is Type && this.AllowedItemType.IsAssignableFrom((Type)itemsToPaste[0])) ||
739                 this.AllowedItemType.IsAssignableFrom(itemsToPaste[0].GetType()));
740         }
741
742
743         class WorkflowItemPresenterAutomationPeer : UIElementAutomationPeer
744         {
745             WorkflowItemPresenter owner;
746
747             public WorkflowItemPresenterAutomationPeer(WorkflowItemPresenter owner)
748                 : base(owner)
749             {
750                 this.owner = owner;
751             }
752
753             protected override AutomationControlType GetAutomationControlTypeCore()
754             {
755                 return AutomationControlType.Custom;
756             }
757
758             protected override string GetAutomationIdCore()
759             {
760                 string baseAutomationID = base.GetAutomationIdCore();
761                 if (!string.IsNullOrEmpty(baseAutomationID))
762                 {
763                     return baseAutomationID;
764                 }
765                 return this.owner.GetType().Name;
766             }
767
768             protected override string GetNameCore()
769             {
770                 // Return an empty string if an activity is dropped on the presenter
771                 if (owner.Item != null)
772                 {
773                     return string.Empty;
774                 }
775                 string name = base.GetNameCore();
776                 if (string.IsNullOrEmpty(name))
777                 {
778                     name = this.owner.HintText;
779                 }
780                 return name;
781             }
782
783             protected override string GetClassNameCore()
784             {
785                 return this.owner.GetType().Name;
786             }
787         }
788
789         private static ModelItem AutoWrapInSequenceHandler(EditingContext editingContext, AutoWrapEventArgs e)
790         {
791             Fx.Assert(e.ExistingActivity != null || e.InsertionPosition == InsertionPosition.None, 
792                 "Existing activity must not be null");
793
794             ModelItem sequence = editingContext.Services.GetService<ModelTreeManager>().CreateModelItem(null, new Sequence());
795             foreach (Activity activity in e.ActivitiesToBeInserted)
796             {
797                 sequence.Properties["Activities"].Collection.Add(activity);
798             }
799
800             switch (e.InsertionPosition)
801             {
802                 case InsertionPosition.Before:
803                     sequence.Properties["Activities"].Collection.Add(e.ExistingActivity);
804                     break;
805                 case InsertionPosition.After:
806                     sequence.Properties["Activities"].Collection.Insert(0, e.ExistingActivity);
807                     break;
808                 case InsertionPosition.None:
809                     break;
810                 default:
811                     Fx.Assert("Invalid insert position");
812                     break;
813             }
814
815             return sequence;
816         }
817
818         // NOTE: This wrapper method is exclusively called by TransitionDesigner, because
819         // WIP of Transition.Action would handle the event if the dragged source comes from
820         // WIP of Transition.Trigger (see Bug 201342).  However, Auto-Surround spacer is usually
821         // handled in DragEnter handler of WIP, and other ActivityDesigner should not need to 
822         // access this method directly.
823         internal void ShowSpacerHelperOnDraggedItems(DragEventArgs arg)
824         {
825             this.spacerHelper.OnWfItemPresenterPreviewDragEnter(this, arg);
826         }
827
828         // classes and helpers for Spacer
829         private sealed class SpacerWrapper
830         {
831             public FrameworkElement Spacer { get; set; }
832             public SpacerPlaceholder Placeholder { get; set; }
833
834             public void ShowSpacer()
835             {
836                 if (Spacer != null)
837                 {
838                     Spacer.Visibility = Visibility.Visible;
839                 }
840                 if (Placeholder != null)
841                 {
842                     Placeholder.Visibility = Visibility.Collapsed;
843                 }
844             }
845
846             public void HideSpacer()
847             {
848                 if (Spacer != null)
849                 {
850                     Spacer.Visibility = Visibility.Collapsed;
851                 }
852                 if (Placeholder != null)
853                 {
854                     Placeholder.Visibility = Visibility.Visible;
855                 }
856             }
857
858             public bool HighlightPlaceholder
859             {
860                 set
861                 {
862                     this.Placeholder.TargetVisiable = value;
863                 }
864             }
865         }
866
867         // All the e.Handle = true in OnDragXXXEnter/Leave/Over:
868         // Prevent the events to be further handled by OnDrag, which will set DragDropEffects to None
869         private sealed class SpacerHelper
870         {
871             public SpacerWrapper TopSpacerWrapper { get; set; }
872             public SpacerWrapper BottomSpacerWrapper { get; set; }
873             public Timer SpacerTimer { get; set; }
874
875             private WorkflowItemPresenter wfItemPresenter;
876             private SpacerWrapper SpacerToShow { get; set; }
877
878             static private SpacerHelper uniqueSpacerHelper = null;
879
880             static private SpacerHelper UniqueSpacerHelper
881             {
882                 set
883                 {
884                     if (uniqueSpacerHelper == value)
885                     {
886                         return;
887                     }
888                     if (uniqueSpacerHelper != null)
889                     {
890                         uniqueSpacerHelper.HighLighted = false;
891                     }
892                     uniqueSpacerHelper = value;
893                     if (uniqueSpacerHelper != null)
894                     {
895                         uniqueSpacerHelper.HighLighted = true;
896                     }
897                 }
898             }
899
900             public SpacerHelper(WorkflowItemPresenter wfItemPresenter)
901             {
902                 Fx.Assert(wfItemPresenter != null, "null WorkflowItemPresenter");
903                 this.TopSpacerWrapper = new SpacerWrapper();
904                 this.BottomSpacerWrapper = new SpacerWrapper();
905                 this.wfItemPresenter = wfItemPresenter;
906                 Loaded();
907             }
908
909             public bool HighLighted 
910             {
911                 set
912                 {
913                     this.TopSpacerWrapper.HighlightPlaceholder = value;
914                     this.BottomSpacerWrapper.HighlightPlaceholder = value;
915                 }
916             }
917
918             public void Unload()
919             {
920                 // event
921                 this.wfItemPresenter.containerGrid.PreviewDrop  -= new DragEventHandler(OnContainerGridPreviewDrop);
922                 this.wfItemPresenter.PreviewDragEnter           -= new DragEventHandler(OnWfItemPresenterPreviewDragEnter);
923                 this.wfItemPresenter.PreviewDragLeave           -= new DragEventHandler(OnWfItemPresenterPreviewDragLeave);
924
925                 this.TopSpacerWrapper.Placeholder.DragEnter     -= new DragEventHandler(OnTopPlaceholderDragEnter);
926                 this.TopSpacerWrapper.Placeholder.DragLeave     -= new DragEventHandler(OnPlaceHoderDragLeave);
927                 this.TopSpacerWrapper.Spacer.DragEnter          -= new DragEventHandler(OnTopSpacerDragEnter);
928                 this.TopSpacerWrapper.Spacer.DragLeave          -= new DragEventHandler(OnTopSpacerDragLeave);
929
930                 this.BottomSpacerWrapper.Placeholder.DragEnter  -= new DragEventHandler(OnBottomPlaceholderDragEnter);
931                 this.BottomSpacerWrapper.Placeholder.DragLeave  -= new DragEventHandler(OnPlaceHoderDragLeave);
932                 this.BottomSpacerWrapper.Spacer.DragEnter       -= new DragEventHandler(OnBottomSpacerDragEnter);
933                 this.BottomSpacerWrapper.Spacer.DragLeave       -= new DragEventHandler(OnBottomSpacerDragLeave);
934
935                 this.SpacerTimer.Elapsed -= new ElapsedEventHandler(OnSpacerTimerElapsed);
936             }
937
938             void UpdateEffects(DragEventArgs args)
939             {
940                 if (this.wfItemPresenter.Item == null)
941                 {
942                     // Item is null, then use WIP's UpdateEffects.
943                     this.wfItemPresenter.UpdateEffects(args);
944                     return;
945                 }
946
947                 if (!this.AllowDropOnSpacer(args))
948                 {
949                     args.Effects = DragDropEffects.None;
950                 }
951             }
952
953             void OnTopPlaceholderDragEnter(object sender, DragEventArgs e)
954             {
955                 this.UpdateEffects(e);
956                 this.OnPlaceholderEnter(this.TopSpacerWrapper, e);
957                 e.Handled = true;
958             }
959
960             void OnBottomPlaceholderDragEnter(object sender, DragEventArgs e)
961             {
962                 this.UpdateEffects(e);
963                 this.OnPlaceholderEnter(this.BottomSpacerWrapper, e);
964                 e.Handled = true;
965             }
966
967             void OnPlaceHoderDragLeave(object sender, DragEventArgs e)
968             {
969                 this.UpdateEffects(e);
970                 this.SpacerTimer.Stop();
971                 e.Handled = true;
972             }
973
974             void OnTopSpacerDragEnter(object sender, DragEventArgs e)
975             {
976                 this.UpdateEffects(e);
977                 this.TopSpacerWrapper.ShowSpacer();
978                 e.Handled = true;
979             }
980
981             void OnTopSpacerDragLeave(object sender, DragEventArgs e)
982             {
983                 this.TopSpacerWrapper.HideSpacer();
984                 e.Handled = true;
985             }
986
987             void OnBottomSpacerDragEnter(object sender, DragEventArgs e)
988             {
989                 this.BottomSpacerWrapper.ShowSpacer();
990                 e.Handled = true;
991             }
992
993             void OnBottomSpacerDragLeave(object sender, DragEventArgs e)
994             {
995                 this.UpdateEffects(e);
996                 this.BottomSpacerWrapper.HideSpacer();
997                 e.Handled = true;
998             }
999
1000             void OnSpacerOrPlaceholderDragOver(object sender, DragEventArgs e)
1001             {
1002                 this.UpdateEffects(e);
1003                 e.Handled = true;
1004             }
1005
1006             void OnSpacerDrop(object sender, DragEventArgs e)
1007             {
1008                 if (!this.AllowDropOnSpacer(e))
1009                 {
1010                     return;
1011                 }
1012                 InsertionPosition insertionPos = (sender == this.BottomSpacerWrapper.Spacer)
1013                     ? InsertionPosition.After : InsertionPosition.Before;
1014
1015                 ModelItemHelper.TryCreateImmediateEditingScopeAndExecute(this.wfItemPresenter.Context, SR.WrapInSequenceDescription, (es) =>
1016                 {
1017                     if (this.wfItemPresenter.DoAutoWrapDrop(insertionPos, e))
1018                     {
1019                         // auto wrap is successful
1020                         if (es != null)
1021                         {
1022                             // if we created an immedate editing scope, try to complete it.
1023                             es.Complete();
1024                         }
1025                     }
1026                 });
1027             }
1028
1029             void OnPlaceholderDrop(object sender, DragEventArgs e)
1030             {
1031                 if (!this.AllowDropOnSpacer(e))
1032                 {
1033                     return;
1034                 }
1035                 InsertionPosition insertionPos = (sender == this.BottomSpacerWrapper.Placeholder)
1036                     ? InsertionPosition.After : InsertionPosition.Before;
1037                 ModelItemHelper.TryCreateImmediateEditingScopeAndExecute(this.wfItemPresenter.Context, SR.WrapInSequenceDescription, (es) =>
1038                     {
1039                         if (this.wfItemPresenter.DoAutoWrapDrop(insertionPos, e))
1040                         {
1041                             // auto wrap is successful
1042                             if (es != null)
1043                             {
1044                                 // if we created an immediate editing scope, try to complete it.
1045                                 es.Complete();
1046                             }
1047                         }
1048                     });
1049             }
1050
1051             void OnSpacerTimerElapsed(object sender, ElapsedEventArgs e)
1052             {
1053                 this.wfItemPresenter.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
1054                 {
1055                     if (this.SpacerToShow != null)
1056                     {
1057                         SpacerToShow.ShowSpacer();
1058                         SpacerToShow = null;
1059                     }
1060                 }));
1061             }
1062
1063             void OnContainerGridPreviewDrop(object sender, DragEventArgs e)
1064             {
1065                 this.SpacerTimer.Stop();
1066                 this.TopSpacerWrapper.HideSpacer();
1067                 this.BottomSpacerWrapper.HideSpacer();
1068                 UniqueSpacerHelper = null;
1069             }
1070
1071             private FrameworkElement CreateAndInitializeSpacer(VerticalAlignment alignment)
1072             {
1073                 FrameworkElement spacer = (SpacerTemplate != null)
1074                     ? (FrameworkElement)SpacerTemplate.LoadContent()
1075                     : new Rectangle();
1076                 spacer.AllowDrop = true;
1077                 spacer.DragOver += new DragEventHandler(OnSpacerOrPlaceholderDragOver);
1078                 spacer.Drop += new DragEventHandler(OnSpacerDrop);
1079                 spacer.VerticalAlignment = alignment;
1080                 spacer.IsHitTestVisible = true;
1081                 spacer.Visibility = Visibility.Collapsed;
1082                 return spacer;
1083             }
1084
1085             void OnWfItemPresenterPreviewDragLeave(object sender, DragEventArgs e)
1086             {
1087                 UniqueSpacerHelper = null;
1088                 this.TopSpacerWrapper.HideSpacer();
1089                 this.BottomSpacerWrapper.HideSpacer();
1090             }
1091
1092             // this method is made internal because WorkflowItemPresenter.ShowSpacerHelperOnDraggedItems
1093             // needs to access this method to show the spacer UI gesture for Auto-surround.
1094             internal void OnWfItemPresenterPreviewDragEnter(object sender, DragEventArgs arg)
1095             {
1096                 if (!this.AllowDropOnSpacer(arg))
1097                 {
1098                     return;
1099                 }
1100                 UniqueSpacerHelper = this;
1101             }
1102
1103             private void Loaded()
1104             {
1105                 this.TopSpacerWrapper.Spacer            = CreateAndInitializeSpacer(VerticalAlignment.Bottom);
1106                 this.TopSpacerWrapper.Placeholder       = CreateSpacerPlaceHolder();
1107                 this.BottomSpacerWrapper.Spacer         = CreateAndInitializeSpacer(VerticalAlignment.Top);
1108                 this.BottomSpacerWrapper.Placeholder    = CreateSpacerPlaceHolder();
1109
1110                 // timer
1111                 this.SpacerTimer = new Timer(500);
1112                 this.SpacerTimer.Elapsed += new ElapsedEventHandler(OnSpacerTimerElapsed);
1113                 this.SpacerTimer.AutoReset = false;
1114
1115                 // view
1116                 this.wfItemPresenter.stackPanel.Children.Insert(0, TopSpacerWrapper.Spacer);
1117                 this.wfItemPresenter.stackPanel.Children.Insert(0, TopSpacerWrapper.Placeholder);
1118                 this.wfItemPresenter.stackPanel.Children.Insert(this.wfItemPresenter.stackPanel.Children.Count, BottomSpacerWrapper.Spacer);
1119                 this.wfItemPresenter.stackPanel.Children.Insert(this.wfItemPresenter.stackPanel.Children.Count, BottomSpacerWrapper.Placeholder);
1120                 this.wfItemPresenter.containerGrid.Background = Brushes.Transparent;
1121
1122                 // event
1123                 this.wfItemPresenter.containerGrid.PreviewDrop  += new DragEventHandler(OnContainerGridPreviewDrop);
1124                 this.wfItemPresenter.PreviewDragEnter           += new DragEventHandler(OnWfItemPresenterPreviewDragEnter);
1125                 this.wfItemPresenter.PreviewDragLeave           += new DragEventHandler(OnWfItemPresenterPreviewDragLeave);
1126                 this.TopSpacerWrapper.Placeholder.DragEnter     += new DragEventHandler(OnTopPlaceholderDragEnter);
1127                 this.TopSpacerWrapper.Placeholder.DragLeave     += new DragEventHandler(OnPlaceHoderDragLeave);
1128                 this.TopSpacerWrapper.Spacer.DragEnter          += new DragEventHandler(OnTopSpacerDragEnter);
1129                 this.TopSpacerWrapper.Spacer.DragLeave          += new DragEventHandler(OnTopSpacerDragLeave);
1130
1131                 this.BottomSpacerWrapper.Placeholder.DragEnter  += new DragEventHandler(OnBottomPlaceholderDragEnter);
1132                 this.BottomSpacerWrapper.Placeholder.DragLeave  += new DragEventHandler(OnPlaceHoderDragLeave);
1133                 this.BottomSpacerWrapper.Spacer.DragEnter       += new DragEventHandler(OnBottomSpacerDragEnter);
1134                 this.BottomSpacerWrapper.Spacer.DragLeave       += new DragEventHandler(OnBottomSpacerDragLeave);
1135             }
1136
1137             private static DataTemplate defaultSpacerTemplate = CreateDefaultSpacerTemplate();
1138
1139             private static DataTemplate SpacerTemplate
1140             {
1141                 get { return defaultSpacerTemplate; }
1142             }
1143
1144             private static DataTemplate CreateDefaultSpacerTemplate()
1145             {
1146                 FrameworkElementFactory feFactory = new FrameworkElementFactory(typeof(VerticalConnector));
1147                 DataTemplate dt = new DataTemplate() { VisualTree = feFactory };
1148                 dt.Seal();
1149                 return dt;
1150             }
1151
1152             private SpacerPlaceholder CreateSpacerPlaceHolder()
1153             {
1154                 // The place holder should be something that can triger DragEnter
1155                 SpacerPlaceholder spacerPlaceholder = new SpacerPlaceholder { MinHeight = 20, Visibility = Visibility.Visible, AllowDrop = true };
1156                 spacerPlaceholder.DragOver += new DragEventHandler(OnSpacerOrPlaceholderDragOver);
1157                 spacerPlaceholder.Drop += new DragEventHandler(OnPlaceholderDrop);
1158                 return spacerPlaceholder; 
1159             }
1160
1161             private void OnPlaceholderEnter(SpacerWrapper wrapper, DragEventArgs e)
1162             {
1163                 if (!this.AllowDropOnSpacer(e))
1164                 {
1165                     return;
1166                 }
1167                 this.SpacerToShow = wrapper;
1168                 this.SpacerTimer.Start();
1169                 e.Handled = true;
1170             }
1171
1172             private bool AllowDropOnSpacer(DragEventArgs e)
1173             {
1174                 return (this.wfItemPresenter.Item != null
1175                     && !this.IsOwnerActivityBeingDragged(e)
1176                     && DragDropHelper.AllowDrop(typeof(Sequence), this.wfItemPresenter.AllowedItemType)     // Is Sequence allowed to be dropped inside the WIP? Beause it will trigger AutoWrap.
1177                     && DragDropHelper.AllowDrop(e.Data, this.wfItemPresenter.Context, typeof(Activity)));   // Is the item being dragged allowed to be dropped onto Sequence?
1178             }
1179
1180             private bool IsOwnerActivityBeingDragged(DragEventArgs e)
1181             {
1182                 if (this.wfItemPresenter.Item == null)
1183                 {
1184                     return false;
1185                 }
1186                 else
1187                 {
1188                     // In case of a toolbox drop, DragDropHelper.GetObjectsToBeDropped 
1189                     //  will create an instance, which will possibliy pop up a type picker
1190                     //  dialog for generic activities. So check for it first and avoid
1191                     //  pop up dialogs.
1192                     if (DragDropHelper.IsDraggingFromToolbox(e))
1193                     {
1194                         return false;
1195                     }
1196                     IEnumerable<ModelItem> draggedObjects = DragDropHelper.GetDraggedModelItems(e);
1197                     return draggedObjects.Contains(this.wfItemPresenter.Item);
1198                 }
1199             }
1200         }
1201
1202     }
1203 }