[runtime] Fix corlib out of date error with disabled COM
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Model / ModelSearchServiceImpl.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.Activities.Presentation.Model
6 {
7     using System;
8     using System.Activities.Debugger;
9     using System.Activities.Expressions;
10     using System.Activities.Presentation.Annotations;
11     using System.Activities.Presentation.Converters;
12     using System.Activities.Presentation.Hosting;
13     using System.Activities.Presentation.Internal.PropertyEditing;
14     using System.Activities.Presentation.PropertyEditing;
15     using System.Activities.Presentation.Services;
16     using System.Activities.Presentation.View;
17     using System.Activities.Presentation.Xaml;
18     using System.Collections;
19     using System.Collections.Generic;
20     using System.Globalization;
21     using System.Linq;
22     using System.Reflection;
23     using System.Runtime;
24     using System.ServiceModel.Activities;
25     using System.Windows;
26     using System.Windows.Documents;
27     using System.Windows.Input;
28     using System.Windows.Threading;
29     using Microsoft.Activities.Presentation;
30
31     // The main class for search. This class will walkthrough the model item tree to build a TextImage.
32     // And it will access ObjectToSourceLocationMapping with a specific SourceLocation to get a ModelItem
33     // and highlight.
34     class ModelSearchServiceImpl : ModelSearchService
35     {
36         const int StartIndexUnchangeMark = -1;
37         const string DisplayNamePropertyName = "DisplayName";
38         EditingContext editingContext;
39         ModelService modelService;
40         WorkflowDesigner designer;
41         List<SearchableEntry> entries = new List<SearchableEntry>();
42         Dictionary<int, SearchableEntry> textImageIndexEntryMapping = new Dictionary<int, SearchableEntry>();
43         TextImage textImage;
44         HashSet<Object> alreadyVisitedObjects = new HashSet<Object>();
45         HashSet<object> objectsOnDesinger = new HashSet<object>();
46         int index;
47         ModelItem lastNavigatedItem;
48         bool isModelTreeChanged;
49         ModelItem itemToFocus;
50         AdornerLayer adornerLayer;
51         SearchToolTipAdorner toolTipAdorner;
52         WorkflowViewElement lastWorkflowViewElement;
53
54         public ModelSearchServiceImpl(WorkflowDesigner designer)
55         {
56             if (designer == null)
57             {
58                 throw FxTrace.Exception.AsError(new ArgumentNullException("designer"));
59             }
60             this.designer = designer;
61             this.editingContext = this.designer.Context;
62
63             this.editingContext.Services.Subscribe<ModelService>(new SubscribeServiceCallback<ModelService>(this.OnModelServiceAvailable));
64             this.editingContext.Services.Subscribe<DesignerView>(new SubscribeServiceCallback<DesignerView>(this.OnDesignerViewAvailable));
65             this.editingContext.Services.Subscribe<ModelTreeManager>(new SubscribeServiceCallback<ModelTreeManager>(this.OnModelTreeManagerAvailable));
66             this.editingContext.Items.Subscribe<Selection>(this.OnSelectionChanged);
67
68             // At the first time, we should generate the TextImage.
69             this.isModelTreeChanged = true;
70         }
71
72         void OnEditingScopeCompleted(object sender, EditingScopeEventArgs e)
73         {
74             this.isModelTreeChanged = true;
75         }
76
77         void OnSelectionChanged(Selection selection)
78         {
79             if (selection.PrimarySelection != this.lastNavigatedItem)
80             {
81                 this.isModelTreeChanged = true;
82             }
83         }
84
85         // Listen to the mouse down in designer and close tooltip.
86         void OnDesignerSurfaceMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
87         {
88             RemoveToolTipAdorner();
89         }
90
91         bool ShouldIgnore(ModelProperty property)
92         {
93             // Since we have searched each variable. We can strip out "Variables" property here.
94             // It's valid to hardcode "Variables" property. That's the way how variable designer get variables.
95             // We should strip out 'DisplayName', since it is searched at the beginning.
96             // We strip out 'Id', since it's a property from the Activity Base class, but never used in design time. 
97             return string.Equals(property.Name, "Variables", StringComparison.Ordinal)
98                         || string.Equals(property.Name, DisplayNamePropertyName, StringComparison.Ordinal)
99                         || string.Equals(property.Name, "Id", StringComparison.Ordinal);
100         }
101
102         public override TextImage GenerateTextImage()
103         {
104             RemoveToolTipAdorner();
105
106             // If the modelitem tree was not changed since last time we generated the text image,
107             // return the original TextImage and set the StartIndex to StartIndexUnchangeMark
108             // means VS should use their own index.
109             if (!this.isModelTreeChanged)
110             {
111                 textImage.StartLineIndex = StartIndexUnchangeMark;
112                 return textImage;
113             }
114             this.entries.Clear();
115             this.textImageIndexEntryMapping.Clear();
116             this.index = 0;
117             IEnumerable<ModelItem> itemsToSearch = this.GetItemsOnDesigner(preOrder: true, excludeRoot: true, excludeErrorActivity: true, excludeExpression: true, includeOtherObjects: false);
118             foreach (ModelItem item in itemsToSearch)
119             {
120                 this.objectsOnDesinger.Add(item.GetCurrentValue());
121             }
122
123             Selection selection = this.editingContext.Items.GetValue<Selection>();
124             int startIndex = StartIndexUnchangeMark;
125
126             // If and only if root is selected, start search from the beginning.
127             if (selection.SelectionCount == 1 && selection.PrimarySelection == modelService.Root)
128             {
129                 startIndex = 0;
130             }
131
132             AddEntriesForArguments(selection, ref startIndex);
133             foreach (ModelItem modelItem in itemsToSearch)
134             {
135                 // Do this check to make sure we start from the topmost selected item.
136                 if (startIndex == StartIndexUnchangeMark)
137                 {
138                     if (selection.SelectedObjects.Contains(modelItem) && modelItem != this.lastNavigatedItem)
139                     {
140                         // set the search start index to the next location of the current focus.
141                         startIndex = index;
142                     }
143                 }
144
145                 // Add the DisplayName property first.
146                 ModelProperty displayNameProperty = modelItem.Properties[DisplayNamePropertyName];
147                 if (displayNameProperty != null)
148                 {
149                     AddEntriesForProperty(displayNameProperty, modelItem, null);
150                 }
151                 foreach (ModelProperty modelProperty in modelItem.Properties)
152                 {
153                     if (!ShouldIgnore(modelProperty))
154                     {
155                         AddEntriesForProperty(modelProperty, modelItem, null);
156                     }
157                 }
158                 AddEntriesForVariables(modelItem);
159             }
160
161             AddBrowsableProperties(this.modelService.Root);
162
163             List<string> searchableTexts = new List<string>();
164             int textImageIndex = 0;
165             foreach (SearchableEntry entry in entries)
166             {
167                 string text = entry.Text;
168                 if (text == null)
169                 {
170                     text = string.Empty;
171                 }
172
173                 foreach (string line in text.Split(new string[] { Environment.NewLine, "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries))
174                 {
175                     this.textImageIndexEntryMapping.Add(textImageIndex, entry);
176                     searchableTexts.Add(line);
177                     textImageIndex++;
178                 }
179             }
180
181             textImage = new TextImage()
182             {
183                 StartLineIndex = startIndex,
184                 Lines = searchableTexts
185             };
186
187             this.isModelTreeChanged = false;
188             return textImage;
189         }
190
191         private void OnModelServiceAvailable(ModelService modelService)
192         {
193             this.modelService = modelService;
194         }
195
196         private void OnDesignerViewAvailable(DesignerView designerView)
197         {
198             designerView.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(this.OnDesignerSurfaceMouseLeftButtonDown), true);
199         }
200
201         private void OnModelTreeManagerAvailable(ModelTreeManager modelTreeManager)
202         {
203             modelTreeManager.EditingScopeCompleted += new EventHandler<EditingScopeEventArgs>(OnEditingScopeCompleted);
204         }
205
206         private void RemoveToolTipAdorner()
207         {
208             if (this.toolTipAdorner != null)
209             {
210                 // remove the adorner on the previous hit item.
211                 this.adornerLayer.Remove(this.toolTipAdorner);
212                 this.toolTipAdorner = null;
213                 this.lastWorkflowViewElement.CustomItemStatus = null;
214             }
215         }
216
217         internal IEnumerable<ModelItem> GetItemsOnDesigner(bool preOrder, bool excludeRoot, bool excludeErrorActivity, bool excludeExpression, bool includeOtherObjects)
218         {
219             WorkflowViewService viewService = this.WorkflowViewService;
220             IList<ModelItem> items =
221                 ModelTreeManager.DepthFirstSearch(modelService.Root,
222                 delegate(Type type)
223                 {
224                     // Only find items on the designer surface.
225                     return includeOtherObjects || (typeof(WorkflowViewElement).IsAssignableFrom(viewService.GetDesignerType(type)));
226                 },
227                 delegate(ModelItem modelItem)
228                 {
229                     return !(excludeExpression && modelItem != null && typeof(ITextExpression).IsAssignableFrom(modelItem.ItemType));
230                 },
231                 preOrder);
232
233             // ModelItemKeyValuePair is associated with CaseDesigner. 
234             // So ModelItemKeyValuePair will be returned even if they are not really Cases.
235             // Those ModelItemKeyValuePairs need to be excluded.
236             IEnumerable<ModelItem> itemsToSearch = null;
237             if (!excludeErrorActivity)
238             {
239                 itemsToSearch = items.Where<ModelItem>(item => !ModelUtilities.IsModelItemKeyValuePair(item.ItemType)
240                             || ModelUtilities.IsSwitchCase(item));
241             }
242             else
243             {
244                 itemsToSearch = items.Where<ModelItem>(item =>
245                     (!ModelUtilities.IsModelItemKeyValuePair(item.ItemType) || ModelUtilities.IsSwitchCase(item))
246                     && !IsErrorActivity(item));
247             }
248             if (excludeRoot)
249             {
250                 itemsToSearch = itemsToSearch.Except<ModelItem>(new ModelItem[] { modelService.Root });
251             }
252             return itemsToSearch;
253         }
254
255         static private bool IsErrorActivity(ModelItem item)
256         {
257             Type type = item.ItemType;
258             if (type.IsGenericType)
259             {
260                 return (typeof(ErrorActivity<>) == type.GetGenericTypeDefinition());
261             }
262
263             return (type == typeof(ErrorActivity));
264         }
265
266         internal static string ExpressionToString(object expression)
267         {
268             ITextExpression expr = expression as ITextExpression;
269             return (expr != null) ? expr.ExpressionText : expression.ToString();
270         }
271
272         SearchableEntry CreateSearchableEntry(SearchableEntryOption entryType,
273             ModelItem item, ModelProperty property, string text, string propertyPath)
274         {
275             return new SearchableEntry()
276             {
277                 LineNumber = index++,
278                 SearchableEntryType = entryType,
279                 ModelItem = item,
280                 ModelProperty = property,
281                 Text = text,
282                 PropertyPath = propertyPath
283             };
284         }
285
286         void AddEntriesForVariables(ModelItem modelItem)
287         {
288             ModelItemCollection variables = VariableHelper.GetVariableCollection(modelItem);
289             if (variables != null)
290             {
291                 foreach (ModelItem variable in variables)
292                 {
293                     entries.Add(CreateSearchableEntry(SearchableEntryOption.Variable, variable, null,
294                         TypeNameHelper.GetDisplayName(variable.Properties[DesignTimeVariable.VariableTypeProperty].ComputedValue as Type, false), null));
295
296                     entries.Add(CreateSearchableEntry(SearchableEntryOption.Variable, variable, null,
297                         variable.Properties[DesignTimeVariable.VariableNameProperty].ComputedValue.ToString(), null));
298
299                     object propertyValue = variable.Properties[DesignTimeVariable.VariableDefaultProperty].ComputedValue;
300
301                     if (propertyValue != null)
302                     {
303                         entries.Add(CreateSearchableEntry(SearchableEntryOption.Variable, variable, null,
304                             ExpressionToString(propertyValue), null));
305                     }
306
307                     if (this.editingContext.Services.GetService<DesignerConfigurationService>().AnnotationEnabled)
308                     {
309                         string annotationText = (string)variable.Properties[Annotation.AnnotationTextPropertyName].ComputedValue;
310                         if (!string.IsNullOrEmpty(annotationText))
311                         {
312                             entries.Add(CreateSearchableEntry(SearchableEntryOption.Variable, variable, null, annotationText, null));
313                         }
314                     }
315                 }
316             }
317         }
318
319         private void AddEntriesForPropertyReference(string valueText, ModelItem modelItem,
320             ModelProperty property, SearchableEntryOption entryType, string propertyPath)
321         {
322             entries.Add(CreateSearchableEntry(entryType, modelItem, property, valueText, propertyPath));
323         }
324
325         private void AddEntriesForPropertyValue(object value, ModelItem modelItem,
326             ModelProperty property, SearchableEntryOption entryType, string propertyPath)
327         {
328             // be ready for recursively visit all sub properties.
329             alreadyVisitedObjects.Clear();
330             IList<string> texts = GetSearchableStrings(value);
331             if (texts != null)
332             {
333                 foreach (string valueText in texts)
334                 {
335                     entries.Add(CreateSearchableEntry(entryType, modelItem, property, valueText, propertyPath));
336                 }
337             }
338         }
339
340         void AddBrowsableProperties(ModelItem modelItem)
341         {
342             foreach (ModelProperty property in modelItem.Properties)
343             {
344                 if (property.IsBrowsable)
345                 {
346                     this.AddEntriesForProperty(property, modelItem, null);
347                 }
348             }
349         }
350
351
352         void AddEntriesForArguments(Selection selection, ref int startIndex)
353         {
354             ModelProperty argumentsProperty = this.modelService.Root.Properties["Properties"];
355             if (argumentsProperty == null)
356             {
357                 return;
358             }
359             ModelItemCollection arguments = argumentsProperty.Collection;
360             if (arguments != null)
361             {
362                 ModelItem selectedArgument = this.GetTopmostSelectedArgument(selection, arguments);
363                 foreach (ModelItem argument in arguments)
364                 {
365                     // Do this check to make sure we start from the topmost selected item.
366                     if (startIndex == StartIndexUnchangeMark && argument == selectedArgument && argument != lastNavigatedItem)
367                     {
368                         startIndex = index;
369                     }
370                     entries.Add(CreateSearchableEntry(SearchableEntryOption.Argument, argument, null,
371                         TypeNameHelper.GetDisplayName(argument.Properties["Type"].ComputedValue as Type, false), null));
372
373                     entries.Add(CreateSearchableEntry(SearchableEntryOption.Argument, argument, null,
374                         argument.Properties[DesignTimeArgument.ArgumentNameProperty].ComputedValue.ToString(), null));
375
376                     IList<string> argumentValues = GetSearchableStrings(argument.Properties[DesignTimeArgument.ArgumentDefaultValueProperty].ComputedValue);
377                     if (argumentValues.Count == 1)
378                     {
379                         AddEntriesForPropertyValue(argumentValues[0],
380                             argument, null, SearchableEntryOption.Argument, null);
381                     }
382
383                     if (this.editingContext.Services.GetService<DesignerConfigurationService>().AnnotationEnabled)
384                     {
385                         string annotationText = (string)argument.Properties[Annotation.AnnotationTextPropertyName].ComputedValue;
386                         if (!string.IsNullOrEmpty(annotationText))
387                         {
388                             entries.Add(CreateSearchableEntry(SearchableEntryOption.Argument, argument, null, annotationText, null));
389                         }
390                     }
391                 }
392             }
393         }
394
395         private ModelItem GetTopmostSelectedArgument(Selection selection, ModelItemCollection arguments)
396         {
397             foreach (ModelItem argument in arguments)
398             {
399                 foreach (ModelItem candidateArgument in selection.SelectedObjects)
400                 {
401                     if (candidateArgument.ItemType == typeof(DesignTimeArgument))
402                     {
403                         // since for arguments, the selection is not the modelitem, it is the fakemodelitem, we cannot do a
404                         // simple reference comparing to find the selected argument.
405                         DesignTimeArgument designTimeArgument = candidateArgument.GetCurrentValue() as DesignTimeArgument;
406                         if (designTimeArgument.ReflectedObject == argument)
407                         {
408                             return argument;
409                         }
410                     }
411                 }
412             }
413             return null;
414         }
415
416         IList<string> GetSearchableStrings(object computedValue)
417         {
418             List<string> results = new List<string>();
419             if (computedValue == null || this.objectsOnDesinger.Contains(computedValue))
420             {
421                 return results;
422             }
423
424             Type type = computedValue.GetType();
425             if (type.IsPrimitive || computedValue is string || type.IsEnum || computedValue is Uri)
426             {
427                 return new List<string>() { computedValue.ToString() };
428             }
429
430             SearchableStringConverterAttribute attribute =
431                 ExtensibilityAccessor.GetAttribute<SearchableStringConverterAttribute>(type);
432
433             if (attribute == null)
434             {
435                 // try its generic type.
436                 if (type.IsGenericType)
437                 {
438                     Type generictype = type.GetGenericTypeDefinition();
439                     attribute = ExtensibilityAccessor.GetAttribute<SearchableStringConverterAttribute>(generictype);
440                 }
441             }
442
443             if (attribute != null)
444             {
445                 Type converterType = Type.GetType(attribute.ConverterTypeName);
446                 if (converterType.IsGenericTypeDefinition)
447                 {
448                     converterType = converterType.MakeGenericType(computedValue.GetType().GetGenericArguments());
449                 }
450                 SearchableStringConverter converter = Activator.CreateInstance(converterType) as SearchableStringConverter;
451                 return converter.Convert(computedValue);
452             }
453
454             // don't have an direct converter? and is a collection, then let's try convert each member.
455             if (computedValue is IEnumerable)
456             {
457                 foreach (object value in computedValue as IEnumerable)
458                 {
459                     results.AddRange(GetSearchableStrings(value));
460                 }
461                 return results;
462             }
463
464             // Already tried all the options, let's do a recursive search.
465             alreadyVisitedObjects.Add(computedValue);
466             PropertyInfo[] properties = type.GetProperties();
467             foreach (PropertyInfo property in properties)
468             {
469                 object propertyValue = property.GetValue(computedValue, null);
470                 if (!alreadyVisitedObjects.Contains(propertyValue))
471                 {
472                     results.AddRange(GetSearchableStrings(propertyValue));
473                 }
474             }
475             return results;
476         }
477
478         void AddEntriesForProperty(ModelProperty property, ModelItem modelItem, string propertyPath)
479         {
480             if (!string.IsNullOrEmpty(propertyPath))
481             {
482                 propertyPath += ",";
483                 propertyPath += property.Name;
484             }
485             else
486             {
487                 propertyPath = property.Name;
488             }
489
490             entries.Add(CreateSearchableEntry(
491                 SearchableEntryOption.Property, modelItem, property, TypeNameHelper.GetDisplayName(property.PropertyType, false), propertyPath));
492
493             entries.Add(CreateSearchableEntry(
494                 SearchableEntryOption.Property, modelItem, property, property.Name, propertyPath));
495
496             if (property.ComputedValue != null)
497             {
498                 PropertyValueEditor propertyValueEditor = null;
499                 try
500                 {
501                     propertyValueEditor = ExtensibilityAccessor.GetSubPropertyEditor(property);
502                 }
503                 catch (TargetInvocationException exception)
504                 {
505                     // To workaround 181412.If the current property's property type is a generic type and the activity 
506                     // is also a generic type Calling to ExtensibilityAccessor.GetSubPropertyEditor will get this exception. 
507                     if (exception.InnerException is ArgumentException)
508                     {
509                         propertyValueEditor = null;
510                     }
511                 }
512                 if (propertyValueEditor != null)
513                 {
514                     IList<ModelProperty> properties = ExtensibilityAccessor.GetSubProperties(property);
515                     foreach (ModelProperty propertyItem in properties)
516                     {
517                         AddEntriesForProperty(propertyItem, modelItem, propertyPath);
518                     }
519                 }
520                 else
521                 {
522                     // We don't search the value of an expandable property.
523                     AddEntriesForPropertyValue(property.ComputedValue, modelItem, property, SearchableEntryOption.Property, propertyPath);
524                 }
525             }
526             else if (property.Reference != null)
527             {
528                 AddEntriesForPropertyReference(property.Reference, modelItem, property, SearchableEntryOption.Property, propertyPath);
529             }
530         }
531
532         public ModelItem FindModelItem(int startLine, int startColumn, int endLine, int endColumn)
533         {
534             SourceLocation sourceLocation = new SourceLocation(/* fileName = */ null, startLine, startColumn, endLine, endColumn);
535             return designer.ObjectToSourceLocationMapping.FindModelItem(sourceLocation);
536         }
537
538         public ModelItem FindModelItemOfViewState(int startLine, int startColumn, int endLine, int endColumn)
539         {
540             SourceLocation sourceLocation = new SourceLocation(/* fileName = */ null, startLine, startColumn, endLine, endColumn);
541             return designer.ObjectToSourceLocationMapping.FindModelItemOfViewState(sourceLocation);
542         }
543
544         public SourceLocation FindSourceLocation(ModelItem modelItem)
545         {
546             return designer.ObjectToSourceLocationMapping.FindSourceLocation(modelItem);
547         }
548
549         public IEnumerable<object> GetObjectsWithSourceLocation()
550         {
551             return designer.ObjectToSourceLocationMapping.GetObjectsWithSourceLocation();
552         }
553
554         private ModelItem FindModelItemForNavigate(int startLine, int startColumn, int endLine, int endColumn)
555         {
556             // If we search ModelItem first, we will not have a chance to search ViewState because
557             // we will always get an ModelItem, at least the out-most Activity.
558             ModelItem modelItem = this.FindModelItemOfViewState(startLine, startColumn, endLine, endColumn);
559             if (modelItem != null)
560             {
561                 return modelItem;
562             }
563
564             return this.FindModelItem(startLine, startColumn, endLine, endColumn);
565         }
566
567         public override bool NavigateTo(int startLine, int startColumn, int endLine, int endColumn)
568         {
569             ModelItem itemToFocus = this.FindModelItemForNavigate(startLine, startColumn, endLine, endColumn);
570
571             return this.NavigateTo(itemToFocus);
572         }
573
574         // Navigate to a ModelItem with the specified location in TextImage. This is for Find Next.
575         public override bool NavigateTo(int location)
576         {
577             if (location < 0 || location >= this.textImageIndexEntryMapping.Count)
578             {
579                 return false;
580             }
581
582             SearchableEntry entry = this.textImageIndexEntryMapping[location];
583             return NavigateTo(entry);
584         }
585
586         public bool NavigateTo(ModelItem itemToFocus)
587         {
588             if (itemToFocus == null)
589             {
590                 return false;
591             }
592
593             SearchableEntry entry = CreateSearchableEntryForArgumentOrVariable(itemToFocus);
594             if (entry != null)
595             {
596                 return this.NavigateTo(entry);
597             }
598
599             itemToFocus = this.FindModelItemToFocus(itemToFocus);
600
601             itemToFocus.Focus();
602
603             return true;
604         }
605
606         private static SearchableEntry CreateSearchableEntryNoRecursive(ModelItem modelItem)
607         {
608             if (typeof(DynamicActivityProperty).IsAssignableFrom(modelItem.ItemType))
609             {
610                 return new SearchableEntry
611                 {
612                     SearchableEntryType = SearchableEntryOption.Argument,
613                     ModelItem = modelItem
614                 };
615             }
616             else if (typeof(Variable).IsAssignableFrom(modelItem.ItemType))
617             {
618                 return new SearchableEntry
619                 {
620                     SearchableEntryType = SearchableEntryOption.Variable,
621                     ModelItem = modelItem
622                 };
623             }
624
625             return null;
626         }
627
628         private static SearchableEntry CreateSearchableEntryForArgumentOrVariable(ModelItem itemToFocus)
629         {
630             SearchableEntry entry = null;
631             ModelUtilities.ReverseTraverse(itemToFocus, (ModelItem modelItem) =>
632             {
633                 entry = CreateSearchableEntryNoRecursive(modelItem);
634                 return (entry == null);
635             });
636             return entry;
637         }
638
639         private bool NavigateTo(SearchableEntry entry)
640         {
641             if (entry.SearchableEntryType == SearchableEntryOption.Variable)
642             {
643                 itemToFocus = entry.ModelItem.Parent.Parent;
644                 HighlightModelItem(itemToFocus);
645                 this.lastNavigatedItem = itemToFocus;
646                 var designerView = this.editingContext.Services.GetService<DesignerView>();
647                 // Open the variable designer.
648                 designerView.CheckButtonVariables();
649                 designerView.variables1.SelectVariable(entry.ModelItem);
650             }
651             else if (entry.SearchableEntryType == SearchableEntryOption.Argument)
652             {
653                 itemToFocus = this.modelService.Root;
654                 HighlightModelItem(itemToFocus);
655                 var designerView = this.editingContext.Services.GetService<DesignerView>();
656                 // Open the argument designer.
657                 designerView.CheckButtonArguments();
658                 designerView.arguments1.SelectArgument(entry.ModelItem);
659                 this.lastNavigatedItem = entry.ModelItem;
660             }
661             else
662             {
663                 itemToFocus = entry.ModelItem;
664                 HighlightModelItem(itemToFocus);
665                 this.lastNavigatedItem = itemToFocus;
666                 ICommandService commandService = this.editingContext.Services.GetService<ICommandService>();
667                 if (commandService != null)
668                 {
669                     commandService.ExecuteCommand(CommandValues.ShowProperties, null);
670                 }
671
672                 PropertyInspector propertiesGrid = this.designer.PropertyInspectorView as PropertyInspector;
673                 propertiesGrid.SelectPropertyByPath(entry.PropertyPath);
674                 if (ShouldShowSearchToolTip(itemToFocus))
675                 {
676                     Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
677                     {
678                         WorkflowViewElement viewElement = itemToFocus.View as WorkflowViewElement;
679                         if (viewElement != null)
680                         {
681                             this.adornerLayer = AdornerLayer.GetAdornerLayer(viewElement as WorkflowViewElement);
682                             if (this.adornerLayer != null)
683                             {
684                                 DesignerView designerView = this.editingContext.Services.GetService<DesignerView>();
685                                 string toolTipText = string.Format(CultureInfo.CurrentUICulture, SR.SearchHintText, entry.ModelProperty.Name);
686                                 this.toolTipAdorner = new SearchToolTipAdorner(viewElement, designerView, toolTipText);
687
688                                 viewElement.CustomItemStatus = "SearchToolTip=" + toolTipText;
689                                 this.lastWorkflowViewElement = viewElement;
690
691                                 this.adornerLayer.Add(this.toolTipAdorner);
692                             }
693                         }
694                     }), DispatcherPriority.ApplicationIdle);
695                 }
696             }
697
698             return true;
699         }
700
701         private bool ShouldShowSearchToolTip(ModelItem item)
702         {
703             return !typeof(WorkflowService).IsAssignableFrom(item.ItemType)
704                 && !typeof(ActivityBuilder).IsAssignableFrom(item.ItemType);
705         }
706
707         private void HighlightModelItem(ModelItem itemToFocus)
708         {
709             DesignerView designerView = this.editingContext.Services.GetService<DesignerView>();
710             double width = 0.0, height = 0.0;
711             Rect rectToBringIntoView;
712             FrameworkElement fe = (FrameworkElement)itemToFocus.View;
713             if (fe != null)
714             {
715                 width = Math.Min(fe.RenderSize.Width, designerView.ScrollViewer.ViewportWidth);
716                 height = Math.Min(fe.RenderSize.Height, designerView.ScrollViewer.ViewportHeight);
717                 rectToBringIntoView = new Rect(0, 0, width, height);
718             }
719             else
720             {
721                 rectToBringIntoView = Rect.Empty;
722             }
723             itemToFocus.Highlight(rectToBringIntoView);
724         }
725
726         private ModelItem FindModelItemToFocus(ModelItem itemToFocus)
727         {
728             WorkflowViewService viewService = this.WorkflowViewService;
729             if (viewService == null || itemToFocus == null)
730             {
731                 return itemToFocus;
732             }
733
734             ModelUtilities.ReverseTraverse(itemToFocus, (ModelItem modelItem) =>
735             {
736                 if (modelItem == null)
737                 {
738                     // continue;
739                     return true;
740                 }
741
742                 // if the item has Designer, we assume it can get focus.
743                 if (CanFocusOnModelItem(modelItem, viewService))
744                 {
745                     itemToFocus = modelItem;
746                     // break;
747                     return false;
748                 }
749
750                 // continue
751                 return true;
752             });
753
754             return itemToFocus;
755         }
756
757         private WorkflowViewService WorkflowViewService
758         {
759             get
760             {
761                 return (WorkflowViewService)this.editingContext.Services.GetService<ViewService>();
762             }
763         }
764
765         private static bool CanFocusOnModelItem(ModelItem itemToFocus, WorkflowViewService viewService)
766         {
767             Fx.Assert(itemToFocus != null && viewService != null, "null argument");
768
769             if (typeof(ITextExpression).IsAssignableFrom(itemToFocus.ItemType))
770             {
771                 return false;
772             }
773
774             Type designerType = viewService.GetDesignerType(itemToFocus.ItemType);
775             return typeof(WorkflowViewElement).IsAssignableFrom(designerType);
776         }
777     }
778 }