/************************************************************************************************* * Description: Implementation of the ListItemProvider class, which implements a * UI Automation provider for a list item in a custom control. * * See EntryPoint.cpp for a full description of this sample. * * * Copyright (C) Microsoft Corporation. All rights reserved. * * This source code is intended only as a supplement to Microsoft * Development Tools and/or on-line documentation. See these other * materials for detailed information regarding Microsoft code samples. * * THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A * PARTICULAR PURPOSE. * *************************************************************************************************/ #define INITGUID #include "UIAProviders.h" ListItemProvider::ListItemProvider(CustomListItem* pControl): m_refCount(1) { m_pListItemControl = pControl; m_pListControl = pControl->GetOwnerList(); } ListItemProvider::~ListItemProvider() { } // IUnknown implementation. IFACEMETHODIMP_(ULONG) ListItemProvider::AddRef() { return InterlockedIncrement(&m_refCount); } IFACEMETHODIMP_(ULONG) ListItemProvider::Release() { long val = InterlockedDecrement(&m_refCount); if (val == 0) { delete this; } return val; } IFACEMETHODIMP ListItemProvider::QueryInterface(REFIID riid, void** ppInterface) { if(riid == __uuidof(IUnknown)) *ppInterface =static_cast(this); else if(riid == __uuidof(IRawElementProviderSimple)) *ppInterface =static_cast(this); else if(riid == __uuidof(IRawElementProviderFragment)) *ppInterface =static_cast(this); else if(riid == __uuidof(ISelectionItemProvider)) *ppInterface =static_cast(this); else { *ppInterface = NULL; return E_NOINTERFACE; } (static_cast(*ppInterface))->AddRef(); return S_OK; } // IRawElementProviderSimple implementation // // Implementation of IRawElementProviderSimple::GetProviderOptions. // IFACEMETHODIMP ListItemProvider::get_ProviderOptions(ProviderOptions* pRetVal) { *pRetVal = ProviderOptions_ServerSideProvider; return S_OK; } // Implementation of IRawElementProviderSimple::GetPatternProvider. // Gets the object that supports the specified pattern. // IFACEMETHODIMP ListItemProvider::GetPatternProvider(PATTERNID patternId, IUnknown** pRetVal) { if (patternId == UIA_SelectionItemPatternId) { *pRetVal =static_cast(this); AddRef(); } else { *pRetVal = NULL; } return S_OK; } // Implementation of IRawElementProviderSimple::GetPropertyValue. // Gets custom properties. Because list items are not directly hosted in an HWND, // more properties should be supported here than for the list box itself. // IFACEMETHODIMP ListItemProvider::GetPropertyValue(PROPERTYID propertyId, VARIANT* pRetVal) { if (propertyId == UIA_AutomationIdPropertyId) { pRetVal->vt = VT_BSTR; int Id = m_pListItemControl->GetId(); // Convert int to BSTR. WCHAR idString[3]; swprintf_s(idString, 3, L"%d", Id); pRetVal->bstrVal = SysAllocString(idString); } else if (propertyId == UIA_NamePropertyId) { pRetVal->vt = VT_BSTR; pRetVal->bstrVal = SysAllocString(m_pListItemControl->GetName()); } else if (propertyId == UIA_ControlTypePropertyId) { pRetVal->vt = VT_I4; pRetVal->lVal = UIA_ListItemControlTypeId; } // HasKeyboardFocus is true if the list has focus, and this item is selected. else if(propertyId == UIA_HasKeyboardFocusPropertyId) { int myIndex = m_pListItemControl->GetItemIndex(); BOOL hasFocus = ((m_pListControl->GetSelectedIndex() == myIndex) && (m_pListControl->GetIsFocused() == true)); pRetVal->vt = VT_BOOL; pRetVal->boolVal = hasFocus ? VARIANT_TRUE : VARIANT_FALSE; } else if (propertyId == UIA_IsControlElementPropertyId) { pRetVal->vt = VT_BOOL; pRetVal->boolVal = VARIANT_TRUE; } else if (propertyId == UIA_IsContentElementPropertyId) { pRetVal->vt = VT_BOOL; pRetVal->boolVal = VARIANT_TRUE; } else if (propertyId == UIA_IsKeyboardFocusablePropertyId) { pRetVal->vt = VT_BOOL; pRetVal->boolVal = VARIANT_TRUE; } else if (propertyId == UIA_ItemStatusPropertyId) { pRetVal->vt = VT_BSTR; if (m_pListItemControl->GetStatus() == Status_Online) { pRetVal->bstrVal = SysAllocString(L"Online"); } else { pRetVal->bstrVal = SysAllocString(L"Offline"); } } else { pRetVal->vt = VT_EMPTY; } return S_OK; } // Implementation of IRawElementProviderSimple::get_HostRawElementProvider. // Gets the UI Automation provider for the host window. // Return NULL. because the list items are not directly hosted in a window. // IFACEMETHODIMP ListItemProvider::get_HostRawElementProvider(IRawElementProviderSimple** pRetVal) { *pRetVal = NULL; return S_OK; } // IRawElementProviderFragment implementation. // // Implementation of IRawElementProviderFragment::Navigate. // Enables UI Automation to locate the element in the tree. // IFACEMETHODIMP ListItemProvider::Navigate(NavigateDirection direction, IRawElementProviderFragment ** pRetVal) { IRawElementProviderFragment* pFrag = NULL; switch(direction) { case NavigateDirection_Parent: pFrag = m_pListControl->GetListProvider(); break; case NavigateDirection_NextSibling: { int myIndex = m_pListItemControl->GetItemIndex(); if (myIndex == m_pListControl->GetCount() - 1) { pFrag = NULL; break; } LISTITERATOR nextIter = m_pListControl->GetItemAt(myIndex + 1); CustomListItem* pNext = (CustomListItem*)(*nextIter); pFrag = pNext->GetListItemProvider(); break; } case NavigateDirection_PreviousSibling: { int myIndex = m_pListItemControl->GetItemIndex(); if (myIndex <= 0) { pFrag = NULL; break; } LISTITERATOR nextIter = m_pListControl->GetItemAt(myIndex - 1); CustomListItem* pPrev = static_cast(*nextIter); pFrag = pPrev->GetListItemProvider(); break; } } *pRetVal = pFrag; if (pFrag != NULL) { pFrag->AddRef(); } return S_OK; } // Implementation of IRawElementProviderFragment::GetRuntimeId. // Gets the runtime identifier. This is an array consisting of UiaAppendRuntimeId, // which makes the ID unique among instances of the control, and the Automation Id. // IFACEMETHODIMP ListItemProvider::GetRuntimeId(SAFEARRAY ** pRetVal) { int id = m_pListItemControl->GetId(); int rId[] = { UiaAppendRuntimeId, id }; SAFEARRAY *psa = SafeArrayCreateVector(VT_I4, 0, 2); for (LONG i = 0; i < 2; i++) { SafeArrayPutElement(psa, &i, &(rId[i])); } *pRetVal = psa; return S_OK; } // Implementation of IRawElementProviderFragment::get_BoundingRectangle. // Gets the bounding rectangle of the item, in screen coordinates. // IFACEMETHODIMP ListItemProvider::get_BoundingRectangle(UiaRect * pRetVal) { IRawElementProviderFragment* pParent = m_pListControl->GetListProvider(); int myIndex = m_pListItemControl->GetItemIndex(); UiaRect parentRect; HRESULT hr = pParent->get_BoundingRectangle(&parentRect); if (SUCCEEDED(hr)) { pRetVal->left = parentRect.left; pRetVal->top = parentRect.top + (m_pListControl->ItemHeight * myIndex); pRetVal->width = parentRect.width; pRetVal->height = m_pListControl->ItemHeight; } return hr; } // Implementation of IRawElementProviderFragment::GetEmbeddedFragmentRoots. // Retrieves any fragment roots that may be hosted in this element. // IFACEMETHODIMP ListItemProvider::GetEmbeddedFragmentRoots(SAFEARRAY ** pRetVal) { *pRetVal = NULL; return S_OK; } // Implementation of IRawElementProviderFragment::SetFocus. // Responds to the control receiving focus through a UI Automation request. // IFACEMETHODIMP ListItemProvider::SetFocus() { Select(); return S_OK; } // Implementation of IRawElementProviderFragment::get_FragmentRoot. // Retrieves the root element of this fragment. // IFACEMETHODIMP ListItemProvider::get_FragmentRoot(IRawElementProviderFragmentRoot** pRetVal) { IRawElementProviderFragmentRoot* pRoot = this->m_pListControl->GetListProvider(); if (pRoot == NULL) { return E_FAIL; } pRoot->AddRef(); *pRetVal = pRoot; return S_OK; } // ISelectionItemProvider implementation. // Implementation of ISelectionItemProvider::Select. // Responds to a request by UI Automation to select the item. // IFACEMETHODIMP ListItemProvider::Select() { int index = m_pListItemControl->GetItemIndex(); m_pListControl->SelectItem(index); // Force refresh even when app doesn't have focus. InvalidateRect(m_pListControl->GetHwnd(), NULL, false); return S_OK; } // Implementation of ISelectionItemProvider::AddToSelection. // Responds to a request by UI Automation to add the item to the selection. // Because this is a single-selection list box, the call is equivalent to Select(). // IFACEMETHODIMP ListItemProvider::AddToSelection() { Select(); return S_OK; } // Implementation of ISelectionItemProvider::RemoveFromSelection. // Responds to a request by UI Automation to remove the item from the selection. // One and only one item must always be selected, so this is not implemented. // IFACEMETHODIMP ListItemProvider::RemoveFromSelection() { return UIA_E_INVALIDOPERATION; } // Implementation of ISelectionItemProvider::get_IsSelected. // Advises whether the item is selected. // IFACEMETHODIMP ListItemProvider::get_IsSelected(BOOL *pRetVal) { *pRetVal = (m_pListItemControl->GetItemIndex() == m_pListControl->GetSelectedIndex()); return S_OK; } // Implementation of ISelectionItemProvider::get_SelectionContainer. // Returns the UI Automation provider for the list control. // IFACEMETHODIMP ListItemProvider::get_SelectionContainer( IRawElementProviderSimple **pRetVal) { IRawElementProviderSimple* pParent = static_cast(m_pListControl->GetListProvider()); pParent->AddRef(); *pRetVal = pParent; return S_OK; } // Raises an event when an item is added to the list. // void ListItemProvider::NotifyItemAdded() { if (UiaClientsAreListening()) { UiaRaiseStructureChangedEvent(this, StructureChangeType_ChildAdded, NULL, 0); } } // Raises an event when an item is removed from the list. // // StructureType_ChildRemoved is unusual in that it is raised on the parent provider, // since the child provider may not exist anymore, but it uses the child's runtime ID. void ListItemProvider::NotifyItemRemoved() { if (UiaClientsAreListening()) { ListProvider* listProvider = m_pListControl->GetListProvider(); // Construct the partial runtime ID for the removed child int id = m_pListItemControl->GetId(); int rId[] = { UiaAppendRuntimeId, id }; UiaRaiseStructureChangedEvent(listProvider, StructureChangeType_ChildRemoved, rId, ARRAYSIZE(rId)); } } // Raises an event when a list item is selected. // void ListItemProvider::NotifyElementSelected() { if (UiaClientsAreListening()) { UiaRaiseAutomationEvent(this, UIA_SelectionItem_ElementSelectedEventId); } }