diff options
author | Michael Weghorn <m.weghorn@posteo.de> | 2024-08-22 13:39:34 +0100 |
---|---|---|
committer | Michael Weghorn <m.weghorn@posteo.de> | 2024-08-23 07:40:17 +0200 |
commit | 2318b823732df3820557943e56dd5243381dc558 (patch) | |
tree | bc3c201aa284c9d23e4eb3f63185e128ef7e72b8 /winaccessibility | |
parent | 002a29b8dd0d4c61194227e4151b4847480cb8b1 (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.cxx | 27 | ||||
-rw-r--r-- | winaccessibility/source/UAccCOM/AccRelation.h | 1 | ||||
-rw-r--r-- | winaccessibility/source/UAccCOM/MAccessible.cxx | 68 |
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: */ |