summaryrefslogtreecommitdiff
path: root/winaccessibility
diff options
context:
space:
mode:
authorMichael Weghorn <m.weghorn@posteo.de>2024-08-22 13:39:34 +0100
committerMichael Weghorn <m.weghorn@posteo.de>2024-08-23 07:40:17 +0200
commit2318b823732df3820557943e56dd5243381dc558 (patch)
treebc3c201aa284c9d23e4eb3f63185e128ef7e72b8 /winaccessibility
parent002a29b8dd0d4c61194227e4151b4847480cb8b1 (diff)
tdf#91739 wina11y: Implement IAccessible2_2::get_relationTargetsOfType
Implement the `IAccessible2_2::get_relationTargetsOfType` method [1] in the Windows accessibility bridge. As described in NVDA issue comment [2], this method is the preferred way that NVDA uses to get e.g. the target object of the "flowsTo" relation. With this commit in place, retrieving the next Writer paragraph in the NVDA Python console using that relation now also works with the fallback code path in NVDA [3] dropped in a local build of NVDA: >>> focus.flowsTo <NVDAObjects.Dynamic_SymphonyParagraphSymphonyTextEditableTextWithAutoSelectDetectionIAccessible object at 0x0B8F5890> [1] https://accessibility.linuxfoundation.org/a11yspecs/ia2/docs/html/interface_i_accessible2__2.html#a63f214b322c663caf01d4bb67277f5af [2] https://github.com/nvaccess/nvda/issues/4115#issuecomment-2299084993 [3] https://github.com/nvaccess/nvda/blob/df6bd0770db12ff73905251054d12f01911030dd/source/NVDAObjects/IAccessible/__init__.py#L1861-L1874 Change-Id: Ifcd15527b4a3f5302168942f19248a0ba9a4d306 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172258 Tested-by: Jenkins Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
Diffstat (limited to 'winaccessibility')
-rw-r--r--winaccessibility/source/UAccCOM/AccRelation.cxx27
-rw-r--r--winaccessibility/source/UAccCOM/AccRelation.h1
-rw-r--r--winaccessibility/source/UAccCOM/MAccessible.cxx68
3 files changed, 94 insertions, 2 deletions
diff --git a/winaccessibility/source/UAccCOM/AccRelation.cxx b/winaccessibility/source/UAccCOM/AccRelation.cxx
index 50de3dd63a57..20bfa20ffa97 100644
--- a/winaccessibility/source/UAccCOM/AccRelation.cxx
+++ b/winaccessibility/source/UAccCOM/AccRelation.cxx
@@ -224,4 +224,31 @@ const wchar_t* CAccRelation::mapToIA2RelationType(sal_Int16 nUnoRelationType)
}
}
+sal_Int16 CAccRelation::mapToUnoRelationType(const BSTR aIA2RelationType)
+{
+ if (wcscmp(aIA2RelationType, IA2_RELATION_FLOWS_FROM) == 0)
+ return AccessibleRelationType::CONTENT_FLOWS_FROM;
+ if (wcscmp(aIA2RelationType, IA2_RELATION_FLOWS_TO) == 0)
+ return AccessibleRelationType::CONTENT_FLOWS_TO;
+ if (wcscmp(aIA2RelationType, IA2_RELATION_CONTROLLED_BY) == 0)
+ return AccessibleRelationType::CONTROLLED_BY;
+ if (wcscmp(aIA2RelationType, IA2_RELATION_CONTROLLER_FOR) == 0)
+ return AccessibleRelationType::CONTROLLER_FOR;
+ if (wcscmp(aIA2RelationType, IA2_RELATION_LABEL_FOR) == 0)
+ return AccessibleRelationType::LABEL_FOR;
+ if (wcscmp(aIA2RelationType, IA2_RELATION_LABELED_BY) == 0)
+ return AccessibleRelationType::LABELED_BY;
+ if (wcscmp(aIA2RelationType, IA2_RELATION_MEMBER_OF) == 0)
+ return AccessibleRelationType::MEMBER_OF;
+ if (wcscmp(aIA2RelationType, IA2_RELATION_SUBWINDOW_OF) == 0)
+ return AccessibleRelationType::SUB_WINDOW_OF;
+ if (wcscmp(aIA2RelationType, IA2_RELATION_NODE_CHILD_OF) == 0)
+ return AccessibleRelationType::NODE_CHILD_OF;
+ if (wcscmp(aIA2RelationType, IA2_RELATION_DESCRIBED_BY) == 0)
+ return AccessibleRelationType::DESCRIBED_BY;
+
+ // not all IAccessible2 relation types have a UNO equivalent
+ return AccessibleRelationType::INVALID;
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/winaccessibility/source/UAccCOM/AccRelation.h b/winaccessibility/source/UAccCOM/AccRelation.h
index a576e23aa952..de709a9a434f 100644
--- a/winaccessibility/source/UAccCOM/AccRelation.h
+++ b/winaccessibility/source/UAccCOM/AccRelation.h
@@ -75,6 +75,7 @@ public:
STDMETHOD(put_XSubInterface)(hyper pXSubInterface) override;
static const wchar_t* mapToIA2RelationType(sal_Int16 nUnoRelationType);
+ static sal_Int16 mapToUnoRelationType(const BSTR aIA2RelationType);
private:
css::accessibility::AccessibleRelation relation;
diff --git a/winaccessibility/source/UAccCOM/MAccessible.cxx b/winaccessibility/source/UAccCOM/MAccessible.cxx
index 549fee11a92f..44ba7016c6b2 100644
--- a/winaccessibility/source/UAccCOM/MAccessible.cxx
+++ b/winaccessibility/source/UAccCOM/MAccessible.cxx
@@ -2687,9 +2687,73 @@ COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accessibleWithCaret(IUnknown
return E_NOTIMPL;
}
-COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_relationTargetsOfType(BSTR, long, IUnknown***, long*)
+COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_relationTargetsOfType(BSTR type,
+ long maxTargets,
+ IUnknown*** targets,
+ long* nTargets)
{
- return E_NOTIMPL;
+ SolarMutexGuard g;
+
+ if (!type || !targets || !nTargets || maxTargets <= 0)
+ return E_INVALIDARG;
+
+ if (m_isDestroy)
+ return S_FALSE;
+
+ if (!m_xContext.is())
+ return E_FAIL;
+
+ try
+ {
+ Reference<XAccessibleRelationSet> xRelationSet = m_xContext->getAccessibleRelationSet();
+ if (!xRelationSet.is())
+ return S_FALSE;
+
+ const sal_Int16 nUnoRelationType = CAccRelation::mapToUnoRelationType(type);
+ if (nUnoRelationType == AccessibleRelationType::INVALID)
+ return S_FALSE;
+
+ AccessibleRelation aRelation = xRelationSet->getRelationByType(nUnoRelationType);
+ if (aRelation.RelationType != nUnoRelationType || !aRelation.TargetSet.hasElements())
+ return S_FALSE;
+
+ const sal_Int32 nRetCount
+ = std::min(sal_Int32(maxTargets), aRelation.TargetSet.getLength());
+ *targets = static_cast<IUnknown**>(CoTaskMemAlloc(nRetCount * sizeof(IUnknown*)));
+ assert(*targets && "Failed to allocate memory for relation targets");
+
+ for (sal_Int32 i = 0; i < nRetCount; i++)
+ {
+ Reference<XAccessible> xTarget = aRelation.TargetSet[i];
+ assert(xTarget.is());
+
+ IAccessible* pIAccessible = CMAccessible::get_IAccessibleFromXAccessible(xTarget.get());
+ if (!pIAccessible)
+ {
+ Reference<XAccessibleContext> xTargetContext = xTarget->getAccessibleContext();
+ if (!xTargetContext.is())
+ {
+ SAL_WARN("iacc2", "Relation target doesn't have an accessible context");
+ CoTaskMemFree(*targets);
+ return E_FAIL;
+ }
+ Reference<XAccessible> xParent = xTargetContext->getAccessibleParent();
+ CMAccessible::g_pAccObjectManager->InsertAccObj(xTarget.get(), xParent.get());
+ pIAccessible = CMAccessible::get_IAccessibleFromXAccessible(xTarget.get());
+ }
+ assert(pIAccessible && "Couldn't retrieve IAccessible object for relation target.");
+
+ pIAccessible->AddRef();
+ (*targets)[i] = pIAccessible;
+ }
+
+ *nTargets = nRetCount;
+ return S_OK;
+ }
+ catch (...)
+ {
+ return E_FAIL;
+ }
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */