2025-11-28 00:35:46 +09:00

396 lines
12 KiB
C++

/*************************************************************************************************
* 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.
*
*************************************************************************************************/
#pragma once
#define INITGUID
#include "UIAProviders.h"
extern UiaIds AutoIds;
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<IRawElementProviderSimple*>(this);
else if(riid == __uuidof(IRawElementProviderSimple)) *ppInterface =static_cast<IRawElementProviderSimple*>(this);
else if(riid == __uuidof(IRawElementProviderFragment)) *ppInterface =static_cast<IRawElementProviderFragment*>(this);
else if(riid == __uuidof(ISelectionItemProvider)) *ppInterface =static_cast<ISelectionItemProvider*>(this);
else
{
*ppInterface = NULL;
return E_NOINTERFACE;
}
(static_cast<IUnknown*>(*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 == AutoIds.SelectionItemPattern)
{
*pRetVal =static_cast<IRawElementProviderSimple*>(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 == AutoIds.AutomationIdProperty)
{
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 == AutoIds.NameProperty)
{
pRetVal->vt = VT_BSTR;
pRetVal->bstrVal = SysAllocString(m_pListItemControl->GetName());
}
else if (propertyId == AutoIds.ControlTypeProperty)
{
pRetVal->vt = VT_I4;
pRetVal->lVal = AutoIds.ListItemControlType;
}
// HasKeyboardFocus is true if the list has focus, and this item is selected.
else if(propertyId == AutoIds.HasKeyboardFocusProperty)
{
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 == AutoIds.IsControlElementProperty)
{
pRetVal->vt = VT_BOOL;
pRetVal->boolVal = VARIANT_TRUE;
}
else if (propertyId == AutoIds.IsContentElementProperty)
{
pRetVal->vt = VT_BOOL;
pRetVal->boolVal = VARIANT_TRUE;
}
else if (propertyId == AutoIds.IsKeyboardFocusableProperty)
{
pRetVal->vt = VT_BOOL;
pRetVal->boolVal = VARIANT_TRUE;
}
else if (propertyId == AutoIds.ItemStatusProperty)
{
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<CustomListItem*>(*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<IRawElementProviderSimple*>(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, AutoIds.ElementSelectedEvent);
}
}