1 
2 #include "studiox_includes.h"
3 #include "uiautomation.h"
4 #include "resource_view_provider.h"
5 #include "resource_item_provider.h"
6 
7 ///////////////////////////////////////////////////////////////////////////////
resource_item_provider(resource_item * pControl)8 resource_item_provider::resource_item_provider(resource_item *pControl)
9 {
10     m_refCount = 1;
11     m_pResItemControl = pControl;
12 
13     resource_tree *pTree = m_pResItemControl->GetResTree();
14     m_pResViewControl = pTree->GetParent();
15 }
16 
17 ///////////////////////////////////////////////////////////////////////////////
~resource_item_provider()18 resource_item_provider::~resource_item_provider()
19 {
20 }
21 
22 ///////////////////////////////////////////////////////////////////////////////
23 // IUnknown implementation.
24 //
25 ///////////////////////////////////////////////////////////////////////////////
IFACEMETHODIMP_(ULONG)26 IFACEMETHODIMP_(ULONG) resource_item_provider::AddRef()
27 {
28     return InterlockedIncrement(&m_refCount);
29 }
30 
31 ///////////////////////////////////////////////////////////////////////////////
IFACEMETHODIMP_(ULONG)32 IFACEMETHODIMP_(ULONG) resource_item_provider::Release()
33 {
34     long val = InterlockedDecrement(&m_refCount);
35     if (val == 0)
36     {
37         delete this;
38     }
39     return val;
40 }
41 
42 ///////////////////////////////////////////////////////////////////////////////
QueryInterface(REFIID riid,void ** ppInterface)43 IFACEMETHODIMP resource_item_provider::QueryInterface(REFIID riid, void** ppInterface)
44 {
45     if (riid == __uuidof(IUnknown))                              *ppInterface = static_cast<IRawElementProviderSimple*>(this);
46     else if (riid == __uuidof(IRawElementProviderSimple))        *ppInterface = static_cast<IRawElementProviderSimple*>(this);
47     else if (riid == __uuidof(IRawElementProviderFragment))      *ppInterface = static_cast<IRawElementProviderFragment*>(this);
48     else if (riid == __uuidof(ISelectionItemProvider))           *ppInterface = static_cast<ISelectionItemProvider*>(this);
49     else if (riid == __uuidof(IExpandCollapseProvider))          *ppInterface = static_cast<IExpandCollapseProvider*>(this);
50     else if (riid == __uuidof(IScrollItemProvider))              *ppInterface = static_cast<IScrollItemProvider*>(this);
51     else
52     {
53         *ppInterface = NULL;
54         return E_NOINTERFACE;
55     }
56     (static_cast<IUnknown*>(*ppInterface))->AddRef();
57     return S_OK;
58 }
59 
60 ///////////////////////////////////////////////////////////////////////////////
61 // IRawElementProviderSimple implementation
62 //
63 // Implementation of IRawElementProviderSimple::GetProviderOptions.
64 //
65 ///////////////////////////////////////////////////////////////////////////////
get_ProviderOptions(ProviderOptions * pRetVal)66 IFACEMETHODIMP resource_item_provider::get_ProviderOptions(ProviderOptions* pRetVal)
67 {
68 
69     *pRetVal = ProviderOptions_ServerSideProvider;
70     return S_OK;
71 }
72 
73 ///////////////////////////////////////////////////////////////////////////////
74 // Implementation of IRawElementProviderSimple::GetPatternProvider.
75 // Gets the object that supports the specified pattern.
76 //
77 ///////////////////////////////////////////////////////////////////////////////
GetPatternProvider(PATTERNID patternId,IUnknown ** pRetVal)78 IFACEMETHODIMP resource_item_provider::GetPatternProvider(PATTERNID patternId, IUnknown** pRetVal)
79 {
80     if ((patternId == UIA_SelectionItemPatternId) ||
81         (patternId == UIA_ExpandCollapsePatternId) ||
82         (patternId == UIA_ScrollItemPatternId))
83     {
84         *pRetVal = static_cast<IRawElementProviderSimple*>(this);
85         AddRef();
86     }
87     else
88     {
89         *pRetVal = NULL;
90     }
91     return S_OK;
92 }
93 
94 ///////////////////////////////////////////////////////////////////////////////
95 // Implementation of IRawElementProviderSimple::GetPropertyValue.
96 // Gets custom properties.
97 //
98 ///////////////////////////////////////////////////////////////////////////////
GetPropertyValue(PROPERTYID propertyId,VARIANT * pRetVal)99 IFACEMETHODIMP resource_item_provider::GetPropertyValue(PROPERTYID propertyId, VARIANT* pRetVal)
100 {
101     switch (propertyId)
102     {
103     case UIA_SizeOfSetPropertyId:
104         pRetVal->vt = VT_I4;
105         pRetVal->lVal = GetCurrentSizeOfSet();
106         break;
107 
108     case UIA_PositionInSetPropertyId:
109         pRetVal->vt = VT_I4;
110         pRetVal->lVal = GetCurrentPositionInSet();;
111         break;
112 
113     case UIA_LevelPropertyId:
114         pRetVal->vt = VT_I4;
115         pRetVal->lVal = GetCurrentLevel();
116         break;
117 
118     case UIA_AutomationIdPropertyId:
119     {
120         pRetVal->vt = VT_BSTR;
121 
122         // Convert integer to string.
123         CString str;
124         str.Format(L"%d", m_pResItemControl->GetId());
125         pRetVal->bstrVal = SysAllocString(str);
126         break;
127     }
128 
129     case UIA_NamePropertyId:
130         pRetVal->vt = VT_BSTR;
131         pRetVal->bstrVal = SysAllocString(m_pResItemControl->GetName());
132         break;
133 
134     case UIA_ControlTypePropertyId:
135         pRetVal->vt = VT_I4;
136         pRetVal->lVal = UIA_TreeItemControlTypeId;
137         break;
138 
139     case UIA_HasKeyboardFocusPropertyId:
140     {
141         // HasKeyboardFocus is true if the list has focus, and this item is selected.
142         resource_item* selected = m_pResViewControl->GetCurrentItem();
143         BOOL hasFocus = (selected == m_pResItemControl);
144         pRetVal->vt = VT_BOOL;
145         pRetVal->boolVal = hasFocus ? VARIANT_TRUE : VARIANT_FALSE;
146     }
147     break;
148 
149     case UIA_IsExpandCollapsePatternAvailablePropertyId:
150     case UIA_IsControlElementPropertyId:
151     case UIA_IsContentElementPropertyId:
152     case UIA_IsKeyboardFocusablePropertyId:
153         pRetVal->vt = VT_BOOL;
154         pRetVal->boolVal = VARIANT_TRUE;
155         break;
156 
157     case UIA_ExpandCollapseExpandCollapseStatePropertyId:
158     {
159         ExpandCollapseState state;
160         get_ExpandCollapseState(&state);
161         pRetVal->vt = VT_I4;
162         pRetVal->lVal = state;
163     }
164         break;
165 
166     case UIA_IsOffscreenPropertyId:
167         pRetVal->vt = VT_BOOL;
168 
169         if (m_pResItemControl->IsVisible())
170         {
171             pRetVal->boolVal = VARIANT_FALSE;
172         }
173         else
174         {
175             pRetVal->boolVal = VARIANT_TRUE;
176         }
177 
178         if (m_pResItemControl->mpRes)
179         {
180             if (m_pResItemControl->mpRes->type == RES_TYPE_FOLDER)
181             {
182                 switch (m_pResItemControl->mpRes->folder_id)
183                 {
184                 case DEFAULT_COLOR_FOLDER:
185                 case CUSTOM_COLOR_FOLDER:
186                 case DEFAULT_FONT_FOLDER:
187                 case CUSTOM_FONT_FOLDER:
188                     pRetVal->boolVal = VARIANT_TRUE;
189                     break;
190                 }
191             }
192             else if (!m_pResItemControl->Parent())
193             {
194                 pRetVal->boolVal = VARIANT_TRUE;
195             }
196         }
197         break;
198 
199     case UIA_FullDescriptionPropertyId:
200     case UIA_ProviderDescriptionPropertyId:
201         pRetVal->vt = VT_BSTR;
202         pRetVal->bstrVal = SysAllocString(m_pResItemControl->GetDescription());
203         break;
204 
205     default:
206         pRetVal->vt = VT_EMPTY;
207         break;
208     }
209 
210     return S_OK;
211 }
212 
213 ///////////////////////////////////////////////////////////////////////////////
214 // Implementation of IRawElementProviderSimple::get_HostRawElementProvider.
215 // Gets the UI Automation provider for the host window.
216 // Return NULL. because the tree view items are not directly hosted in a window.
217 //
218 ///////////////////////////////////////////////////////////////////////////////
get_HostRawElementProvider(IRawElementProviderSimple ** pRetVal)219 IFACEMETHODIMP resource_item_provider::get_HostRawElementProvider(IRawElementProviderSimple** pRetVal)
220 {
221     *pRetVal = NULL;
222     return S_OK;
223 }
224 
225 ///////////////////////////////////////////////////////////////////////////////
226 // IRawElementProviderFragment implementation.
227 //
228 // Implementation of IRawElementProviderFragment::Navigate.
229 // Enables UI Automation to locate the element in the tree.
230 //
231 ///////////////////////////////////////////////////////////////////////////////
Navigate(NavigateDirection direction,IRawElementProviderFragment ** pRetVal)232 IFACEMETHODIMP resource_item_provider::Navigate(NavigateDirection direction, IRawElementProviderFragment** pRetVal)
233 {
234     IRawElementProviderFragment* pFrag = NULL;
235     resource_item* pItem = NULL;
236 
237     switch (direction)
238     {
239     case NavigateDirection_Parent:
240         pItem = m_pResItemControl->NavigateParent();
241 
242         if (pItem)
243         {
244             pFrag = pItem->GetResItemProvider();
245         }
246         else
247         {
248             pFrag = m_pResViewControl->GetResViewProvider();
249         }
250         break;
251 
252     case NavigateDirection_NextSibling:
253         pItem = m_pResItemControl->NavigateNext();
254 
255         if (pItem)
256         {
257             pFrag = pItem->GetResItemProvider();
258         }
259         break;
260 
261     case NavigateDirection_FirstChild:
262         pItem = m_pResItemControl->NavigateFirst();
263 
264         if (pItem)
265         {
266             pFrag = pItem->GetResItemProvider();
267         }
268         break;
269 
270     case NavigateDirection_LastChild:
271         pItem = m_pResItemControl->NavigateLast();
272 
273         if (pItem)
274         {
275             pFrag = pItem->GetResItemProvider();
276         }
277         break;
278     }
279     *pRetVal = pFrag;
280     if (pFrag != NULL)
281     {
282         pFrag->AddRef();
283     }
284     return S_OK;
285 }
286 
287 ///////////////////////////////////////////////////////////////////////////////
288 // Implementation of IRawElementProviderFragment::GetRuntimeId.
289 // Gets the runtime identifier. This is an array consisting of UiaAppendRuntimeId,
290 // which makes the ID unique among instances of the control, and the Automation Id.
291 //
292 ///////////////////////////////////////////////////////////////////////////////
GetRuntimeId(SAFEARRAY ** pRetVal)293 IFACEMETHODIMP resource_item_provider::GetRuntimeId(SAFEARRAY** pRetVal)
294 {
295     int id = m_pResItemControl->GetId();
296     int rId[] = { UiaAppendRuntimeId, id };
297 
298     SAFEARRAY* psa = SafeArrayCreateVector(VT_I4, 0, 2);
299     for (LONG i = 0; i < 2; i++)
300     {
301         SafeArrayPutElement(psa, &i, &(rId[i]));
302     }
303     *pRetVal = psa;
304 
305     return S_OK;
306 }
307 
308 ///////////////////////////////////////////////////////////////////////////////
309 // Implementation of IRawElementProviderFragment::get_BoundingRectangle.
310 // Gets the bounding rectangle of the item, in screen coordinates.
311 //
312 ///////////////////////////////////////////////////////////////////////////////
get_BoundingRectangle(UiaRect * pRetVal)313 IFACEMETHODIMP resource_item_provider::get_BoundingRectangle(UiaRect* pRetVal)
314 {
315     CRect rect = m_pResItemControl->GetPos();
316 
317     m_pResViewControl->ClientToScreen(&rect);
318     SCROLLINFO si;
319     si.cbSize = sizeof(SCROLLINFO);
320     si.fMask = SIF_POS;
321     SendMessage(m_pResViewControl->GetSafeHwnd(), SBM_GETSCROLLINFO, 0, (LPARAM)&si);
322 
323     int height = rect.Height();
324     if ((m_pResItemControl->GetResType() == RES_TYPE_GROUP) && m_pResItemControl->IsOpen())
325     {
326         height -= m_pResItemControl->GetItemHeight();
327     }
328 
329     pRetVal->left = rect.left;
330     pRetVal->top = rect.top - si.nPos;
331     pRetVal->width = rect.Width();
332     pRetVal->height = height;
333     return S_OK;
334 }
335 
336 ///////////////////////////////////////////////////////////////////////////////
337 // Implementation of IRawElementProviderFragment::GetEmbeddedFragmentRoots.
338 // Retrieves any fragment roots that may be hosted in this element.
339 //
340 ///////////////////////////////////////////////////////////////////////////////
GetEmbeddedFragmentRoots(SAFEARRAY ** pRetVal)341 IFACEMETHODIMP resource_item_provider::GetEmbeddedFragmentRoots(SAFEARRAY** pRetVal)
342 {
343     *pRetVal = NULL;
344     return S_OK;
345 }
346 
347 ///////////////////////////////////////////////////////////////////////////////
348 // Implementation of IRawElementProviderFragment::SetFocus.
349 // Responds to the control receiving focus through a UI Automation request.
350 //
351 ///////////////////////////////////////////////////////////////////////////////
SetFocus()352 IFACEMETHODIMP resource_item_provider::SetFocus()
353 {
354     Select();
355     return S_OK;
356 }
357 
358 ///////////////////////////////////////////////////////////////////////////////
359 // Implementation of IRawElementProviderFragment::get_FragmentRoot.
360 // Retrieves the root element of this fragment.
361 //
362 ///////////////////////////////////////////////////////////////////////////////
get_FragmentRoot(IRawElementProviderFragmentRoot ** pRetVal)363 IFACEMETHODIMP resource_item_provider::get_FragmentRoot(IRawElementProviderFragmentRoot** pRetVal)
364 {
365     IRawElementProviderFragmentRoot* pRoot = this->m_pResViewControl->GetResViewProvider();
366     if (pRoot == NULL)
367     {
368         return E_FAIL;
369     }
370     pRoot->AddRef();
371     *pRetVal = pRoot;
372     return S_OK;
373 }
374 
375 
376 ///////////////////////////////////////////////////////////////////////////////
377 // ISelectionItemProvider implementation.
378 //
379 // Implementation of ISelectionItemProvider::Select.
380 // Responds to a request by UI Automation to select the item.
381 //
382 ///////////////////////////////////////////////////////////////////////////////
Select()383 IFACEMETHODIMP resource_item_provider::Select()
384 {
385     m_pResViewControl->SetCurrentItem(m_pResItemControl);
386 
387     // Force refresh even when app doesn't have focus.
388     InvalidateRect(m_pResViewControl->GetSafeHwnd(), NULL, false);
389     return S_OK;
390 }
391 
392 ///////////////////////////////////////////////////////////////////////////////
393 // Implementation of ISelectionItemProvider::AddToSelection.
394 // Responds to a request by UI Automation to add the item to the selection.
395 // Because this is a single-selection list box, the call is equivalent to Select().
396 //
397 ///////////////////////////////////////////////////////////////////////////////
AddToSelection()398 IFACEMETHODIMP resource_item_provider::AddToSelection()
399 {
400     Select();
401     return S_OK;
402 }
403 
404 ///////////////////////////////////////////////////////////////////////////////
405 // Implementation of ISelectionItemProvider::RemoveFromSelection.
406 // Responds to a request by UI Automation to remove the item from the selection.
407 // One and only one item must always be selected, so this is not implemented.
408 //
409 ///////////////////////////////////////////////////////////////////////////////
RemoveFromSelection()410 IFACEMETHODIMP resource_item_provider::RemoveFromSelection()
411 {
412     return UIA_E_INVALIDOPERATION;
413 }
414 
415 ///////////////////////////////////////////////////////////////////////////////
416 // Implementation of ISelectionItemProvider::get_IsSelected.
417 // Advises whether the item is selected.
418 //
419 ///////////////////////////////////////////////////////////////////////////////
get_IsSelected(BOOL * pRetVal)420 IFACEMETHODIMP resource_item_provider::get_IsSelected(BOOL* pRetVal)
421 {
422     *pRetVal = (m_pResItemControl == m_pResViewControl->GetCurrentItem());
423     return S_OK;
424 }
425 
426 ///////////////////////////////////////////////////////////////////////////////
427 // Implementation of ISelectionItemProvider::get_SelectionContainer.
428 // Returns the UI Automation provider for the tree view control.
429 //
430 ///////////////////////////////////////////////////////////////////////////////
get_SelectionContainer(IRawElementProviderSimple ** pRetVal)431 IFACEMETHODIMP resource_item_provider::get_SelectionContainer(
432     IRawElementProviderSimple** pRetVal)
433 {
434     IRawElementProviderSimple* pParent =
435         static_cast<IRawElementProviderSimple*>(m_pResViewControl->GetResViewProvider());
436     pParent->AddRef();
437     *pRetVal = pParent;
438     return S_OK;
439 }
440 
441 ///////////////////////////////////////////////////////////////////////////////
442 // Implementation of IExpandCollapseProvider::Expand.
443 // Hides all nodes, controls, or content that are descendants of the control.
444 //
445 ///////////////////////////////////////////////////////////////////////////////
Expand()446 IFACEMETHODIMP resource_item_provider::Expand()
447 {
448     switch (m_pResItemControl->GetResType())
449     {
450     case RES_TYPE_GROUP:
451     case RES_TYPE_FOLDER:
452         m_pResItemControl->Open();
453         break;
454     }
455 
456     return S_OK;
457 }
458 
459 ///////////////////////////////////////////////////////////////////////////////
460 // Implementation of IExpandCollapseProvider::Collapse.
461 // Displays all child nodes, controls, or content of the control.
462 //
463 ///////////////////////////////////////////////////////////////////////////////
Collapse()464 IFACEMETHODIMP resource_item_provider::Collapse()
465 {
466     switch (m_pResItemControl->GetResType())
467     {
468     case RES_TYPE_GROUP:
469     case RES_TYPE_FOLDER:
470         m_pResItemControl->Close();
471         break;
472     }
473 
474     return S_OK;
475 }
476 
477 ///////////////////////////////////////////////////////////////////////////////
478 // Implementation of IExpandCollapseProvider::get_ExpandCollapseState.
479 // Gets the state, expanded or collapsed, of the control.
480 //
481 ///////////////////////////////////////////////////////////////////////////////
get_ExpandCollapseState(ExpandCollapseState * pRetVal)482 IFACEMETHODIMP resource_item_provider::get_ExpandCollapseState(ExpandCollapseState* pRetVal)
483 {
484     switch (m_pResItemControl->GetResType())
485     {
486     case RES_TYPE_GROUP:
487     case RES_TYPE_FOLDER:
488         if (m_pResItemControl->IsOpen())
489         {
490             *pRetVal = ExpandCollapseState_Expanded;
491         }
492         else
493         {
494             *pRetVal = ExpandCollapseState_Collapsed;
495         }
496         break;
497 
498     default:
499         *pRetVal = ExpandCollapseState_LeafNode;
500         break;
501     }
502 
503     return S_OK;
504 }
505 
506 ///////////////////////////////////////////////////////////////////////////////
507 // Implementation of IScrollItemProvider::ScrollIntoView.
508 // Scrolls the content area of a container object in order to display the
509 // control within the visible region of the container.
510 //
511 ///////////////////////////////////////////////////////////////////////////////
ScrollIntoView()512 IFACEMETHODIMP resource_item_provider::ScrollIntoView()
513 {
514     m_pResViewControl->ScrollIntoView(m_pResItemControl);
515 
516     return S_OK;
517 }
518 
519 ///////////////////////////////////////////////////////////////////////////////
520 // Raises an event when an element is selected.
521 //
522 ///////////////////////////////////////////////////////////////////////////////
RaiseSelectElementEvent()523 void resource_item_provider::RaiseSelectElementEvent()
524 {
525     if (UiaClientsAreListening())
526     {
527         UiaRaiseAutomationEvent(this, UIA_SelectionItem_ElementSelectedEventId);
528     }
529 }
530 
531 ///////////////////////////////////////////////////////////////////////////////
532 // Raises an event when the focus has changed from one element to another.
533 //
534 ///////////////////////////////////////////////////////////////////////////////
RaiseChangeFocusEvent()535 void resource_item_provider::RaiseChangeFocusEvent()
536 {
537     if (UiaClientsAreListening())
538     {
539         UiaRaiseAutomationEvent(this, UIA_AutomationFocusChangedEventId);
540     }
541 }
542 
543 ///////////////////////////////////////////////////////////////////////////////
544 // Raises an event when a property is changed.
545 //
546 ///////////////////////////////////////////////////////////////////////////////
RaiseExpandCollapseAutomationEvent(BOOL oldValue,BOOL newValue)547 void resource_item_provider::RaiseExpandCollapseAutomationEvent(BOOL oldValue, BOOL newValue)
548 {
549     if (UiaClientsAreListening())
550     {
551         VARIANT oldVar;
552         VARIANT newVar;
553         oldVar.vt = VT_I4;
554         oldVar.lVal = oldValue ? ExpandCollapseState_Expanded : ExpandCollapseState_Collapsed;
555 
556         newVar.vt = VT_I4;
557         newVar.lVal = newValue ? ExpandCollapseState_Expanded : ExpandCollapseState_Collapsed;
558 
559         UiaRaiseAutomationPropertyChangedEvent(this, UIA_ExpandCollapseExpandCollapseStatePropertyId, oldVar, newVar);
560     }
561 }
562 
563 ///////////////////////////////////////////////////////////////////////////////
564 // Calcualte the position in the set for the element.
565 //
566 ///////////////////////////////////////////////////////////////////////////////
GetCurrentPositionInSet()567 int resource_item_provider::GetCurrentPositionInSet()
568 {
569     int pos = 1;
570     resource_item* parent;
571 
572     parent = m_pResItemControl->NavigateParent();
573 
574     if (parent)
575     {
576         resource_item* child;
577         child = parent->NavigateFirst();
578 
579         while (child)
580         {
581             if (child == m_pResItemControl)
582             {
583                 break;
584             }
585             child = child->NavigateNext();
586             pos++;
587         }
588     }
589 
590     return pos;
591 }
592 
593 ///////////////////////////////////////////////////////////////////////////////
594 // Calculate the size of the set where the element is located.
595 //
596 ///////////////////////////////////////////////////////////////////////////////
GetCurrentSizeOfSet()597 int resource_item_provider::GetCurrentSizeOfSet()
598 {
599     int size = 0;
600     resource_item* parent;
601 
602     parent = m_pResItemControl->NavigateParent();
603 
604     if (parent)
605     {
606         resource_item* child = parent->NavigateFirst();
607         while (child)
608         {
609             child = child->NavigateNext();
610             size++;
611         }
612     }
613 
614     return size;
615 }
616 
617 ///////////////////////////////////////////////////////////////////////////////
618 // Calculate current level for the element.
619 //
620 ///////////////////////////////////////////////////////////////////////////////
GetCurrentLevel()621 int resource_item_provider::GetCurrentLevel()
622 {
623     int level = 1;
624     resource_item* parent = m_pResItemControl->NavigateParent();
625 
626     while (parent)
627     {
628         parent = parent->NavigateParent();
629         level++;
630     }
631 
632     return level;
633 }
634 
635