d6dd7278b77f07aa61bd019c3b463e49f66bcf6a
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / CutCopyPasteHelper.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 namespace System.Activities.Presentation
6 {
7     using System;
8     using System.Activities.Debugger;
9     using System.Activities.Presentation.Hosting;
10     using System.Activities.Presentation.Model;
11     using System.Activities.Presentation.View;
12     using System.Activities.Presentation.Xaml;
13     using System.Activities.Statements;
14     using System.Collections.Generic;
15     using System.Diagnostics;
16     using System.Diagnostics.CodeAnalysis;
17     using System.Globalization;
18     using System.IO;
19     using System.Linq;
20     using System.Runtime;
21     using System.Runtime.InteropServices;
22     using System.Runtime.Versioning;
23     using System.ServiceModel.Activities;
24     using System.Windows;
25     using System.Windows.Documents;
26     using System.Windows.Input;
27     using System.Windows.Media;
28     using System.Xaml;
29     using Microsoft.Activities.Presentation;
30     using Microsoft.Activities.Presentation.Xaml;
31
32     public static class CutCopyPasteHelper
33     {
34         internal static readonly DependencyProperty ChildContainersProperty =
35             DependencyProperty.RegisterAttached("ChildContainers", typeof(HashSet<ICompositeView>), typeof(CutCopyPasteHelper), new UIPropertyMetadata(null));
36
37         static object workflowCallbackContext = null;
38
39         internal const string WorkflowClipboardFormat = "WorkflowXamlFormat";
40         internal const string WorkflowClipboardFormat_TargetFramework = "WorkflowXamlFormat_TargetFramework";
41
42         //define a workflow callback clipboard format - make it unique across all processes
43         static readonly string WorkflowCallbackClipboardFormat = string.Format(CultureInfo.InvariantCulture, "WorkflowCallbackFormat{0}", Guid.NewGuid());
44
45         const string versionInfo = "1.0";
46
47         static IList<Type> disallowedTypesForCopy;
48
49         static IEnumerable<Type> DisallowedTypesForCopy
50         {
51             get
52             {
53                 if (null == disallowedTypesForCopy)
54                 {
55                     disallowedTypesForCopy = new List<Type>();
56                     disallowedTypesForCopy.Add(typeof(ActivityBuilder));
57                     disallowedTypesForCopy.Add(typeof(ModelItemKeyValuePair<,>));
58                     disallowedTypesForCopy.Add(typeof(WorkflowService));
59                     disallowedTypesForCopy.Add(typeof(Catch));
60                 }
61                 return disallowedTypesForCopy;
62             }
63         }
64
65         internal static void AddDisallowedTypeForCopy(Type type)
66         {
67             if (!DisallowedTypesForCopy.Any(p => type == p))
68             {
69                 disallowedTypesForCopy.Add(type);
70             }
71         }
72
73         [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
74         static void AddChildContainer(WorkflowViewElement viewElement, ICompositeView sourceContainer)
75         {
76             if (viewElement == null)
77             {
78                 throw FxTrace.Exception.AsError(new ArgumentNullException("viewElement"));
79             }
80             if (sourceContainer == null)
81             {
82                 throw FxTrace.Exception.AsError(new ArgumentNullException("sourceContainer"));
83             }
84
85             HashSet<ICompositeView> containers = (HashSet<ICompositeView>)viewElement.GetValue(CutCopyPasteHelper.ChildContainersProperty);
86             if (containers == null)
87             {
88                 containers = new HashSet<ICompositeView>();
89                 viewElement.SetValue(CutCopyPasteHelper.ChildContainersProperty, containers);
90             }
91             containers.Add(sourceContainer);
92         }
93
94         [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
95         static HashSet<ICompositeView> GetChildContainers(WorkflowViewElement workflowViewElement)
96         {
97             HashSet<ICompositeView> childContainers = null;
98             if (workflowViewElement != null && workflowViewElement.ShowExpanded)
99             {
100                 childContainers = (HashSet<ICompositeView>)workflowViewElement.GetValue(CutCopyPasteHelper.ChildContainersProperty);
101             }
102             return childContainers;
103         }
104
105         //This enables us to get children ICompositeViews from WorkflowViewElements. 
106         //Eg. get the WorkflowItemsPresenter from SequenceDesigner.
107         //This is useful for Cut-Copy-Paste, Delete handling, etc.
108         internal static void RegisterWithParentViewElement(ICompositeView container)
109         {
110             WorkflowViewElement parent = GetParentViewElement(container);
111             if (parent != null)
112             {
113                 CutCopyPasteHelper.AddChildContainer(parent, container);
114             }
115         }
116
117         //Returns the first WorkflowViewElement in the parent chain.
118         //If ICompositeView is a WorkflowViewElement this method returns the same object.
119         static WorkflowViewElement GetParentViewElement(ICompositeView container)
120         {
121             DependencyObject parent = container as DependencyObject;
122             return GetParentViewElement(parent);
123         }
124
125         //Returns the first WorkflowViewElement in the parent chain.
126         //Move this to a helper class.
127         internal static WorkflowViewElement GetParentViewElement(DependencyObject obj)
128         {
129             while (obj != null && !(obj is WorkflowViewElement))
130             {
131                 obj = VisualTreeHelper.GetParent(obj);
132             }
133             return obj as WorkflowViewElement;
134         }
135         
136         internal static IList<object> SortFromMetaData(IList<object> itemsToPaste, List<object> metaData)
137         {
138             IList<object> mergedItemsToPaste = SortFromMetaDataOnly(metaData);
139             // append items that are not sorted
140             foreach (object itemToPaste in itemsToPaste)
141             {
142                 if (!mergedItemsToPaste.Contains(itemToPaste))
143                 {
144                     mergedItemsToPaste.Add(itemToPaste);
145                 }
146             }
147             return mergedItemsToPaste;
148         }
149
150         internal static IList<object> SortFromMetaDataOnly(List<object> metaData)
151         {
152             List<object> mergedItemsToPaste = new List<object>();
153             if (metaData == null)
154             {
155                 return mergedItemsToPaste;
156             }
157
158             foreach (object metaDataObject in metaData)
159             {
160                 List<object> orderedItemsMetaData = metaDataObject as List<object>;
161                 
162                 if (orderedItemsMetaData == null)
163                 {
164                     continue;
165                 }
166
167                 foreach (object objectToPaste in orderedItemsMetaData)
168                 {
169                     if (!mergedItemsToPaste.Contains(objectToPaste))
170                     {
171                         mergedItemsToPaste.Add(objectToPaste);
172                     }
173                 }
174             }
175            
176             return mergedItemsToPaste;
177         }
178
179         public static void DoCut(EditingContext context)
180         {
181             if (context == null)
182             {
183                 throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
184             }
185             Selection currentSelection = context.Items.GetValue<Selection>();
186             List<ModelItem> modelItemsToCut = new List<ModelItem>(currentSelection.SelectedObjects);
187             CutCopyPasteHelper.DoCut(modelItemsToCut, context);
188         }
189
190         internal static void DoCut(List<ModelItem> modelItemsToCut, EditingContext context)
191         {
192             if (modelItemsToCut == null)
193             {
194                 throw FxTrace.Exception.AsError(new ArgumentNullException("modelItemsToCut"));
195             }
196             if (context == null)
197             {
198                 throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
199             }
200             modelItemsToCut.RemoveAll((modelItem) => { return modelItem == null; });
201             if (modelItemsToCut.Count > 0)
202             {
203                 using (EditingScope es = (EditingScope)modelItemsToCut[0].BeginEdit(SR.CutOperationEditingScopeDescription))
204                 {
205                     try
206                     {
207                         CutCopyOperation(modelItemsToCut, context, true);
208                     }
209                     catch (ExternalException e)
210                     {
211                         es.Revert();
212                         ErrorReporting.ShowErrorMessage(e.Message);
213                         return;
214                     }
215                     DesignerView view = context.Services.GetService<DesignerView>();
216                     //Setting the selection to Breadcrumb root.
217                     Fx.Assert(view != null, "DesignerView Cannot be null during cut");
218                     WorkflowViewElement rootView = view.RootDesigner as WorkflowViewElement;
219                     if (rootView != null)
220                     {
221                         Selection.SelectOnly(context, rootView.ModelItem);
222                     }
223                     es.Complete();
224                 }
225             }
226         }
227
228         public static void DoCopy(EditingContext context)
229         {
230             if (context == null)
231             {
232                 throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
233             }
234
235             Selection currentSelection = context.Items.GetValue<Selection>();
236             List<ModelItem> modelItemsToCopy = new List<ModelItem>(currentSelection.SelectedObjects);
237             CutCopyPasteHelper.DoCopy(modelItemsToCopy, context);
238         }
239
240         private static void DoCopy(List<ModelItem> modelItemsToCopy, EditingContext context)
241         {
242             if (modelItemsToCopy == null)
243             {
244                 throw FxTrace.Exception.AsError(new ArgumentNullException("modelItemsToCopy"));
245             }
246
247             if (context == null)
248             {
249                 throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
250             }
251
252             // copy only works if we have DesignerView up and running so check and throw here
253             if (context.Services.GetService<DesignerView>() == null)
254             {
255                 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CutCopyRequiresDesignerView));
256             }
257             modelItemsToCopy.RemoveAll((modelItem) => { return modelItem == null; });
258             try
259             {
260                 CutCopyOperation(modelItemsToCopy, context, false);
261             }
262             catch (ExternalException e)
263             {
264                 ErrorReporting.ShowErrorMessage(e.Message);
265             }
266         }
267
268         static void CutCopyOperation(List<ModelItem> modelItemsToCutCopy, EditingContext context, bool isCutOperation)
269         {
270             List<object> objectsOnClipboard = null;
271             List<object> metaData = null;
272             if (modelItemsToCutCopy.Count > 0)
273             {
274                 objectsOnClipboard = new List<object>(modelItemsToCutCopy.Count);
275                 metaData = new List<object>();
276                 Dictionary<ICompositeView, List<ModelItem>> notifyDictionary = new Dictionary<ICompositeView, List<ModelItem>>();
277                 UIElement breadCrumbRootView = ((DesignerView)context.Services.GetService<DesignerView>()).RootDesigner;
278                 foreach (ModelItem modelItem in modelItemsToCutCopy)
279                 {
280                     object currentElement = modelItem.GetCurrentValue();
281
282                     if (typeof(Activity).IsAssignableFrom(currentElement.GetType()))
283                     {
284                         string fileName;
285                         if (AttachablePropertyServices.TryGetProperty(currentElement, XamlDebuggerXmlReader.FileNameName, out fileName))
286                         {
287                             AttachablePropertyServices.RemoveProperty(currentElement, XamlDebuggerXmlReader.FileNameName);
288                         }
289                     }
290
291                     if (modelItem.View != null)
292                     {
293                         //The case where the breadcrumbroot designer is Cut/Copied. We do not delete the root designer, we only copy it.
294                         if (breadCrumbRootView.Equals(modelItem.View))
295                         {
296                             notifyDictionary.Clear();
297                             objectsOnClipboard.Add(modelItem.GetCurrentValue());
298                             break;
299                         }
300                         else
301                         {
302                             ICompositeView container = (ICompositeView)DragDropHelper.GetCompositeView((WorkflowViewElement)modelItem.View);
303                             if (container != null)
304                             {
305                                 //If the parent and some of its children are selected and cut/copied, we ignore the children. 
306                                 //The entire parent will be cut/copied. 
307                                 //HashSet parentModelItems contains all the model items in the parent chain of current modelItem.
308                                 //We use HashSet.IntersectWith operation to determine if one of the parents is set to be cut.
309                                 HashSet<ModelItem> parentModelItems = CutCopyPasteHelper.GetSelectableParentModelItems(modelItem);
310                                 parentModelItems.IntersectWith(modelItemsToCutCopy);
311                                 if (parentModelItems.Count == 0)
312                                 {
313                                     if (!notifyDictionary.ContainsKey(container))
314                                     {
315                                         notifyDictionary[container] = new List<ModelItem>();
316                                     }
317                                     notifyDictionary[container].Add(modelItem);
318                                 }
319                             }
320                         }
321
322                     }
323                 }
324
325                 foreach (ICompositeView container in notifyDictionary.Keys)
326                 {
327                     object containerMetaData = false;
328                     if (isCutOperation)
329                     {
330                         containerMetaData = container.OnItemsCut(notifyDictionary[container]);
331                     }
332                     else
333                     {
334                         containerMetaData = container.OnItemsCopied(notifyDictionary[container]);
335                     }
336                     if (containerMetaData != null)
337                     {
338                         metaData.Add(containerMetaData);
339                     }
340                     //Put the actual activities and not the modelItems in the clipboard.
341                     foreach (ModelItem modelItem in notifyDictionary[container])
342                     {
343                         objectsOnClipboard.Add(modelItem.GetCurrentValue());
344                     }
345                 }
346                 if (metaData.Count == 0)
347                 {
348                     metaData = null;
349                 }
350             }
351             try
352             {
353                 FrameworkName targetFramework = context.Services.GetService<DesignerConfigurationService>().TargetFrameworkName;
354                 PutOnClipBoard(objectsOnClipboard, metaData, targetFramework);
355             }
356             catch (XamlObjectReaderException exception)
357             {
358                 if (modelItemsToCutCopy.Count > 0 && ErrorActivity.GetHasErrorActivities(modelItemsToCutCopy[0].Root.GetCurrentValue()))
359                 {
360                     ErrorReporting.ShowErrorMessage(SR.CutCopyErrorActivityMessage);
361                 }
362                 else
363                 {
364                     ErrorReporting.ShowErrorMessage(exception.Message);
365                 }
366             }
367         }
368
369         //This method collects all the ModelItems in the parent chain by calling the GetSelectableParentViewElements method 
370         //which walks the WPF Visual tree. We want to avoid walking ModelItem tree.
371         internal static HashSet<ModelItem> GetSelectableParentModelItems(ModelItem modelItem)
372         {
373             if (null == modelItem)
374             {
375                 throw FxTrace.Exception.ArgumentNull("modelItem");
376             }
377             List<WorkflowViewElement> parentViewElements = GetSelectableParentViewElements(modelItem.View as WorkflowViewElement);
378             HashSet<ModelItem> parentModelItems = new HashSet<ModelItem>();
379             foreach (WorkflowViewElement view in parentViewElements)
380             {
381                 parentModelItems.Add(view.ModelItem);
382             }
383             return parentModelItems;
384         }
385
386         //This is more efficient than walking up the VisualTree looking for WorkflowViewElements.
387         //Assuming that Cut-Copy will always be against selected elements. 
388         //This implies that only elements under the BreadCrumbRoot can be cut/copied.
389         static List<WorkflowViewElement> GetSelectableParentViewElements(WorkflowViewElement childElement)
390         {
391             List<WorkflowViewElement> parentViewElements = new List<WorkflowViewElement>();
392             if (childElement != null)
393             {
394                 UIElement breadcrumbRoot = childElement.Context.Services.GetService<DesignerView>().RootDesigner;
395                 ICompositeView container = (ICompositeView)DragDropHelper.GetCompositeView(childElement);
396                 while (!childElement.Equals(breadcrumbRoot) && container != null)
397                 {
398                     childElement = CutCopyPasteHelper.GetParentViewElement(container);
399                     Fx.Assert(childElement != null, "container should be present in a WorkflowViewElement");
400                     parentViewElements.Add(childElement);
401                     container = (ICompositeView)DragDropHelper.GetCompositeView(childElement);
402                 }
403             }
404             return parentViewElements;
405         }
406
407         public static void DoPaste(EditingContext context)
408         {
409             if (context == null)
410             {
411                 throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
412             }
413             DoPaste(context, new Point(-1, -1), null);
414         }
415
416         internal static void DoPaste(EditingContext context, Point pastePoint, WorkflowViewElement pastePointReference)
417         {
418             if (context == null)
419             {
420                 throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
421             }
422
423             ModelItem modelItem = context.Items.GetValue<Selection>().PrimarySelection;
424             if (modelItem == null)
425             {
426                 return;
427             }
428
429             //Get data from clipboard.
430             List<object> metaData = null;
431             List<object> clipboardObjects = GetFromClipboard(out metaData, context);
432             if (clipboardObjects != null)
433             {
434                 using (EditingScope es = (EditingScope)modelItem.BeginEdit(SR.PasteUndoDescription))
435                 {
436                     if (clipboardObjects.Count == 3 && clipboardObjects[1] is Func<ModelItem, object, object>)
437                     {
438                         var factoryMethod = (Func<ModelItem, object, object>)clipboardObjects[1];
439                         object result = factoryMethod(modelItem, clipboardObjects[2]);
440                         clipboardObjects = new List<object>();
441                         clipboardObjects.Add(result);
442                     }
443                     ICompositeView container = GetContainerForPaste(modelItem, pastePoint);
444                     if (container != null)
445                     {
446                         container.OnItemsPasted(clipboardObjects, metaData, pastePoint, pastePointReference);
447                     }
448                     es.Complete();
449                 }
450             }
451         }
452
453         static ICompositeView GetClickedContainer(ModelItem clickedModelItem, Point clickPoint)
454         {
455             Visual parentVisual = clickedModelItem.View as Visual;
456             if (parentVisual == null)
457             {
458                 return null;
459             }
460
461             DependencyObject visualHit = null;
462             HitTestResult hitTest = VisualTreeHelper.HitTest(parentVisual, clickPoint);
463             if (hitTest != null)
464             {
465                 visualHit = hitTest.VisualHit;
466                 while (visualHit != null && !visualHit.Equals(parentVisual) &&
467                     !typeof(ICompositeView).IsAssignableFrom(visualHit.GetType()))
468                 {
469                     visualHit = VisualTreeHelper.GetParent(visualHit);
470                 }
471             }
472             return visualHit as ICompositeView;
473
474         }
475
476         static ICompositeView GetContainerForPaste(ModelItem pasteModelItem, Point clickPoint)
477         {
478             ICompositeView pasteContainer = null;
479
480             if (null != pasteModelItem && null != pasteModelItem.View && pasteModelItem.View is WorkflowViewElement)
481             {
482                 pasteContainer = ((WorkflowViewElement)pasteModelItem.View).ActiveCompositeView;
483             }
484             if (null == pasteContainer)
485             {
486                 //Get clicked container.
487                 if (clickPoint.X > 0 && clickPoint.Y > 0)
488                 {
489                     pasteContainer = GetClickedContainer(pasteModelItem, clickPoint);
490                 }
491
492                 //If the container itself is a WVE, there's posibility that it's collapsed.
493                 //Thus, we need to check this as well.
494                 if (pasteContainer != null && pasteContainer is WorkflowViewElement)
495                 {
496                     WorkflowViewElement view = pasteContainer as WorkflowViewElement;
497                     if (!view.ShowExpanded)
498                     {
499                         pasteContainer = null;
500                     }
501                 }
502                 else if (pasteContainer == null) //If the modelitem.View itself is a container.
503                 {
504                     WorkflowViewElement view = pasteModelItem.View as WorkflowViewElement;
505                     if (view != null && view.ShowExpanded)
506                     {
507                         pasteContainer = pasteModelItem.View as ICompositeView;
508                     }
509                 }
510
511                 //Get the container registered with modelItem.View if unambigous
512                 //If nothing works take the container with keyboard focus if one exists.
513                 if (pasteContainer == null)
514                 {
515                     HashSet<ICompositeView> childrenContainers = CutCopyPasteHelper.GetChildContainers(pasteModelItem.View as WorkflowViewElement);
516                     if ((childrenContainers == null || childrenContainers.Count == 0) && null != pasteModelItem.View)
517                     {
518                         pasteContainer = (ICompositeView)DragDropHelper.GetCompositeView((WorkflowViewElement)pasteModelItem.View);
519                     }
520                     else if (null != childrenContainers && childrenContainers.Count == 1)
521                     {
522                         pasteContainer = new List<ICompositeView>(childrenContainers)[0];
523                     }
524                     else
525                     {
526                         pasteContainer = Keyboard.FocusedElement as ICompositeView;
527                     }
528
529                 }
530             }
531             return pasteContainer;
532         }
533
534         private static void PutOnClipBoard(List<object> selectedData, List<object> metaData, FrameworkName targetFramework)
535         {
536             CutCopyPasteHelper.workflowCallbackContext = null;
537             if (selectedData != null)
538             {
539                 ClipboardData clipboardData = new ClipboardData();
540                 clipboardData.Data = selectedData;
541                 clipboardData.Metadata = metaData;
542                 clipboardData.Version = versionInfo;
543
544                 XamlReader reader = ViewStateXamlHelper.RemoveIdRefs(new XamlObjectReader(clipboardData));
545                 StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
546                 XamlServices.Transform(reader, new XamlXmlWriter(stringWriter, reader.SchemaContext), true);
547                 string clipBoardString = stringWriter.ToString();
548
549                 DataObject dataObject = new DataObject(WorkflowClipboardFormat, clipBoardString);
550                 dataObject.SetData(DataFormats.Text, clipBoardString);
551                 dataObject.SetData(WorkflowClipboardFormat_TargetFramework, targetFramework);
552                 RetriableClipboard.SetDataObject(dataObject, true);
553             }
554         }
555
556         //PutCallbackOnClipBoard - tries to put into private (this application only) clipboard a callback 
557         //to a method. The method will be invoked when user retrieves clipboard content - i.e. by
558         //calling a paste command.
559         //the callback has to be:
560         //- static method
561         //- have return value (not void)
562         //- takes 2 input parameters:
563         //   * 1 parameter is modelitem - this is a target modelitem upon which callback is to be executed
564         //   * 2 parameter is user provided context - any object. Since this callback will be executed within
565         //    this application only, there is no need for context to be serializable.
566         internal static void PutCallbackOnClipBoard(Func<ModelItem, object, object> callbackMethod, Type callbackResultType, object context)
567         {
568             if (null == callbackMethod || null == context)
569             {
570                 throw FxTrace.Exception.AsError(new ArgumentNullException(null == callbackMethod ? "callbackMethod" : "context"));
571             }
572             ClipboardData clipboardData = new ClipboardData();
573             List<object> data = new List<object>();
574             data.Add(callbackResultType);
575             data.Add(callbackMethod);
576             clipboardData.Data = data;
577             clipboardData.Version = versionInfo;
578             CutCopyPasteHelper.workflowCallbackContext = context;
579             try
580             {
581                 RetriableClipboard.SetDataObject(new DataObject(WorkflowCallbackClipboardFormat, clipboardData, false), false);
582             }
583             catch (ExternalException e)
584             {
585                 ErrorReporting.ShowErrorMessage(e.Message);
586             }
587         }
588
589         private static FrameworkName GetTargetFrameworkFromClipboard(DataObject dataObject)
590         {
591             Fx.Assert(dataObject != null, "dataObject should not be null");
592
593             FrameworkName clipboardFrameworkName = null;
594             if (dataObject.GetDataPresent(WorkflowClipboardFormat_TargetFramework))
595             {
596                 clipboardFrameworkName = TryGetData(dataObject, WorkflowClipboardFormat_TargetFramework) as FrameworkName;
597             }
598
599             if (clipboardFrameworkName == null)
600             {
601                 clipboardFrameworkName = FrameworkNameConstants.NetFramework40;
602             }
603
604             return clipboardFrameworkName;
605         }
606
607         //This method returns the list of objects put on clipboard by cut/copy. 
608         //Out parameter is the metaData information.
609         [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DoNotCatchGeneralExceptionTypes,
610             Justification = "Deserialization of cliboard data might fail. Propagating exceptions might lead to VS crash.")]
611         [SuppressMessage("Reliability", "Reliability108",
612             Justification = "Deserialization of cliboard data might fail. Propagating exceptions might lead to VS crash.")]
613         private static List<object> GetFromClipboard(out List<object> metaData, EditingContext editingContext)
614         {
615             Fx.Assert(editingContext != null, "editingContext should not be null");
616
617             MultiTargetingSupportService multiTargetingService = editingContext.Services.GetService<IMultiTargetingSupportService>() as MultiTargetingSupportService;
618             DesignerConfigurationService config = editingContext.Services.GetService<DesignerConfigurationService>();
619             DataObject dataObject = RetriableClipboard.GetDataObject() as DataObject;
620             List<object> workflowData = null;
621             metaData = null;
622
623             if (dataObject != null)
624             {
625                 if (dataObject.GetDataPresent(WorkflowClipboardFormat))
626                 {
627                     bool isCopyingFromHigherFrameworkToLowerFramework = false;
628
629                     if (multiTargetingService != null && config != null)
630                     {
631                         isCopyingFromHigherFrameworkToLowerFramework = GetTargetFrameworkFromClipboard(dataObject).Version > config.TargetFrameworkName.Version;
632                     }
633
634                     string clipBoardString = (string)TryGetData(dataObject, WorkflowClipboardFormat);
635                     using (StringReader stringReader = new StringReader(clipBoardString))
636                     {
637                         try
638                         {
639                             XamlSchemaContext schemaContext;
640                             if (isCopyingFromHigherFrameworkToLowerFramework)
641                             {
642                                 schemaContext = new MultiTargetingXamlSchemaContext(multiTargetingService);
643                             }
644                             else
645                             {
646                                 schemaContext = new XamlSchemaContext();
647                             }
648
649                             using (XamlXmlReader xamlXmlReader = new XamlXmlReader(stringReader, schemaContext))
650                             {
651                                 ClipboardData clipboardData = (ClipboardData)XamlServices.Load(xamlXmlReader);
652                                 metaData = clipboardData.Metadata;
653                                 workflowData = clipboardData.Data;
654                             }
655                         }
656                         catch (Exception e)
657                         {
658                             Trace.WriteLine(e.Message);
659                         }
660                     }
661                 }
662                 else if (dataObject.GetDataPresent(WorkflowCallbackClipboardFormat))
663                 {
664                     ClipboardData localData = (ClipboardData)TryGetData(dataObject, WorkflowCallbackClipboardFormat);
665                     metaData = null;
666                     workflowData = localData.Data;
667                     workflowData.Add(CutCopyPasteHelper.workflowCallbackContext);
668                 }
669             }
670             return workflowData;
671         }
672
673         private static object TryGetData(DataObject dataObject, string dataFormat)
674         {
675             try
676             {
677                 return dataObject.GetData(dataFormat);
678             }
679             catch (OutOfMemoryException)
680             {
681                 Trace.TraceError("OutOfMemoryException thrown from DataObject.");
682             }
683             return null;
684         }
685
686         private static bool CanCopy(Type type)
687         {
688             foreach (Type disallowedType in CutCopyPasteHelper.DisallowedTypesForCopy)
689             {
690                 if (disallowedType.IsAssignableFrom(type))
691                 {
692                     return false;
693                 }
694                 if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(disallowedType))
695                 {
696                     return false;
697                 }
698             }
699             return true;
700         }
701
702         private static bool CanCopy(ModelItem item)
703         {
704             return null != item.View && item.View is WorkflowViewElement &&
705                 null != ((WorkflowViewElement)item.View).ModelItem &&
706                 CanCopy(((WorkflowViewElement)item.View).ModelItem.ItemType);
707         }
708
709         public static bool CanCopy(EditingContext context)
710         {
711             if (context == null)
712             {
713                 throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
714             }
715             Selection selection = context.Items.GetValue<Selection>();
716             return selection.SelectionCount > 0 && selection.SelectedObjects.All(p => CanCopy(p));
717         }
718
719         public static bool CanCut(EditingContext context)
720         {
721             if (context == null)
722             {
723                 throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
724             }
725             bool result = false;
726             Selection selection = context.Items.GetValue<Selection>();
727             if (null != selection && selection.SelectionCount > 0)
728             {
729                 DesignerView designerView = context.Services.GetService<DesignerView>();
730                 result = selection.SelectedObjects.All(p =>
731                     CanCopy(p) && !p.View.Equals(designerView.RootDesigner));
732             }
733             return result;
734         }
735
736         public static bool CanPaste(EditingContext context)
737         {
738             if (context == null)
739             {
740                 throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
741             }
742             bool result = false;
743             ModelItem primarySelection = context.Items.GetValue<Selection>().PrimarySelection;
744             if (null != primarySelection)
745             {
746                 ICompositeView container = GetContainerForPaste(primarySelection, new Point(-1, -1));
747                 if (null != container)
748                 {
749                     DataObject dataObject = RetriableClipboard.GetDataObject() as DataObject;
750                     if (null != dataObject)
751                     {
752                         List<object> metaData = null;
753                         List<object> itemsToPaste = null;
754                         try
755                         {
756                             if (dataObject.GetDataPresent(WorkflowClipboardFormat))
757                             {
758                                 itemsToPaste = GetFromClipboard(out metaData, context);
759                                 result = container.CanPasteItems(itemsToPaste);
760                             }
761                             else if (dataObject.GetDataPresent(WorkflowCallbackClipboardFormat))
762                             {
763                                 itemsToPaste = GetFromClipboard(out metaData, context);
764                                 result = container.CanPasteItems(itemsToPaste.GetRange(0, 1));
765                             }
766                         }
767                         //This is being defensive for the case where user code for CanPasteITems throws a non-fatal exception.
768                         catch (Exception exp)
769                         {
770                             if (Fx.IsFatal(exp))
771                             {
772                                 throw;
773                             }
774                         }
775                     }
776                 }
777             }
778             return result;
779         }
780     }
781 }