27a55ac9886321e46767c948351b6165a17517cb
[mono.git] / mcs / class / referencesource / System.Workflow.ComponentModel / AuthoringOM / Design / CommandSet.cs
1 namespace System.Workflow.ComponentModel.Design
2 {
3     using System;
4     using System.IO;
5     using System.Xml;
6     using System.Collections;
7     using System.Collections.Generic;
8     using System.Windows.Forms;
9     using System.ComponentModel;
10     using System.Resources;
11     using System.Diagnostics;
12     using System.Drawing.Design;
13     using System.Drawing.Imaging;
14     using System.Drawing.Printing;
15     using System.Windows.Forms.Design;
16     using System.ComponentModel.Design;
17     using System.ComponentModel.Design.Serialization;
18     using System.Runtime.Serialization.Formatters.Binary;
19
20     // IMPORTANT: 
21     //KEYBOARD: You need to goto <Document and settings\<user name>\ApplicationData\Microsoft\VisualStudio\8.0\" and delete 
22     // all of your *.vsk file, becuase VS always picks up keyboard bindings from that file, and also on deveenv.exe /.setup
23     // he does not clean that up. 
24     //MENUS: You need to goto <Document and settings\<user name>\ApplicationData\Microsoft\VisualStudio\8.0\1033" and delete 
25     // all of your *.prf file, becuase VS always picks up menus from that file, and also on deveenv.exe /.setup
26     // he does not clean that up. 
27     internal sealed class CommandSet : IDisposable
28     {
29         internal static CommandID[] NavigationToolCommandIds = new CommandID[] { WorkflowMenuCommands.ZoomIn, WorkflowMenuCommands.ZoomOut, WorkflowMenuCommands.Pan, WorkflowMenuCommands.DefaultFilter };
30
31         private IServiceProvider serviceProvider;
32         private IMenuCommandService menuCommandService;
33         private ISelectionService selectionService;
34         private WorkflowView workflowView;
35
36         private List<CommandSetItem> commandSet;
37         private CommandSetItem[] zoomCommands;
38         private CommandSetItem[] layoutCommands;
39         private CommandSetItem[] navigationToolCommands;
40
41         private const string CF_DESIGNER = "CF_WINOEDESIGNERCOMPONENTS";
42         private const string CF_DESIGNERSTATE = "CF_WINOEDESIGNERCOMPONENTSSTATE";
43
44         private WorkflowDesignerMessageFilter activeFilter;
45
46         public CommandSet(IServiceProvider serviceProvider)
47         {
48             Debug.Assert(serviceProvider != null);
49             this.serviceProvider = serviceProvider;
50
51             this.menuCommandService = (IMenuCommandService)this.serviceProvider.GetService(typeof(IMenuCommandService));
52             if (this.menuCommandService == null)
53                 throw new InvalidOperationException(SR.GetString(SR.General_MissingService, typeof(IMenuCommandService).FullName));
54
55             this.workflowView = serviceProvider.GetService(typeof(WorkflowView)) as WorkflowView;
56             if (this.workflowView == null)
57                 throw new InvalidOperationException(SR.GetString(SR.General_MissingService, typeof(WorkflowView).FullName));
58
59             this.selectionService = (ISelectionService)this.serviceProvider.GetService(typeof(ISelectionService));
60             if (this.selectionService == null)
61                 throw new InvalidOperationException(SR.GetString(SR.General_MissingService, typeof(ISelectionService).FullName));
62
63             this.commandSet = new List<CommandSetItem>();
64             this.commandSet.AddRange(new CommandSetItem[] {
65                         //Save commands
66                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnMenuSaveWorkflowAsImage), WorkflowMenuCommands.SaveAsImage), 
67                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnMenuCopyToClipboard), WorkflowMenuCommands.CopyToClipboard), 
68
69                         // Printing commands
70                         new CommandSetItem(new EventHandler(OnStatusPrint), new EventHandler(OnMenuPrint), WorkflowMenuCommands.Print), 
71                         new CommandSetItem(new EventHandler(OnStatusPageSetup), new EventHandler(OnMenuPageSetup), WorkflowMenuCommands.PageSetup), 
72
73                         // Editing commands
74                         new CommandSetItem(new EventHandler(OnStatusDelete), new EventHandler(OnMenuDelete), MenuCommands.Delete), 
75                         new CommandSetItem(new EventHandler(OnStatusCopy), new EventHandler(OnMenuCopy), MenuCommands.Copy), 
76                         new CommandSetItem(new EventHandler(OnStatusCut), new EventHandler(OnMenuCut), MenuCommands.Cut), 
77                         new CommandSetItem(new EventHandler(OnStatusPaste), new EventHandler(OnMenuPaste), MenuCommands.Paste, true),
78                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnMenuSelectAll), MenuCommands.SelectAll),
79
80                         // Properties
81                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnMenuDesignerProperties), WorkflowMenuCommands.DesignerProperties),
82
83                         // IMPORTANT: Microsoft does not handle this command, so VS.NET sends it to solution explorer
84                         // window, which enables this meu item on the for the current file node
85                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnViewCode), new CommandID(StandardCommands.Cut.Guid, 333)),
86
87                         // Keyboard commands
88                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnKeyCancel), MenuCommands.KeyCancel), 
89                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnKeyCancel), MenuCommands.KeyReverseCancel), 
90                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnKeyMove), MenuCommands.KeyMoveUp), 
91                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnKeyMove), MenuCommands.KeyMoveDown), 
92                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnKeyMove), MenuCommands.KeyMoveLeft), 
93                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnKeyMove), MenuCommands.KeyMoveRight),
94                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnKeyMove), MenuCommands.KeySelectNext), 
95                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnKeyMove), MenuCommands.KeySelectPrevious),
96                         new CommandSetItem(new EventHandler(OnStatusExpandCollapse), new EventHandler(OnExpandCollapse), WorkflowMenuCommands.Expand),
97                         new CommandSetItem(new EventHandler(OnStatusExpandCollapse), new EventHandler(OnExpandCollapse), WorkflowMenuCommands.Collapse),
98                         new CommandSetItem(new EventHandler(OnStatusEnable), new EventHandler(OnEnable), WorkflowMenuCommands.Disable, true),
99                         new CommandSetItem(new EventHandler(OnStatusEnable), new EventHandler(OnEnable), WorkflowMenuCommands.Enable, true),
100                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnCreateTheme), WorkflowMenuCommands.CreateTheme),
101                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnChangeTheme), WorkflowMenuCommands.ChangeTheme),
102                         new CommandSetItem(new EventHandler(OnStatusAnySelection), new EventHandler(OnKeyDefault), MenuCommands.KeyDefaultAction),
103                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnKeyPageDnUp), WorkflowMenuCommands.PageUp),
104                         new CommandSetItem(new EventHandler(OnStatusAlways), new EventHandler(OnKeyPageDnUp), WorkflowMenuCommands.PageDown),
105
106                     });
107
108
109             //WorkflowView commands
110             this.zoomCommands = new CommandSetItem[] 
111                     {
112                         new CommandSetItem(new EventHandler(OnStatusZoom), new EventHandler(OnZoom), WorkflowMenuCommands.Zoom400Mode, DR.GetString(DR.Zoom400Mode)),
113                         new CommandSetItem(new EventHandler(OnStatusZoom), new EventHandler(OnZoom), WorkflowMenuCommands.Zoom300Mode, DR.GetString(DR.Zoom300Mode)),
114                         new CommandSetItem(new EventHandler(OnStatusZoom), new EventHandler(OnZoom), WorkflowMenuCommands.Zoom200Mode, DR.GetString(DR.Zoom200Mode)),
115                         new CommandSetItem(new EventHandler(OnStatusZoom), new EventHandler(OnZoom), WorkflowMenuCommands.Zoom150Mode, DR.GetString(DR.Zoom150Mode)),
116                         new CommandSetItem(new EventHandler(OnStatusZoom), new EventHandler(OnZoom), WorkflowMenuCommands.Zoom100Mode, DR.GetString(DR.Zoom100Mode)),
117                         new CommandSetItem(new EventHandler(OnStatusZoom), new EventHandler(OnZoom), WorkflowMenuCommands.Zoom75Mode, DR.GetString(DR.Zoom75Mode)),
118                         new CommandSetItem(new EventHandler(OnStatusZoom), new EventHandler(OnZoom), WorkflowMenuCommands.Zoom50Mode, DR.GetString(DR.Zoom50Mode)),
119                         new CommandSetItem(new EventHandler(OnStatusZoom), new EventHandler(OnZoom), WorkflowMenuCommands.ShowAll, DR.GetString(DR.ZoomShowAll)),
120                     };
121             this.commandSet.AddRange(this.zoomCommands);
122
123             this.layoutCommands = new CommandSetItem[] 
124                     {
125                         new CommandSetItem(new EventHandler(OnStatusLayout), new EventHandler(OnPageLayout), WorkflowMenuCommands.DefaultPage), 
126                         new CommandSetItem(new EventHandler(OnStatusLayout), new EventHandler(OnPageLayout), WorkflowMenuCommands.PrintPreviewPage), 
127                         new CommandSetItem(new EventHandler(OnStatusLayout), new EventHandler(OnPageLayout), WorkflowMenuCommands.PrintPreview),
128                     };
129             this.commandSet.AddRange(this.layoutCommands);
130
131             this.navigationToolCommands = new CommandSetItem[] 
132                     {
133                         new CommandSetItem(new EventHandler(OnStatusMessageFilter), new EventHandler(OnMessageFilterChanged), NavigationToolCommandIds[0]), 
134                         new CommandSetItem(new EventHandler(OnStatusMessageFilter), new EventHandler(OnMessageFilterChanged), NavigationToolCommandIds[1]),
135                         new CommandSetItem(new EventHandler(OnStatusMessageFilter), new EventHandler(OnMessageFilterChanged), NavigationToolCommandIds[2]), 
136                         new CommandSetItem(new EventHandler(OnStatusMessageFilter), new EventHandler(OnMessageFilterChanged), NavigationToolCommandIds[3]),
137                     };
138
139             this.commandSet.AddRange(this.navigationToolCommands);
140
141             // add all menu commands
142             for (int i = 0; i < this.commandSet.Count; i++)
143             {
144                 if (this.menuCommandService.FindCommand(this.commandSet[i].CommandID) == null)
145                     this.menuCommandService.AddCommand(this.commandSet[i]);
146             }
147
148             IComponentChangeService changeService = this.serviceProvider.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
149             if (changeService != null)
150                 changeService.ComponentChanged += new ComponentChangedEventHandler(OnComponentChanged);
151
152             // Now setup the default command GUID for this designer.  This GUID is also used in our toolbar
153             // definition file to identify toolbars we own.  We store the GUID in a command ID here in the
154             // dictionary of the root component.  Our host may pull this GUID out and use it.
155             IDictionaryService ds = this.serviceProvider.GetService(typeof(IDictionaryService)) as IDictionaryService;
156             if (ds != null)
157                 ds.SetValue(typeof(CommandID), new CommandID(new Guid("5f1c3c8d-60f1-4b98-b85b-8679f97e8eac"), 0));
158         }
159
160         #region IDisposable Members
161         public void Dispose()
162         {
163             IComponentChangeService changeService = this.serviceProvider.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
164             if (changeService != null)
165                 changeService.ComponentChanged -= new ComponentChangedEventHandler(OnComponentChanged);
166
167             if (this.activeFilter != null)
168             {
169                 this.workflowView.RemoveDesignerMessageFilter(this.activeFilter);
170                 this.activeFilter.Dispose();
171                 this.activeFilter = null;
172             }
173
174             this.selectionService = null;
175
176             for (int i = 0; i < this.commandSet.Count; i++)
177                 this.menuCommandService.RemoveCommand(this.commandSet[i]);
178             this.menuCommandService = null;
179         }
180         #endregion
181
182         #region Helper fucntions
183         internal void UpdateCommandSet()
184         {
185             // whip through all of the commands and ask them to update.
186             for (int i = 0; i < this.commandSet.Count; i++)
187                 this.commandSet[i].UpdateStatus();
188         }
189
190         internal void UpdateZoomCommands(bool enable)
191         {
192             int commandID = this.ConvertToZoomCommand(this.workflowView.Zoom);
193             foreach (MenuCommand menuCommand in this.zoomCommands)
194             {
195                 menuCommand.Enabled = enable;
196                 menuCommand.Checked = (commandID == menuCommand.CommandID.ID);
197             }
198         }
199
200         internal void UpdatePageLayoutCommands(bool enable)
201         {
202             //we might have two commands checked at the same (PrintPreviewPage and PrintPreview - since they have sligtly different logic (one is toggle and the other is not))
203             foreach (MenuCommand menuCommand in this.layoutCommands)
204             {
205                 menuCommand.Enabled = enable;
206                 menuCommand.Checked = this.workflowView.PrintPreviewMode ? (menuCommand.CommandID == WorkflowMenuCommands.PrintPreview || menuCommand.CommandID == WorkflowMenuCommands.PrintPreviewPage) : menuCommand.CommandID == WorkflowMenuCommands.DefaultPage;
207             }
208         }
209
210         internal void UpdatePanCommands(bool enable)
211         {
212             CommandID commandID = ConvertMessageFilterToCommandID();
213             foreach (MenuCommand menuCommand in this.navigationToolCommands)
214             {
215                 menuCommand.Enabled = enable;
216                 menuCommand.Checked = (commandID == menuCommand.CommandID);
217             }
218         }
219
220         private CommandID ConvertMessageFilterToCommandID()
221         {
222             if (this.activeFilter is PanningMessageFilter)
223             {
224                 return WorkflowMenuCommands.Pan;
225             }
226             else if (this.activeFilter is ZoomingMessageFilter)
227             {
228                 if (((ZoomingMessageFilter)this.activeFilter).ZoomingIn)
229                     return WorkflowMenuCommands.ZoomIn;
230                 else
231                     return WorkflowMenuCommands.ZoomOut;
232             }
233             else
234             {
235                 return WorkflowMenuCommands.DefaultFilter;
236             }
237         }
238
239         private int ConvertToZoomLevel(int commandId)
240         {
241             int zoomLevel = 100;
242             if (commandId == WorkflowMenuCommands.Zoom400Mode.ID) zoomLevel = 400;
243             else if (commandId == WorkflowMenuCommands.Zoom300Mode.ID) zoomLevel = 300;
244             else if (commandId == WorkflowMenuCommands.Zoom200Mode.ID) zoomLevel = 200;
245             else if (commandId == WorkflowMenuCommands.Zoom150Mode.ID) zoomLevel = 150;
246             else if (commandId == WorkflowMenuCommands.Zoom100Mode.ID) zoomLevel = 100;
247             else if (commandId == WorkflowMenuCommands.Zoom75Mode.ID) zoomLevel = 75;
248             else if (commandId == WorkflowMenuCommands.Zoom50Mode.ID) zoomLevel = 50;
249
250             return zoomLevel;
251         }
252
253         private int ConvertToZoomCommand(int zoomLevel)
254         {
255             int commandID = 0; //do not select anything if the zoom level is not one of the standard ones
256             if (zoomLevel == 400) commandID = WorkflowMenuCommands.Zoom400Mode.ID;
257             else if (zoomLevel == 300) commandID = WorkflowMenuCommands.Zoom300Mode.ID;
258             else if (zoomLevel == 200) commandID = WorkflowMenuCommands.Zoom200Mode.ID;
259             else if (zoomLevel == 150) commandID = WorkflowMenuCommands.Zoom150Mode.ID;
260             else if (zoomLevel == 100) commandID = WorkflowMenuCommands.Zoom100Mode.ID;
261             else if (zoomLevel == 75) commandID = WorkflowMenuCommands.Zoom75Mode.ID;
262             else if (zoomLevel == 50) commandID = WorkflowMenuCommands.Zoom50Mode.ID;
263
264             return commandID;
265         }
266         #endregion
267
268         #region Status Handlers
269         private void OnComponentChanged(object sender, ComponentChangedEventArgs e)
270         {
271             if (this.activeFilter != null)
272             {
273                 this.workflowView.RemoveDesignerMessageFilter(this.activeFilter);
274                 this.activeFilter = null;
275                 UpdatePanCommands(true);
276             }
277         }
278
279         private void OnStatusZoom(object sender, EventArgs e)
280         {
281             UpdateZoomCommands(true);
282         }
283
284         private void OnZoom(object sender, EventArgs e)
285         {
286             MenuCommand menuCommand = (MenuCommand)sender;
287             if (menuCommand.CommandID.ID == WorkflowMenuCommands.ShowAll.ID)
288             {
289                 int newZoom = (int)(100.0f / this.workflowView.ActiveLayout.Scaling * Math.Min((float)this.workflowView.ViewPortSize.Width / (float)this.workflowView.ActiveLayout.Extent.Width, (float)this.workflowView.ViewPortSize.Height / (float)this.workflowView.ActiveLayout.Extent.Height));
290                 this.workflowView.Zoom = Math.Min(Math.Max(newZoom, AmbientTheme.MinZoom), AmbientTheme.MaxZoom);
291             }
292             else
293             {
294                 this.workflowView.Zoom = ConvertToZoomLevel(menuCommand.CommandID.ID);
295             }
296
297             UpdateZoomCommands(true);
298         }
299
300         private void OnStatusLayout(object sender, EventArgs e)
301         {
302             UpdatePageLayoutCommands(true);
303         }
304
305         private void OnPageLayout(object sender, EventArgs e)
306         {
307             MenuCommand menuCommand = (MenuCommand)sender;
308             this.workflowView.PrintPreviewMode = (menuCommand.CommandID == WorkflowMenuCommands.PrintPreview) ? !this.workflowView.PrintPreviewMode : (menuCommand.CommandID == WorkflowMenuCommands.PrintPreviewPage);
309             UpdatePageLayoutCommands(true);
310         }
311
312         private void OnStatusMessageFilter(object sender, EventArgs e)
313         {
314             UpdatePanCommands(true);
315         }
316
317         private void OnMessageFilterChanged(object sender, EventArgs e)
318         {
319             if (this.activeFilter != null)
320             {
321                 this.workflowView.RemoveDesignerMessageFilter(this.activeFilter);
322                 this.activeFilter = null;
323             }
324
325             MenuCommand menuCommand = (MenuCommand)sender;
326             int commandId = menuCommand.CommandID.ID;
327             if (WorkflowMenuCommands.ZoomIn.ID == commandId)
328                 this.activeFilter = new ZoomingMessageFilter(true);
329             else if (WorkflowMenuCommands.ZoomOut.ID == commandId)
330                 this.activeFilter = new ZoomingMessageFilter(false);
331             else if (WorkflowMenuCommands.Pan.ID == commandId)
332                 this.activeFilter = new PanningMessageFilter();
333
334             if (this.activeFilter != null)
335                 this.workflowView.AddDesignerMessageFilter(this.activeFilter);
336
337             this.workflowView.Focus();
338             UpdatePanCommands(true);
339         }
340
341         private void OnStatusPrint(object sender, EventArgs e)
342         {
343             OnStatusAlways(sender, e);
344         }
345
346         private void OnStatusPageSetup(object sender, EventArgs e)
347         {
348             OnStatusAlways(sender, e);
349         }
350
351         private void OnStatusCopy(object sender, EventArgs e)
352         {
353             MenuCommand cmd = (MenuCommand)sender;
354             bool enable = false;
355
356             IDesignerHost designerHost = this.serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost;
357             if (designerHost != null && !designerHost.Loading)
358             {
359                 ArrayList selectedComponents = new ArrayList(this.selectionService.GetSelectedComponents());
360                 enable = Helpers.AreAllActivities(selectedComponents);
361
362                 if (enable)
363                 {
364                     foreach (Activity activity in selectedComponents)
365                     {
366                         if (activity.Site != null)
367                         {
368                             designerHost = activity.Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
369                             if (designerHost != null && this.selectionService.GetComponentSelected(designerHost.RootComponent))
370                             {
371                                 enable = false;
372                                 break;
373                             }
374                         }
375                     }
376                 }
377             }
378
379             cmd.Enabled = enable;
380         }
381
382         private void OnStatusCut(object sender, EventArgs e)
383         {
384             OnStatusDelete(sender, e);
385         }
386
387         private void OnStatusDelete(object sender, EventArgs e)
388         {
389             MenuCommand cmd = (MenuCommand)sender;
390             cmd.Enabled = false;
391
392             // check if we are cutting root component
393             IDesignerHost designerHost = this.serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost;
394             if (designerHost != null && designerHost.RootComponent != null && this.selectionService.GetComponentSelected(designerHost.RootComponent))
395                 return;
396
397             //Check that we are cutting all activities
398             //Check if we are in writable context
399             ICollection components = this.selectionService.GetSelectedComponents();
400             if (!DesignerHelpers.AreComponentsRemovable(components))
401                 return;
402
403             // check if we can delete these
404             Activity[] topLevelActivities = Helpers.GetTopLevelActivities(components);
405             IDictionary commonParentActivities = Helpers.PairUpCommonParentActivities(topLevelActivities);
406             foreach (DictionaryEntry entry in commonParentActivities)
407             {
408                 CompositeActivityDesigner compositeActivityDesigner = ActivityDesigner.GetDesigner(entry.Key as Activity) as CompositeActivityDesigner;
409                 if (compositeActivityDesigner != null && !compositeActivityDesigner.CanRemoveActivities(new List<Activity>((Activity[])((ArrayList)entry.Value).ToArray(typeof(Activity))).AsReadOnly()))
410                 {
411                     cmd.Enabled = false;
412                     return;
413                 }
414             }
415
416             cmd.Enabled = true;
417         }
418
419         private void OnStatusPaste(object sender, EventArgs e)
420         {
421             MenuCommand cmd = (MenuCommand)sender;
422             cmd.Enabled = false;
423
424             //Check if we are in writtable context
425             object selectedObject = this.selectionService.PrimarySelection;
426             CompositeActivityDesigner compositeDesigner = ActivityDesigner.GetDesigner(selectedObject as Activity) as CompositeActivityDesigner;
427             if (compositeDesigner == null)
428                 compositeDesigner = ActivityDesigner.GetParentDesigner(selectedObject);
429
430             if (compositeDesigner == null || !compositeDesigner.IsEditable)
431                 return;
432
433             //Check if data object format is valid
434             IDesignerHost designerHost = this.serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost;
435             IToolboxService ts = (IToolboxService)this.serviceProvider.GetService(typeof(IToolboxService));
436             IDataObject dataObj = Clipboard.GetDataObject();
437             if (dataObj == null || designerHost == null || (!dataObj.GetDataPresent(CF_DESIGNER) && (ts != null && !ts.IsSupported(dataObj, designerHost))))
438                 return;
439
440             //Get the drop target and check if it is valid
441             HitTestInfo hitInfo = null;
442             if (selectedObject is HitTestInfo)
443             {
444                 hitInfo = (HitTestInfo)selectedObject;
445             }
446             else if (selectedObject is CompositeActivity)
447             {
448                 hitInfo = new HitTestInfo(compositeDesigner, HitTestLocations.Designer);
449             }
450             else if (selectedObject is Activity)
451             {
452                 Activity selectedActivity = selectedObject as Activity;
453                 CompositeActivity parentActivity = selectedActivity.Parent;
454                 CompositeActivityDesigner parentDesigner = ActivityDesigner.GetDesigner(parentActivity) as CompositeActivityDesigner;
455                 if (parentDesigner != null)
456                     hitInfo = new ConnectorHitTestInfo(parentDesigner, HitTestLocations.Designer, parentActivity.Activities.IndexOf(selectedActivity) + 1);
457             }
458
459             //Deserialize activities
460             ICollection components = null;
461             try
462             {
463                 components = CompositeActivityDesigner.DeserializeActivitiesFromDataObject(this.serviceProvider, dataObj);
464             }
465             catch (CheckoutException ex)
466             {
467                 if (ex != CheckoutException.Canceled)
468                     throw ex;
469             }
470
471             cmd.Enabled = (components != null && hitInfo != null && compositeDesigner.CanInsertActivities(hitInfo, new List<Activity>(Helpers.GetTopLevelActivities(components)).AsReadOnly()));
472         }
473
474         private void OnStatusAnySelection(object sender, EventArgs e)
475         {
476             // any selection means that except the root component, if any of the activity is
477             // selected then enable it
478             MenuCommand cmd = (MenuCommand)sender;
479             IDesignerHost designerHost = this.serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost;
480             cmd.Enabled = (designerHost != null && this.selectionService.GetSelectedComponents().Count > 0 &&
481                             !this.selectionService.GetComponentSelected(designerHost.RootComponent));
482         }
483
484         private void OnStatusAlways(object sender, EventArgs e)
485         {
486             MenuCommand cmd = (MenuCommand)sender;
487             cmd.Enabled = true;
488         }
489
490         private void OnStatusExpandCollapse(object sender, EventArgs e)
491         {
492             MenuCommand menuCommand = (MenuCommand)sender;
493
494             int expandCollapseItems = 0;
495             foreach (object obj in this.selectionService.GetSelectedComponents())
496             {
497                 Activity activity = obj as Activity;
498                 if (activity != null)
499                 {
500                     CompositeActivityDesigner compositeDesigner = ActivityDesigner.GetDesigner(activity) as CompositeActivityDesigner;
501                     if (compositeDesigner != null && compositeDesigner.CanExpandCollapse &&
502                         ((menuCommand.CommandID == WorkflowMenuCommands.Expand && !compositeDesigner.Expanded) ||
503                         (menuCommand.CommandID == WorkflowMenuCommands.Collapse && compositeDesigner.Expanded)))
504                     {
505                         expandCollapseItems += 1;
506                     }
507                 }
508             }
509
510             menuCommand.Visible = menuCommand.Enabled = (expandCollapseItems == this.selectionService.SelectionCount);
511         }
512
513         private void OnStatusEnable(object sender, EventArgs e)
514         {
515             MenuCommand menuCommand = (MenuCommand)sender;
516
517             bool enabledPropertyValue = true;
518             bool enabled = true;
519             ArrayList selectedObjects = new ArrayList(this.selectionService.GetSelectedComponents());
520             for (int i = 0; i < selectedObjects.Count && enabled; i++)
521             {
522                 Activity activity = selectedObjects[i] as Activity;
523                 if (activity != null)
524                 {
525                     ActivityDesigner activityDesigner = ActivityDesigner.GetDesigner(activity);
526                     if (activityDesigner == null || activityDesigner.IsLocked ||
527                         (i > 0 && enabledPropertyValue != activity.Enabled) ||
528                         (this.workflowView.RootDesigner != null && this.workflowView.RootDesigner.Activity == activity))
529                     {
530                         enabled = false;
531                     }
532                     else
533                     {
534                         enabledPropertyValue = activity.Enabled;
535                     }
536                 }
537                 else
538                 {
539                     enabled = false;
540                 }
541             }
542
543             menuCommand.Visible = menuCommand.Enabled = (enabled && ((menuCommand.CommandID == WorkflowMenuCommands.Enable && !enabledPropertyValue) || (menuCommand.CommandID == WorkflowMenuCommands.Disable && enabledPropertyValue)));
544         }
545
546         #endregion
547
548         #region Execute Handlers
549         private void OnKeyDefault(object sender, EventArgs e)
550         {
551             SendKeyDownCommand(Keys.Enter);
552         }
553
554         //sends specified key to the wf view, returns the .Handled flag
555         private bool SendKeyDownCommand(Keys key)
556         {
557             IDesignerHost host = this.serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost;
558             if (host != null)
559             {
560                 IRootDesigner rootDesigner = ActivityDesigner.GetDesigner(host.RootComponent as Activity) as IRootDesigner;
561                 if (rootDesigner != null)
562                 {
563                     WorkflowView view = rootDesigner.GetView(ViewTechnology.Default) as WorkflowView;
564                     if (view != null)
565                     {
566                         //because the some key presses are not coming into the Microsoft OnKeyDown
567                         //we need to do this work around to manually send the keypress into the designer
568
569                         KeyEventArgs eventArgs = new KeyEventArgs(key);
570                         view.OnCommandKey(eventArgs);
571                         return eventArgs.Handled;
572                     }
573                 }
574             }
575
576             return false;
577         }
578
579         private void OnKeyMove(object sender, EventArgs e)
580         {
581             object selectedObject = this.selectionService.PrimarySelection;
582             if (selectedObject == null)
583                 return;
584
585             MenuCommand menuCommand = (MenuCommand)sender;
586
587             Keys key = Keys.Left;
588
589             if (menuCommand.CommandID.ID == MenuCommands.KeyMoveDown.ID)
590                 key = Keys.Down;
591             else if (menuCommand.CommandID.ID == MenuCommands.KeyMoveUp.ID)
592                 key = Keys.Up;
593             else if (menuCommand.CommandID.ID == MenuCommands.KeyMoveLeft.ID)
594                 key = Keys.Left;
595             else if (menuCommand.CommandID.ID == MenuCommands.KeyMoveRight.ID)
596                 key = Keys.Right;
597             else if (menuCommand.CommandID.ID == MenuCommands.KeySelectNext.ID)
598                 key = Keys.Tab;
599             else if (menuCommand.CommandID.ID == MenuCommands.KeySelectPrevious.ID)
600             { key = Keys.Tab | Keys.Shift; }
601
602             SendKeyDownCommand(key);
603         }
604
605         private void OnExpandCollapse(object sender, EventArgs e)
606         {
607             // on enter key we want to do DoDefault of the designer
608             MenuCommand menuCommand = (MenuCommand)sender;
609
610             foreach (object obj in this.selectionService.GetSelectedComponents())
611             {
612                 Activity activity = obj as Activity;
613                 if (activity != null)
614                 {
615                     CompositeActivityDesigner designer = ActivityDesigner.GetDesigner(activity) as CompositeActivityDesigner;
616                     if (designer != null)
617                         designer.Expanded = (menuCommand.CommandID.ID == WorkflowMenuCommands.Expand.ID);
618                 }
619             }
620
621             MenuCommand expandCommand = this.menuCommandService.FindCommand(WorkflowMenuCommands.Expand);
622             if (expandCommand != null)
623                 OnStatusExpandCollapse(expandCommand, EventArgs.Empty);
624
625             MenuCommand collapseCommand = this.menuCommandService.FindCommand(WorkflowMenuCommands.Collapse);
626             if (collapseCommand != null)
627                 OnStatusExpandCollapse(collapseCommand, EventArgs.Empty);
628         }
629
630         private void OnEnable(object sender, EventArgs e)
631         {
632             // on enter key we want to do DoDefault of the designer
633             MenuCommand menuCommand = (MenuCommand)sender;
634
635             DesignerTransaction trans = null;
636             IComponent selectedComponent = this.selectionService.PrimarySelection as IComponent;
637             if (selectedComponent != null && selectedComponent.Site != null)
638             {
639                 IDesignerHost host = selectedComponent.Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
640                 if (host != null)
641                     trans = host.CreateTransaction(SR.GetString(SR.ChangingEnabled));
642             }
643
644             try
645             {
646                 foreach (object obj in this.selectionService.GetSelectedComponents())
647                 {
648                     Activity activity = obj as Activity;
649                     if (activity != null)
650                     {
651                         ActivityDesigner activityDesigner = ActivityDesigner.GetDesigner(activity);
652                         if (activityDesigner != null && !activityDesigner.IsLocked)
653                         {
654                             PropertyDescriptor propertyDescriptor = TypeDescriptor.GetProperties(activity)["Enabled"];
655                             if (propertyDescriptor != null)
656                                 propertyDescriptor.SetValue(activity, !activity.Enabled);
657                         }
658                     }
659                 }
660
661                 if (trans != null)
662                     trans.Commit();
663             }
664             finally
665             {
666                 if (trans != null)
667                     ((IDisposable)trans).Dispose();
668             }
669
670             MenuCommand commentCommand = this.menuCommandService.FindCommand(WorkflowMenuCommands.Disable);
671             if (commentCommand != null)
672                 OnStatusEnable(commentCommand, EventArgs.Empty);
673
674             MenuCommand uncommentCommand = this.menuCommandService.FindCommand(WorkflowMenuCommands.Enable);
675             if (uncommentCommand != null)
676                 OnStatusEnable(uncommentCommand, EventArgs.Empty);
677         }
678
679         private void OnCreateTheme(object sender, EventArgs e)
680         {
681             ThemeConfigurationDialog themeConfigDialog = new ThemeConfigurationDialog(this.serviceProvider);
682             if (themeConfigDialog.ShowDialog() == DialogResult.OK)
683             {
684                 WorkflowTheme themeToApply = themeConfigDialog.ComposedTheme.Clone() as WorkflowTheme;
685                 if (themeToApply != null)
686                 {
687                     WorkflowTheme.CurrentTheme = themeToApply;
688                     WorkflowTheme.SaveThemeSettingToRegistry();
689                 }
690             }
691         }
692
693         private void OnChangeTheme(object sender, EventArgs e)
694         {
695             IExtendedUIService extUIService = this.serviceProvider.GetService(typeof(IExtendedUIService)) as IExtendedUIService;
696             if (extUIService != null)
697                 extUIService.ShowToolsOptions();
698         }
699
700         private void OnKeyCancel(object sender, EventArgs e)
701         {
702             SendKeyDownCommand(Keys.Escape);
703         }
704
705         private void OnKeyPageDnUp(object sender, EventArgs e)
706         {
707             MenuCommand menuCommand = (MenuCommand)sender;
708             SendKeyDownCommand((menuCommand.CommandID == WorkflowMenuCommands.PageUp) ? Keys.PageUp : Keys.PageDown);
709         }
710
711         private void OnViewCode(object sender, EventArgs e)
712         {
713             IDesignerHost designerHost = this.serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost;
714             IComponent rootComponent = (designerHost != null) ? designerHost.RootComponent : null;
715             if (rootComponent != null)
716             {
717                 IMemberCreationService memberCreationService = rootComponent.Site.GetService(typeof(IMemberCreationService)) as IMemberCreationService;
718                 if (memberCreationService != null)
719                     memberCreationService.ShowCode();
720             }
721         }
722
723         private void OnMenuPageSetup(object sender, EventArgs e)
724         {
725             PrinterSettings.StringCollection printers = PrinterSettings.InstalledPrinters;
726             if (printers.Count < 1)
727             {
728                 DesignerHelpers.ShowError(this.serviceProvider, DR.GetString(DR.ThereIsNoPrinterInstalledErrorMessage));
729                 return;
730             }
731
732             WorkflowPageSetupDialog pageSetupDialog = new WorkflowPageSetupDialog(this.serviceProvider);
733             if (DialogResult.OK == pageSetupDialog.ShowDialog())
734                 this.workflowView.PerformLayout(false);
735         }
736
737         private void OnMenuSaveWorkflowAsImage(object sender, EventArgs e)
738         {
739             List<SupportedImageFormats> supportedFormats = new List<SupportedImageFormats>();
740             supportedFormats.Add(new SupportedImageFormats(DR.GetString(DR.BMPImageFormat), ImageFormat.Bmp));
741             supportedFormats.Add(new SupportedImageFormats(DR.GetString(DR.JPEGImageFormat), ImageFormat.Jpeg));
742             supportedFormats.Add(new SupportedImageFormats(DR.GetString(DR.PNGImageFormat), ImageFormat.Png));
743             supportedFormats.Add(new SupportedImageFormats(DR.GetString(DR.TIFFImageFormat), ImageFormat.Tiff));
744             supportedFormats.Add(new SupportedImageFormats(DR.GetString(DR.WMFImageFormat), ImageFormat.Wmf));
745             supportedFormats.Add(new SupportedImageFormats(DR.GetString(DR.EXIFImageFormat), ImageFormat.Exif));
746             supportedFormats.Add(new SupportedImageFormats(DR.GetString(DR.EMFImageFormat), ImageFormat.Emf));
747
748             SaveFileDialog saveFileDialog = new SaveFileDialog();
749             saveFileDialog.Title = DR.GetString(DR.SaveWorkflowImageDialogTitle);
750             saveFileDialog.DefaultExt = "bmp";
751
752             string filter = String.Empty;
753             foreach (SupportedImageFormats format in supportedFormats)
754                 filter += (filter.Length > 0) ? "|" + format.Description : format.Description;
755
756             saveFileDialog.Filter = filter;
757             saveFileDialog.FilterIndex = 0;
758             if (saveFileDialog.ShowDialog() == DialogResult.OK && saveFileDialog.FilterIndex > 0 && saveFileDialog.FilterIndex <= supportedFormats.Count)
759                 this.workflowView.SaveWorkflowImage(saveFileDialog.FileName, supportedFormats[saveFileDialog.FilterIndex - 1].Format);
760         }
761
762         private void OnMenuCopyToClipboard(object sender, EventArgs e)
763         {
764             this.workflowView.SaveWorkflowImageToClipboard();
765         }
766
767         private void OnMenuPrint(object sender, EventArgs e)
768         {
769             //check if the printers are installed
770             PrinterSettings.StringCollection printers = PrinterSettings.InstalledPrinters;
771             if (printers.Count < 1)
772             {
773                 DesignerHelpers.ShowError(this.serviceProvider, DR.GetString(DR.ThereIsNoPrinterInstalledErrorMessage));
774                 return;
775             }
776
777             //check printer selection before actually printing
778             PrintDocument printDoc = this.workflowView.PrintDocument;
779             PrintDialog printDialog = new System.Windows.Forms.PrintDialog();
780             printDialog.AllowPrintToFile = false;
781             printDialog.Document = printDoc;
782
783             try
784             {
785                 if (DialogResult.OK == printDialog.ShowDialog())
786                 {
787                     //cache main settings
788                     PrinterSettings cachedPrinterSettings = printDoc.PrinterSettings;
789                     PageSettings cachedPageSettings = printDoc.DefaultPageSettings;
790
791                     //set the user selected settings
792                     //The printer dialog itself calls print on print document we do not have to call it.
793                     printDoc.PrinterSettings = printDialog.PrinterSettings;
794                     printDoc.DefaultPageSettings = printDialog.Document.DefaultPageSettings;
795
796                     //print it...
797                     printDoc.Print();
798
799                     //and restore the main settings back
800                     printDoc.PrinterSettings = cachedPrinterSettings;
801                     printDoc.DefaultPageSettings = cachedPageSettings;
802                 }
803                 else
804                 {
805                     //todo: copy updated settings from the dialog to the print doc
806                     //in the worst case it's a no-op, in case user clicked apply/cancel it's the only way to
807                     //update the settings (see Winoe#3129 and VSWhidbey#403124 for more details)
808                 }
809             }
810             catch (Exception exception)
811             {
812                 string errorString = DR.GetString(DR.SelectedPrinterIsInvalidErrorMessage);
813                 errorString += "\n" + exception.Message;
814                 DesignerHelpers.ShowError(this.serviceProvider, errorString);
815             }
816         }
817
818         private void OnMenuDesignerProperties(object sender, EventArgs e)
819         {
820             if (this.menuCommandService != null)
821                 this.menuCommandService.GlobalInvoke(MenuCommands.PropertiesWindow);
822         }
823
824         private void OnMenuCut(object sender, EventArgs e)
825         {
826             //check if we are cutting root component
827             IDesignerHost designerHost = this.serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost;
828             if (designerHost != null && this.selectionService.GetComponentSelected(designerHost.RootComponent))
829                 return;
830
831             //Check that we are cutting all activities
832             //Check if we are in writable context
833             ICollection components = this.selectionService.GetSelectedComponents();
834             if (!Helpers.AreAllActivities(components) || !DesignerHelpers.AreAssociatedDesignersMovable(components))
835                 return;
836
837             // copy the selected component to clipboard
838             OnMenuCopy(sender, e);
839
840             // Set transaction description string based on number of activities being moved
841             string description = String.Empty;
842
843             if (components.Count > 1)
844             {
845                 description = SR.GetString(SR.CutMultipleActivities, components.Count);
846             }
847             else
848             {
849                 ArrayList componentList = new ArrayList(components);
850                 if (componentList.Count > 0)
851                     description = SR.GetString(SR.CutSingleActivity, (componentList[0] as Activity).Name);
852                 else
853                     description = SR.GetString(SR.CutActivity);
854             }
855
856             DesignerTransaction cutTransaction = designerHost.CreateTransaction(description);
857
858             try
859             {
860                 OnMenuDelete(sender, e);
861                 cutTransaction.Commit();
862             }
863             catch
864             {
865                 cutTransaction.Cancel();
866             }
867         }
868
869         private void OnMenuCopy(object sender, EventArgs e)
870         {
871             //Make sure that we are copying activities
872             if (!Helpers.AreAllActivities(this.selectionService.GetSelectedComponents()))
873                 return;
874
875             // serialize all top level activities to the store
876             Activity[] topLevelActivities = Helpers.GetTopLevelActivities(this.selectionService.GetSelectedComponents());
877             IDataObject dataObject = CompositeActivityDesigner.SerializeActivitiesToDataObject(this.serviceProvider, topLevelActivities);
878             Clipboard.SetDataObject(dataObject);
879         }
880
881         private void OnMenuPaste(object sender, EventArgs e)
882         {
883             object selectedObject = this.selectionService.PrimarySelection;
884             CompositeActivityDesigner compositeDesigner = ActivityDesigner.GetDesigner(selectedObject as Activity) as CompositeActivityDesigner;
885             if (compositeDesigner == null)
886                 compositeDesigner = ActivityDesigner.GetParentDesigner(selectedObject);
887
888             if (compositeDesigner == null || !compositeDesigner.IsEditable)
889                 return;
890
891             // deserialize activities
892             IDataObject dataObj = Clipboard.GetDataObject();
893             ICollection components = null;
894
895             try
896             {
897                 components = CompositeActivityDesigner.DeserializeActivitiesFromDataObject(this.serviceProvider, dataObj, true);
898             }
899             catch (Exception ex)
900             {
901                 if (ex != CheckoutException.Canceled)
902                     throw new Exception(DR.GetString(DR.ActivityInsertError) + "\n" + ex.Message, ex);
903             }
904
905             if (components == null)
906                 throw new InvalidOperationException(DR.GetString(DR.InvalidOperationBadClipboardFormat));
907
908             // get the drop target 
909             HitTestInfo hitInfo = null;
910             if (selectedObject is HitTestInfo)
911             {
912                 hitInfo = (HitTestInfo)selectedObject;
913             }
914             else if (selectedObject is CompositeActivity)
915             {
916                 hitInfo = new HitTestInfo(compositeDesigner, HitTestLocations.Designer);
917             }
918             else if (selectedObject is Activity)
919             {
920                 Activity selectedActivity = selectedObject as Activity;
921                 CompositeActivity parentActivity = selectedActivity.Parent;
922                 CompositeActivityDesigner parentDesigner = ActivityDesigner.GetDesigner(parentActivity) as CompositeActivityDesigner;
923                 if (parentDesigner != null)
924                     hitInfo = new ConnectorHitTestInfo(parentDesigner, HitTestLocations.Designer, parentActivity.Activities.IndexOf(selectedActivity) + 1);
925             }
926
927             List<Activity> topLevelActivities = new List<Activity>(Helpers.GetTopLevelActivities(components));
928
929             // check if we can insert or not
930             // I know  I should have disabled the paste menu it-self, but doing status check for paste gives a big performance hit. I am working on it.
931             if (hitInfo == null || !compositeDesigner.CanInsertActivities(hitInfo, topLevelActivities.AsReadOnly()))
932                 throw new Exception(SR.GetString(SR.Error_NoPasteSupport));
933
934             // Make sure the project has references to all inserted activities (in the case
935             // where an activity is copied from another project
936             IExtendedUIService extendedUIService = this.serviceProvider.GetService(typeof(IExtendedUIService)) as IExtendedUIService;
937             if (extendedUIService != null)
938             {
939                 foreach (Activity pastedActivity in components)
940                     extendedUIService.AddAssemblyReference(pastedActivity.GetType().Assembly.GetName());
941             }
942
943             CompositeActivityDesigner.InsertActivities(compositeDesigner, hitInfo, topLevelActivities.AsReadOnly(), SR.GetString(SR.PastingActivities));
944             Stream componentStateStream = dataObj.GetData(CF_DESIGNERSTATE) as Stream;
945             if (componentStateStream != null)
946                 Helpers.DeserializeDesignersFromStream(components, componentStateStream);
947
948             // set something on selections service
949             this.selectionService.SetSelectedComponents(topLevelActivities.ToArray(), SelectionTypes.Replace);
950             this.workflowView.EnsureVisible(this.selectionService.PrimarySelection);
951         }
952
953         private void OnMenuSelectAll(object sender, EventArgs e)
954         {
955             ActivityDesigner rootDesigner = ActivityDesigner.GetSafeRootDesigner(this.serviceProvider) as ActivityDesigner;
956             if (rootDesigner != null)
957             {
958                 List<Activity> activities = new List<Activity>();
959                 if (rootDesigner.Activity is CompositeActivity)
960                     activities.AddRange(Helpers.GetNestedActivities(rootDesigner.Activity as CompositeActivity));
961                 this.selectionService.SetSelectedComponents(activities.ToArray(), SelectionTypes.Replace);
962             }
963         }
964
965         private void OnMenuDelete(object sender, EventArgs e)
966         {
967             SendKeyDownCommand(Keys.Delete);
968         }
969         #endregion
970     }
971
972     #region Class SupportedImageFormats
973     internal class SupportedImageFormats
974     {
975         public string Description;
976         public ImageFormat Format;
977
978         public SupportedImageFormats(string description, ImageFormat imageFormat)
979         {
980             Description = description;
981             Format = imageFormat;
982         }
983     }
984     #endregion
985
986     #region Class CommandSetItem
987     internal sealed class CommandSetItem : MenuCommand
988     {
989         private EventHandler statusHandler;
990         private bool immidiateStatusUpdate = false;
991
992         public CommandSetItem(EventHandler statusHandler, EventHandler invokeHandler, CommandID id)
993             : base(invokeHandler, id)
994         {
995             this.statusHandler = statusHandler;
996         }
997
998         public CommandSetItem(EventHandler statusHandler, EventHandler invokeHandler, CommandID id, string text)
999             : this(statusHandler, invokeHandler, id)
1000         {
1001             Properties["Text"] = text;
1002         }
1003
1004         public CommandSetItem(EventHandler statusHandler, EventHandler invokeHandler, CommandID id, bool immidiateStatusUpdate)
1005             : this(statusHandler, invokeHandler, id)
1006         {
1007             this.immidiateStatusUpdate = immidiateStatusUpdate;
1008         }
1009
1010         public override int OleStatus
1011         {
1012             get
1013             {
1014                 if (this.immidiateStatusUpdate)
1015                     UpdateStatus();
1016                 return base.OleStatus;
1017             }
1018         }
1019
1020         public void UpdateStatus()
1021         {
1022             if (statusHandler != null)
1023             {
1024                 try
1025                 {
1026                     statusHandler(this, EventArgs.Empty);
1027                 }
1028                 catch
1029                 {
1030                 }
1031             }
1032         }
1033     }
1034     #endregion
1035 }