diff options
Diffstat (limited to 'winaccessibility/source/UAccCOM/MAccessible.cxx')
-rw-r--r-- | winaccessibility/source/UAccCOM/MAccessible.cxx | 2767 |
1 files changed, 2767 insertions, 0 deletions
diff --git a/winaccessibility/source/UAccCOM/MAccessible.cxx b/winaccessibility/source/UAccCOM/MAccessible.cxx new file mode 100644 index 0000000000..6c1367185c --- /dev/null +++ b/winaccessibility/source/UAccCOM/MAccessible.cxx @@ -0,0 +1,2767 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <UAccCOM.h> +#include "MAccessible.h" + +#include <algorithm> +#include <cstddef> + +#include "AccAction.h" +#include "AccRelation.h" +#include "AccComponent.h" +#include "AccText.h" +#include "AccEditableText.h" +#include "AccImage.h" +#include "AccTable.h" +#include "AccTableCell.h" +#include "AccValue.h" +#include "AccHypertext.h" +#include "AccHyperLink.h" + +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <unotools/configmgr.hxx> +#include <vcl/accessibility/AccessibleTextAttributeHelper.hxx> +#include <vcl/svapp.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <comphelper/AccessibleImplementationHelper.hxx> + +#include <com/sun/star/accessibility/XAccessibleText.hpp> +#include <com/sun/star/accessibility/XAccessibleEditableText.hpp> +#include <com/sun/star/accessibility/XAccessibleImage.hpp> +#include <com/sun/star/accessibility/XAccessibleTable.hpp> +#include <com/sun/star/accessibility/XAccessibleExtendedComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleAction.hpp> +#include <com/sun/star/accessibility/XAccessibleKeyBinding.hpp> +#include <com/sun/star/accessibility/XAccessibleHypertext.hpp> +#include <com/sun/star/accessibility/XAccessibleHyperlink.hpp> +#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/XAccessibleGroupPosition.hpp> +#include <com/sun/star/accessibility/XAccessibleValue.hpp> +#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp> +#include <com/sun/star/style/LineSpacing.hpp> +#include <com/sun/star/style/TabStop.hpp> +#include <com/sun/star/container/XIndexReplace.hpp> + + +using namespace com::sun::star::uno; +using namespace com::sun::star::accessibility; + +namespace { + +enum class XInterfaceType { + XI_COMPONENT, + XI_TEXT, + XI_TABLE, + XI_TABLECELL, + XI_EDITABLETEXT, + XI_IMAGE, + XI_SELECTION, + XI_EXTENDEDCOMP, + XI_VALUE, + XI_KEYBINDING, + XI_ACTION, + XI_HYPERTEXT, + XI_HYPERLINK, + XI_ATTRIBUTE +}; + +template <class Interface> +bool queryXInterface(XAccessible* pXAcc, XInterface** ppXI) +{ + if (!pXAcc) + return false; + + Reference<XAccessibleContext> pRContext = pXAcc->getAccessibleContext(); + if (!pRContext.is()) + return false; + + Reference<Interface> pRXI(pRContext, UNO_QUERY); + if (!pRXI.is()) + return false; + + *ppXI = pRXI.get(); + return true; +} + +// Since there's no specific XInterface for table cells, this +// method checks that the accessible's parent is a table +// (implements XAccessibleTable) and pXAcc's context implements +// XAccessibleComponent. +bool queryTableCell(XAccessible* pXAcc, XInterface** ppXI) +{ + XInterface* pXInterface = nullptr; + + const bool bSupportsInterface = queryXInterface<XAccessibleComponent>(pXAcc, &pXInterface); + if (!bSupportsInterface) + return false; + + // check whether parent is a table (its accessible context implements XAccessibleTable) + XInterface* pParentXInterface = nullptr; + Reference<XAccessible> xParentAcc = pXAcc->getAccessibleContext()->getAccessibleParent(); + const bool bParentIsTable = queryXInterface<XAccessibleTable>(xParentAcc.get(), &pParentXInterface); + + if (!bParentIsTable) + return false; + + *ppXI = pXInterface; + return true; +} + + +void lcl_addIA2State(AccessibleStates& rStates, sal_Int64 nUnoState, sal_Int16 nRole) +{ + switch (nUnoState) + { + case css::accessibility::AccessibleStateType::ACTIVE: + rStates |= IA2_STATE_ACTIVE; + break; + case css::accessibility::AccessibleStateType::ARMED: + rStates |= IA2_STATE_ARMED; + break; + case css::accessibility::AccessibleStateType::CHECKABLE: + // STATE_SYSTEM_PRESSED is used instead of STATE_SYSTEM_CHECKED for these button + // roles (s. AccObject::GetMSAAStateFromUNO), so don't set CHECKABLE state for them + if (nRole != AccessibleRole::PUSH_BUTTON && nRole != AccessibleRole::TOGGLE_BUTTON) + rStates |= IA2_STATE_CHECKABLE; + break; + case css::accessibility::AccessibleStateType::DEFUNC: + rStates |= IA2_STATE_DEFUNCT; + break; + case css::accessibility::AccessibleStateType::EDITABLE: + rStates |= IA2_STATE_EDITABLE; + break; + case css::accessibility::AccessibleStateType::HORIZONTAL: + rStates |= IA2_STATE_HORIZONTAL; + break; + case css::accessibility::AccessibleStateType::ICONIFIED: + rStates |= IA2_STATE_ICONIFIED; + break; + case css::accessibility::AccessibleStateType::MANAGES_DESCENDANTS: + rStates |= IA2_STATE_MANAGES_DESCENDANTS; + break; + case css::accessibility::AccessibleStateType::MODAL: + rStates |= IA2_STATE_MODAL; + break; + case css::accessibility::AccessibleStateType::MULTI_LINE: + rStates |= IA2_STATE_MULTI_LINE; + break; + case css::accessibility::AccessibleStateType::OPAQUE: + rStates |= IA2_STATE_OPAQUE; + break; + case css::accessibility::AccessibleStateType::SINGLE_LINE: + rStates |= IA2_STATE_SINGLE_LINE; + break; + case css::accessibility::AccessibleStateType::STALE: + rStates |= IA2_STATE_STALE; + break; + case css::accessibility::AccessibleStateType::TRANSIENT: + rStates |= IA2_STATE_TRANSIENT; + break; + case css::accessibility::AccessibleStateType::VERTICAL: + rStates |= IA2_STATE_VERTICAL; + break; + default: + // no match + break; + } +} + +} + +AccObjectWinManager* CMAccessible::g_pAccObjectManager = nullptr; + +CMAccessible::CMAccessible(): +m_pszName(nullptr), +m_pszValue(nullptr), +m_pszActionDescription(nullptr), +m_iRole(0x00), +m_dState(0x00), +m_pIParent(nullptr), +m_dChildID(0x00), +m_dFocusChildID(UACC_NO_FOCUS), +m_hwnd(nullptr), +m_isDestroy(false) +{ + CEnumVariant::Create(&m_pEnumVar); + m_containedObjects.clear(); +} + +CMAccessible::~CMAccessible() +{ + SolarMutexGuard g; + + if(m_pszName!=nullptr) + { + SysFreeString(std::exchange(m_pszName, nullptr)); + } + if(m_pszValue!=nullptr) + { + SysFreeString(std::exchange(m_pszValue, nullptr)); + } + + if(m_pszActionDescription!=nullptr) + { + SysFreeString(std::exchange(m_pszActionDescription, nullptr)); + } + + if(m_pIParent) + { + m_pIParent->Release(); + m_pIParent=nullptr; + } + m_pEnumVar->Release(); + m_containedObjects.clear(); +} + +/** +* Returns the Parent IAccessible interface pointer to AT. +* It should add reference, and the client should release the component. +* It should return E_FAIL when the parent point is null. +* @param ppdispParent [in,out] used to return the parent interface point. +* when the point is null, should return null. +* @return S_OK if successful and E_FAIL if the m_pIParent is NULL. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accParent(IDispatch **ppdispParent) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(ppdispParent == nullptr) + { + return E_INVALIDARG; + } + + if(m_pIParent) + { + *ppdispParent = m_pIParent; + (*ppdispParent)->AddRef(); + return S_OK; + } + else if(m_hwnd) + { + HRESULT hr = AccessibleObjectFromWindow(m_hwnd, OBJID_WINDOW, IID_IAccessible, reinterpret_cast<void**>(ppdispParent)); + if (!SUCCEEDED(hr) || !*ppdispParent) + { + return S_FALSE; + } + return S_OK; + } + return S_FALSE; + + } catch(...) { return E_FAIL; } +} + +/** +* Returns child count of current COM object. +* @param pcountChildren [in,out] used to return the children count. +* @return S_OK if successful. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accChildCount(long *pcountChildren) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(pcountChildren == nullptr) + { + return E_INVALIDARG; + } + + if (!m_xAccessible.is()) + return S_FALSE; + + Reference<XAccessibleContext> const pRContext = + m_xAccessible->getAccessibleContext(); + if( pRContext.is() ) + { + sal_Int64 nChildCount = pRContext->getAccessibleChildCount(); + if (nChildCount > std::numeric_limits<long>::max()) + { + SAL_WARN("iacc2", "CMAccessible::get_accChildCount: Child count exceeds maximum long value, " + "returning max long."); + nChildCount = std::numeric_limits<long>::max(); + } + + *pcountChildren = nChildCount; + } + + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* Returns child interface pointer for AT according to input child ID. +* @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID, +* the child ID specify child index from 0 to children count, 0 stands for object self. +* @param ppdispChild, [in,out] use to return the child interface point. +* @return S_OK if successful and S_FALSE if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accChild(VARIANT varChild, IDispatch **ppdispChild) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(ppdispChild == nullptr) + { + return E_INVALIDARG; + } + if(varChild.vt==VT_I4) + { + //get child interface pointer due to child ID + if(varChild.lVal==CHILDID_SELF) + { + AddRef(); + *ppdispChild = this; + return S_OK; + } + *ppdispChild = GetChildInterface(varChild.lVal); + if((*ppdispChild) == nullptr) + return E_FAIL; + (*ppdispChild)->AddRef(); + return S_OK; + } + return S_FALSE; + + } catch(...) { return E_FAIL; } +} + +/** +* Returns the accessible name of the current COM object self or its one child to AT. +* @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID, +* the child ID specify child index from 0 to children count, 0 stands for object self. +* @param pszName, [in,out] use to return the name of the proper object. +* @return S_OK if successful and S_FALSE if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accName(VARIANT varChild, BSTR *pszName) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(pszName == nullptr) + { + return E_INVALIDARG; + } + if(varChild.vt==VT_I4) + { + if(varChild.lVal==CHILDID_SELF) + { + SysFreeString(*pszName); + *pszName = SysAllocString(m_pszName); + return S_OK; + } + + long lVal = varChild.lVal; + varChild.lVal = CHILDID_SELF; + IMAccessible *pChild = this->GetChildInterface(lVal); + if(!pChild) + return E_FAIL; + return pChild->get_accName(varChild,pszName); + } + return S_FALSE; + + } catch(...) { return E_FAIL; } +} + +/** +* Returns the accessible value of the current COM object self or its one child to AT. +* @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID, +* the child ID specify child index from 0 to children count, 0 stands for object self. +* @param pszValue, [in,out] use to return the value of the proper object. +* @return S_OK if successful and S_FALSE if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accValue(VARIANT varChild, BSTR *pszValue) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if( pszValue == nullptr ) + { + return E_INVALIDARG; + } + if( varChild.vt==VT_I4 ) + { + if(varChild.lVal==CHILDID_SELF) + { + if(m_dState & STATE_SYSTEM_PROTECTED) + return E_ACCESSDENIED; + + if ( m_pszValue !=nullptr && wcslen(m_pszValue) == 0 ) + return S_OK; + + SysFreeString(*pszValue); + *pszValue = SysAllocString(m_pszValue); + return S_OK; + } + + long lVal = varChild.lVal; + varChild.lVal = CHILDID_SELF; + IMAccessible *pChild = this->GetChildInterface(lVal); + if(!pChild) + return E_FAIL; + return pChild->get_accValue(varChild,pszValue); + } + return S_FALSE; + + } catch(...) { return E_FAIL; } +} + +/** +* Returns the accessible description of the current COM object self or its one child to AT. +* @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID, +* the child ID specify child index from 0 to children count, 0 stands for object self. +* @param pszDescription, [in,out] use to return the description of the proper object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accDescription(VARIANT varChild, BSTR *pszDescription) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(pszDescription == nullptr) + { + return E_INVALIDARG; + } + if(varChild.vt==VT_I4) + { + if(varChild.lVal==CHILDID_SELF) + { + if (!m_xAccessible.is()) + return S_FALSE; + + Reference<XAccessibleContext> xContext = m_xAccessible->getAccessibleContext(); + if (!xContext.is()) + return S_FALSE; + + const OUString sDescription = xContext->getAccessibleDescription(); + SysFreeString(*pszDescription); + *pszDescription = SysAllocString(o3tl::toW(sDescription.getStr())); + return S_OK; + } + + long lVal = varChild.lVal; + varChild.lVal = CHILDID_SELF; + IMAccessible *pChild = this->GetChildInterface(lVal); + if(!pChild) + return E_FAIL; + return pChild->get_accDescription(varChild,pszDescription); + } + return S_FALSE; + + } catch(...) { return E_FAIL; } +} + +/** +* Returns the accessible role of the current COM object self or its one child to AT. +* @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID, +* the child ID specify child index from 0 to children count, 0 stands for object self. +* @param pvarRole, [in,out] use to return the role of the proper object. +* @return S_OK if successful and S_FALSE if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accRole(VARIANT varChild, VARIANT *pvarRole) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(pvarRole == nullptr) + { + return E_INVALIDARG; + } + if(varChild.vt == VT_I4) + { + + if(varChild.lVal == CHILDID_SELF) + { + VariantInit(pvarRole); + pvarRole->vt = VT_I4; + + if (m_iRole < IA2_ROLE_CAPTION) + pvarRole->lVal = m_iRole; + else + pvarRole->lVal = ROLE_SYSTEM_CLIENT; + + return S_OK; + } + + + long lVal = varChild.lVal; + varChild.lVal = CHILDID_SELF; + IMAccessible *pChild = this->GetChildInterface(lVal); + if(!pChild) + return E_FAIL; + return pChild->get_accRole(varChild,pvarRole); + } + return S_FALSE; + + } catch(...) { return E_FAIL; } +} + +/** +* Returns the accessible state of the current COM object self or its one child to AT. +* @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID, +* the child ID specify child index from 0 to children count, 0 stands for object self. +* @param pvarState, [in,out] use to return the state of the proper object. +* @return S_OK if successful and S_FALSE if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accState(VARIANT varChild, VARIANT *pvarState) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(pvarState == nullptr) + { + return E_INVALIDARG; + } + if(varChild.vt==VT_I4) + { + if(varChild.lVal == CHILDID_SELF) + { + if (m_xAccessible.is()) + { + Reference<XAccessibleContext> const pContext = + m_xAccessible->getAccessibleContext(); + if(pContext.is()) + { + // add the STATE_SYSTEM_LINKED state + Reference< XAccessibleHypertext > pRHypertext(pContext,UNO_QUERY); + if(pRHypertext.is()) + { + if( pRHypertext->getHyperLinkCount() > 0 ) + m_dState |= STATE_SYSTEM_LINKED; + else + m_dState &= ~STATE_SYSTEM_LINKED; + } + else + m_dState &= ~STATE_SYSTEM_LINKED; + } + } + + VariantInit(pvarState); + pvarState->vt = VT_I4; + pvarState->lVal = m_dState; + return S_OK; + } + + long lVal = varChild.lVal; + varChild.lVal = CHILDID_SELF; + IMAccessible *pChild = this->GetChildInterface(lVal); + if(!pChild) + return E_FAIL; + return pChild->get_accState(varChild,pvarState); + } + return S_FALSE; + + } catch(...) { return E_FAIL; } +} + +/** +* Returns the accessible helpString of the current COM object self or its one child to AT. +* @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID, +* the child ID specify child index from 0 to children count, 0 stands for object self. +* @param pszHelp, [in,out] use to return the helpString of the proper object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accHelp(VARIANT, BSTR *) +{ + return E_NOTIMPL; +} + +/** +* Returns the accessible HelpTopic of the current COM object self or its one child to AT. +* @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID, +* the child ID specify child index from 0 to children count, 0 stands for object self. +* @param pszHelpFile, [in,out] use to return the HelpTopic of the proper object. +* @param pidTopic, use to return the HelpTopic ID of the proper object. +* @return S_OK if successful and E_FAIL if failure. +* Not implemented yet +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accHelpTopic(BSTR *, VARIANT, long *) +{ + return E_NOTIMPL; +} + +static bool GetMnemonicChar( const OUString& aStr, sal_Unicode* wStr) +{ + for (sal_Int32 i = 0;; i += 2) { + i = aStr.indexOf('~', i); + if (i == -1 || i == aStr.getLength() - 1) { + return false; + } + auto c = aStr[i + 1]; + if (c != '~') { + *wStr = c; + return true; + } + } +} + +/** +* Returns the accessible keyboard shortcut of the current COM object self or its one child to AT. +* @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID, +* the child ID specify child index from 0 to children count, 0 stands for object self. +* @param pszKeyboardShortcut, [in,out] use to return the kbshortcut of the proper object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut) +{ + SolarMutexGuard g; + + try { + + if (m_isDestroy) return S_FALSE; + + if(pszKeyboardShortcut == nullptr) + { + return E_INVALIDARG; + } + + if(varChild.vt==VT_I4) + { + if(varChild.lVal == CHILDID_SELF) + { + if (m_xAccessible.is()) + { + Reference<XAccessibleContext> const pRContext = + m_xAccessible->getAccessibleContext(); + if( !pRContext.is() ) + return S_FALSE; + + Reference<XAccessibleAction> pRXI(pRContext,UNO_QUERY); + + OUString wString; + + if( pRXI.is() && pRXI->getAccessibleActionCount() >= 1) + { + Reference< XAccessibleKeyBinding > binding = pRXI->getAccessibleActionKeyBinding(0); + if( binding.is() ) + { + long nCount = binding->getAccessibleKeyBindingCount(); + if(nCount >= 1) + { + wString = comphelper::GetkeyBindingStrByXkeyBinding( binding->getAccessibleKeyBinding(0) ); + } + } + } + if(wString.isEmpty()) + { + Reference<XAccessibleRelationSet> pRrelationSet = pRContext->getAccessibleRelationSet(); + if(!pRrelationSet.is()) + { + return S_FALSE; + } + + long nRelCount = pRrelationSet->getRelationCount(); + + // Modified by Steve Yin, for SODC_1552 + if( /*nRelCount <= 0 &&*/ m_iRole == ROLE_SYSTEM_TEXT ) + { + VARIANT varParentRole; + VariantInit( &varParentRole ); + + if (m_pIParent + && SUCCEEDED(m_pIParent->get_accRole(varChild, &varParentRole)) + && varParentRole.lVal == ROLE_SYSTEM_COMBOBOX) // edit in comboBox + { + m_pIParent->get_accKeyboardShortcut(varChild, pszKeyboardShortcut); + return S_OK; + } + } + + AccessibleRelation *paccRelation = nullptr; + AccessibleRelation accRelation; + for(int i=0; i<nRelCount ; i++) + { + if( pRrelationSet->getRelation(i).RelationType == 6 ) + { + accRelation = pRrelationSet->getRelation(i); + paccRelation = &accRelation; + } + } + + if(paccRelation == nullptr) + return S_FALSE; + + Sequence< Reference< XInterface > > xTargets = paccRelation->TargetSet; + Reference<XInterface> pRAcc = xTargets[0]; + + XAccessible* pXAcc = static_cast<XAccessible*>(pRAcc.get()); + + Reference<XAccessibleContext> pRLebelContext = pXAcc->getAccessibleContext(); + if(!pRLebelContext.is()) + return S_FALSE; + + pRrelationSet = pRLebelContext->getAccessibleRelationSet(); + nRelCount = pRrelationSet->getRelationCount(); + + paccRelation = nullptr; + for(int j=0; j<nRelCount ; j++) + { + if( pRrelationSet->getRelation(j).RelationType == 5 ) + { + accRelation = pRrelationSet->getRelation(j); + paccRelation = &accRelation; + } + } + + if(paccRelation) + { + xTargets = paccRelation->TargetSet; + pRAcc = xTargets[0]; + if (m_xAccessible.get() != static_cast<XAccessible*>(pRAcc.get())) + return S_FALSE; + } + + Reference<XAccessibleExtendedComponent> pRXIE(pRLebelContext,UNO_QUERY); + if(!pRXIE.is()) + return S_FALSE; + + OUString ouStr = pRXIE->getTitledBorderText(); + sal_Unicode key; + if(GetMnemonicChar(ouStr, &key)) + { + wString = "Alt+" + OUStringChar(key); + } + else + return S_FALSE; + } + + SysFreeString(*pszKeyboardShortcut); + *pszKeyboardShortcut = SysAllocString(o3tl::toW(wString.getStr())); + + return S_OK; + } + else + { + return S_FALSE; + } + } + + long lVal = varChild.lVal; + varChild.lVal = CHILDID_SELF; + IMAccessible *pChild = this->GetChildInterface(lVal); + if(!pChild) + return E_FAIL; + + return pChild->get_accKeyboardShortcut(varChild,pszKeyboardShortcut); + } + return S_FALSE; + + } catch(...) { return E_FAIL; } +} + +/** +* Returns the current focused child to AT. +* @param pvarChild, [in,out] vt member of pvarChild must be VT_I4,and lVal member stores the child ID, +* the child ID specify child index from 0 to children count, 0 stands for object self. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accFocus(VARIANT *pvarChild) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(pvarChild == nullptr) + { + return E_INVALIDARG; + } + if( m_dFocusChildID==UACC_NO_FOCUS ) + { + pvarChild->vt = VT_EMPTY;//no focus on the object and its children + return S_OK; + } + //if the descendant of current object has focus indicated by m_dFocusChildID, return the IDispatch of this focused object + else + { + IMAccessible* pIMAcc = g_pAccObjectManager->GetIAccessibleFromResID(m_dFocusChildID); + if (pIMAcc == nullptr) + { + return E_FAIL; + } + pIMAcc->AddRef(); + pvarChild->vt = VT_DISPATCH; + pvarChild->pdispVal = pIMAcc; + + } + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* Returns the selection of the current COM object to AT. +* @param pvarChildren,[in,out] +* if selection num is 0,return VT_EMPTY for vt, +* if selection num is 1,return VT_I4 for vt,and child index for lVal +* if selection num >1,return VT_UNKNOWN for vt, and IEnumVariant* for punkVal +* @return S_OK if successful and S_FALSE if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accSelection(VARIANT *pvarChildren) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(pvarChildren == nullptr) + { + return E_INVALIDARG; + } + switch(m_pEnumVar->GetCountOfElements()) + { + case 0: + pvarChildren->vt = VT_EMPTY; + break; + case 1: + VARIANT varTmp[1]; + ULONG count; + VariantInit(&varTmp[0]); + m_pEnumVar->Next(1,varTmp,&count); + if(count!=1) + return S_FALSE; + pvarChildren->vt = VT_DISPATCH; + pvarChildren->pdispVal = varTmp[0].pdispVal; + pvarChildren->pdispVal->AddRef(); + VariantClear(&varTmp[0]); + m_pEnumVar->Reset(); + break; + default: + pvarChildren->vt = VT_UNKNOWN; + IEnumVARIANT* pClone; + m_pEnumVar->Clone(&pClone); + pClone->Reset(); + pvarChildren->punkVal = pClone; + break; + } + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* Returns the location of the current COM object self or its one child to AT. +* @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID, +* the child ID specify child index from 0 to children count, 0 stands for object self. +* @param pxLeft, [in,out] use to return the x-coordination of the proper object. +* @param pyTop, [in,out] use to return the y-coordination of the proper object. +* @param pcxWidth, [in,out] use to return the x-coordination width of the proper object. +* @param pcyHeight, [in,out] use to return the y-coordination height of the proper object. +* @return S_OK if successful and S_FALSE if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varChild) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(pxLeft == nullptr || pyTop == nullptr || pcxWidth == nullptr || pcyHeight == nullptr) + { + return E_INVALIDARG; + } + + if(varChild.vt==VT_I4) + { + if(varChild.lVal==CHILDID_SELF) + { + if (!m_xAccessible.is()) + return S_FALSE; + + Reference<XAccessibleContext> const pRContext = + m_xAccessible->getAccessibleContext(); + if( !pRContext.is() ) + return S_FALSE; + Reference< XAccessibleComponent > pRComponent(pRContext,UNO_QUERY); + if( !pRComponent.is() ) + return S_FALSE; + + css::awt::Point pCPoint = pRComponent->getLocationOnScreen(); + css::awt::Size pCSize = pRComponent->getSize(); + *pxLeft = pCPoint.X; + *pyTop = pCPoint.Y; + *pcxWidth = pCSize.Width; + *pcyHeight = pCSize.Height; + return S_OK; + } + } + return S_FALSE; + + } catch(...) { return E_FAIL; } +} + +/** +* Returns the current focused child to AT. +* @param navDir, the direction flag of the navigation. +* @param varStart, the start child id of this navigation action. +* @param pvarEndUpAt, [in,out] the end up child of this navigation action. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEndUpAt) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(pvarEndUpAt == nullptr) + { + return E_INVALIDARG; + } + HRESULT ret = E_FAIL; + switch (navDir) + { + case NAVDIR_FIRSTCHILD: + ret = GetFirstChild(varStart,pvarEndUpAt); + break; + case NAVDIR_LASTCHILD: + ret = GetLastChild(varStart,pvarEndUpAt); + break; + case NAVDIR_NEXT: + ret = GetNextSibling(varStart,pvarEndUpAt); + break; + case NAVDIR_PREVIOUS: + ret = GetPreSibling(varStart,pvarEndUpAt); + break; + case NAVDIR_DOWN://do not implement temporarily + break; + case NAVDIR_UP://do not implement temporarily + break; + case NAVDIR_LEFT://do not implement temporarily + break; + case NAVDIR_RIGHT://do not implement temporarily + break; + default: + break; + } + return ret; + + } catch(...) { return E_FAIL; } +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::accHitTest(long xLeft, long yTop, VARIANT *pvarChild) +{ + SolarMutexGuard g; + + if (m_isDestroy) + return S_FALSE; + + if (!pvarChild) + return E_INVALIDARG; + + try + { + pvarChild->vt = VT_EMPTY; + + Reference<XAccessibleContext> xContext = GetContextByXAcc(m_xAccessible.get()); + Reference<XAccessibleComponent> xComponent(xContext, UNO_QUERY); + if (!xComponent.is()) + return S_FALSE; + + // convert from screen to object-local coordinates + css::awt::Point aTopLeft = xComponent->getLocationOnScreen(); + css::awt::Point aPoint(xLeft - aTopLeft.X, yTop - aTopLeft.Y); + + Reference<XAccessible> xAccAtPoint = xComponent->getAccessibleAtPoint(aPoint); + if (!xAccAtPoint.is()) + return S_FALSE; + + IAccessible* pRet = get_IAccessibleFromXAccessible(xAccAtPoint.get()); + if (!pRet) + { + g_pAccObjectManager->InsertAccObj(xAccAtPoint.get(), m_xAccessible.get(), m_hwnd); + pRet = get_IAccessibleFromXAccessible(xAccAtPoint.get()); + } + if (!pRet) + return S_FALSE; + + pvarChild->vt = VT_DISPATCH; + pvarChild->pdispVal = pRet; + pRet->AddRef(); + + return S_OK; + } catch(...) { return E_FAIL; } +} + +/** +* Get The other Interface from CMAccessible. +* @param guidService, must be IID_IAccessible here. +* @param riid, the IID interface . +* @return S_OK if successful and S_FALSE if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::QueryService(REFGUID guidService, REFIID riid, void** ppvObject) +{ + if( InlineIsEqualGUID(guidService, IID_IAccessible) ) + return QueryInterface(riid, ppvObject); + return S_FALSE; +} + +/** +* No longer supported according to IAccessible doc. +* Servers should return E_NOTIMPL +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::put_accName(VARIANT, BSTR) +{ + return E_NOTIMPL; +} + +/** +* Set the accessible value of the current COM object self or its one child from UNO. +* @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID, +* the child ID specify child index from 0 to children count, 0 stands for object self. +* @param szValue, the value used to set the value of the proper object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::put_accValue(VARIANT varChild, BSTR szValue) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + if(varChild.vt==VT_I4) + { + if(varChild.lVal==CHILDID_SELF) + { + SysFreeString(m_pszValue); + m_pszValue=SysAllocString(szValue); + return S_OK; + } + + long lVal = varChild.lVal; + varChild.lVal = CHILDID_SELF; + IMAccessible *pChild = this->GetChildInterface(lVal); + if(!pChild) + return E_FAIL; + return pChild->put_accValue(varChild,szValue); + } + return E_FAIL; + + } catch(...) { return E_FAIL; } +} + +/** +* Set the accessible name of the current COM object self from UNO. +* @param pszName, the name value used to set the name of the current object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccName(const OLECHAR __RPC_FAR *pszName) +{ + // internal IMAccessible - no mutex meeded + + try { + if (m_isDestroy) return S_FALSE; + + if(pszName == nullptr) + { + return E_INVALIDARG; + } + + SysFreeString(m_pszName); + m_pszName = SysAllocString(pszName); + if(m_pszName==nullptr) + return E_FAIL; + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* Set the accessible role of the current COM object self from UNO. +* @param pRole, the role value used to set the role of the current object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccRole(unsigned short pRole) +{ + // internal IMAccessible - no mutex meeded + + m_iRole = pRole; + return S_OK; +} + +/** +* Add one state into the current state set for the current COM object from UNO. +* @param pXSate, the state used to set the name of the current object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::DecreaseState(DWORD pXSate) +{ + // internal IMAccessible - no mutex meeded + + m_dState &= (~pXSate); + return S_OK; +} + +/** +* Delete one state into the current state set for the current COM object from UNO. +* @param pXSate, the state used to set the name of the current object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::IncreaseState(DWORD pXSate) +{ + // internal IMAccessible - no mutex meeded + + m_dState |= pXSate; + return S_OK; +} + +/** +* Set state into the current state set for the current COM object from UNO. +* @param pXSate, the state used to set the name of the current object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::SetState(DWORD pXSate) +{ + // internal IMAccessible - no mutex meeded + + m_dState = pXSate; + return S_OK; +} + +/** +* Set the accessible value of the current COM object self from UNO. +* @param pszAccValue, the name used to set the value of the current object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccValue(const OLECHAR __RPC_FAR *pszAccValue) +{ + // internal IMAccessible - no mutex meeded + + try { + if (m_isDestroy) return S_FALSE; + + if(pszAccValue == nullptr) + { + return E_INVALIDARG; + } + SysFreeString(m_pszValue); + m_pszValue = SysAllocString(pszAccValue); + if(m_pszValue==nullptr) + return E_FAIL; + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* Set the HWND value of the current COM object self from UNO. It should set the parent IAccessible +* Object through the method AccessibleObjectFromWindow(...). +* @param hwnd, the HWND used to set the value of the current object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccWindowHandle(HWND hwnd) +{ + // internal IMAccessible - no mutex meeded + + try { + if (m_isDestroy) return S_FALSE; + m_hwnd = hwnd; + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* Set accessible focus by specifying child ID +* @param dChildID, the child id identifies the focus child. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccFocus(long dChildID) +{ + // internal IMAccessible - no mutex meeded + + try { + if (m_isDestroy) return S_FALSE; + + if(dChildID==CHILDID_SELF) + { + if(m_pIParent) + { + m_pIParent->Put_XAccFocus(m_dChildID); + } + } + else + { + m_dFocusChildID = dChildID; + //traverse all ancestors to set the focused child ID so that when the get_accFocus is called on + //any of the ancestors, this id can be used to get the IAccessible of focused object. + if(m_pIParent) + { + m_pIParent->Put_XAccFocus(dChildID); + } + } + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* Set accessible parent object for the current COM object if +* the current object is a child of some COM object +* @param pIParent, the parent of the current object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccParent(IMAccessible __RPC_FAR *pIParent) +{ + // internal IMAccessible - no mutex meeded + + this->m_pIParent = pIParent; + + if(pIParent) + m_pIParent->AddRef(); + + return S_OK; +} + +/** +* Set unique child id to COM +* @param dChildID, the id of the current object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccChildID(long dChildID) +{ + // internal IMAccessible - no mutex meeded + + this->m_dChildID = dChildID; + return S_OK; +} + +/** +* Set AccObjectWinManager object pointer to COM +* @param pManager, the AccObjectWinManager pointer. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccObjectManager(hyper pManager) +{ + // internal IMAccessible - no mutex meeded + + g_pAccObjectManager = reinterpret_cast<AccObjectWinManager*>(pManager); + return S_OK; +} + +/** +* When a UNO control disposing, it disposes its listeners, +* then notify AccObject in bridge management, then notify +* COM that the XAccessible is invalid, so set m_xAccessible as NULL +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::NotifyDestroy() +{ + // internal IMAccessible - no mutex meeded + + m_isDestroy = true; + m_xAccessible.clear(); + return S_OK; +} + +/** +*private methods that help implement public functions +*/ + +/** +* Return child interface pointer by child ID,note: need to call AddRef() +* @param lChildID, specify child index,which AT(such as Inspect32) gives. +* @return IMAccessible*, pointer to the corresponding child object. +*/ +IMAccessible* CMAccessible::GetChildInterface(long dChildID)//for test +{ + if(dChildID<0) + { + if(g_pAccObjectManager) + { + IMAccessible* pIMAcc = g_pAccObjectManager->GetIAccessibleFromResID(dChildID); + return pIMAcc; + } + return nullptr; + } + else + { + if (!m_xAccessible.is()) + return nullptr; + + Reference<XAccessibleContext> const pRContext = + m_xAccessible->getAccessibleContext(); + if( !pRContext.is() ) + return nullptr; + + if(dChildID<1 || dChildID>pRContext->getAccessibleChildCount()) + return nullptr; + + Reference< XAccessible > pXChild = pRContext->getAccessibleChild(dChildID-1); + IAccessible* pChild = get_IAccessibleFromXAccessible(pXChild.get()); + + if(!pChild) + { + g_pAccObjectManager->InsertAccObj(pXChild.get(), m_xAccessible.get(), m_hwnd); + pChild = get_IAccessibleFromXAccessible(pXChild.get()); + } + + if (pChild) + { + IMAccessible* pIMAcc = static_cast<IMAccessible*>(pChild); + return pIMAcc; + } + } + + return nullptr; +} + +/** +* for descendantmanager circumstance,provide child interface when navigate +* @param varCur, the current child. +* @param flags, the navigation direction. +* @return IMAccessible*, the child of the end up node. +*/ +IMAccessible* CMAccessible::GetNavigateChildForDM(VARIANT varCur, short flags) +{ + + XAccessibleContext* pXContext = GetContextByXAcc(m_xAccessible.get()); + if(pXContext==nullptr) + { + return nullptr; + } + + sal_Int64 count = pXContext->getAccessibleChildCount(); + if(count<1) + { + return nullptr; + } + + IMAccessible* pCurChild = nullptr; + union { + XAccessible* pChildXAcc; + hyper nHyper = 0; + }; + Reference<XAccessible> pRChildXAcc; + XAccessibleContext* pChildContext = nullptr; + sal_Int64 index = 0, delta = 0; + switch(flags) + { + case DM_FIRSTCHILD: + pRChildXAcc = pXContext->getAccessibleChild(0); + break; + case DM_LASTCHILD: + pRChildXAcc = pXContext->getAccessibleChild(count-1); + break; + case DM_NEXTCHILD: + case DM_PREVCHILD: + pCurChild = GetChildInterface(varCur.lVal); + if(pCurChild==nullptr) + { + return nullptr; + } + pCurChild->GetUNOInterface(&nHyper); + if(pChildXAcc==nullptr) + { + return nullptr; + } + pChildContext = GetContextByXAcc(pChildXAcc); + if(pChildContext == nullptr) + { + return nullptr; + } + delta = (flags==DM_NEXTCHILD)?1:-1; + //currently, getAccessibleIndexInParent is error in UNO for + //some kind of List,such as ValueSet, the index will be less 1 than + //what should be, need to fix UNO code + index = pChildContext->getAccessibleIndexInParent()+delta; + if((index>=0)&&(index<=count-1)) + { + pRChildXAcc = pXContext->getAccessibleChild(index); + } + break; + default: + break; + } + + if(!pRChildXAcc.is()) + { + return nullptr; + } + pChildXAcc = pRChildXAcc.get(); + g_pAccObjectManager->InsertAccObj(pChildXAcc, m_xAccessible.get()); + return g_pAccObjectManager->GetIAccessibleFromXAccessible(pChildXAcc); +} + +/** +*the following 4 private methods are for accNavigate implementation +*/ + +/** +* Return first child for parent container, process differently according +* to whether it is descendant manage +* @param varStart, the start child id of this navigation action. +* @param pvarEndUpAt, [in,out] the end up child of this navigation action. +* @return S_OK if successful and E_FAIL if failure. +*/ +HRESULT CMAccessible::GetFirstChild(VARIANT varStart,VARIANT* pvarEndUpAt) +{ + + try { + if (m_isDestroy) return S_FALSE; + + if(pvarEndUpAt == nullptr) + { + return E_INVALIDARG; + } + if(varStart.vt != VT_I4) + { + pvarEndUpAt->vt = VT_EMPTY; + return E_INVALIDARG; + } + + pvarEndUpAt->pdispVal = GetNavigateChildForDM(varStart, DM_FIRSTCHILD); + if(pvarEndUpAt->pdispVal) + { + pvarEndUpAt->pdispVal->AddRef(); + pvarEndUpAt->vt = VT_DISPATCH; + return S_OK; + } + + pvarEndUpAt->vt = VT_EMPTY; + return E_FAIL; + + } catch(...) { return E_FAIL; } +} + +/** +* Return last child for parent container, process differently according +* to whether it is descendant manage +* @param varStart, the start child id of this navigation action. +* @param pvarEndUpAt, [in,out] the end up child of this navigation action. +* @return S_OK if successful and E_FAIL if failure. +*/ +HRESULT CMAccessible::GetLastChild(VARIANT varStart,VARIANT* pvarEndUpAt) +{ + + try { + if (m_isDestroy) return S_FALSE; + + if(pvarEndUpAt == nullptr) + { + return E_INVALIDARG; + } + if(varStart.vt != VT_I4) + { + pvarEndUpAt->vt = VT_EMPTY; + return E_INVALIDARG; + } + + pvarEndUpAt->pdispVal = GetNavigateChildForDM(varStart, DM_LASTCHILD); + if(pvarEndUpAt->pdispVal) + { + pvarEndUpAt->pdispVal->AddRef(); + pvarEndUpAt->vt = VT_DISPATCH; + return S_OK; + } + pvarEndUpAt->vt = VT_EMPTY; + return E_FAIL; + + } catch(...) { return E_FAIL; } +} + +/** +* The method GetNextSibling is general, whatever it is descendant manage or not +* Get the next sibling object. +* @param varStart, the start child id of this navigation action. +* @param pvarEndUpAt, [in,out] the end up child of this navigation action. +* @return S_OK if successful and E_FAIL if failure. +*/ +HRESULT CMAccessible::GetNextSibling(VARIANT varStart,VARIANT* pvarEndUpAt) +{ + + try { + if (m_isDestroy) return S_FALSE; + if(varStart.vt != VT_I4) + { + pvarEndUpAt->vt = VT_EMPTY; + return E_INVALIDARG; + } + + Reference<XAccessibleContext> const pRContext = + GetContextByXAcc(m_xAccessible.get()); + if(pRContext.is()) + { + varStart.iVal = sal_Int16(pRContext->getAccessibleIndexInParent() + 2); + if(m_pIParent) + if( m_pIParent->get_accChild(varStart,&pvarEndUpAt->pdispVal) == S_OK) + { + pvarEndUpAt->vt = VT_DISPATCH; + return S_OK; + } + } + pvarEndUpAt->vt = VT_EMPTY; + return E_FAIL; + + } catch(...) { return E_FAIL; } +} + +/** +*the method GetPreSibling is general, whatever it is descendant manage or not +* @param varStart, the start child id of this navigation action. +* @param pvarEndUpAt, [in,out] the end up child of this navigation action. +* @return S_OK if successful and E_FAIL if failure. +*/ +HRESULT CMAccessible::GetPreSibling(VARIANT varStart,VARIANT* pvarEndUpAt) +{ + + try { + if (m_isDestroy) return S_FALSE; + + if(pvarEndUpAt == nullptr) + { + return E_INVALIDARG; + } + if(varStart.vt != VT_I4) + { + pvarEndUpAt->vt = VT_EMPTY; + return E_INVALIDARG; + } + + Reference<XAccessibleContext> const pRContext = + GetContextByXAcc(m_xAccessible.get()); + if(pRContext.is()) + { + varStart.iVal = sal_Int16(pRContext->getAccessibleIndexInParent()); + if(m_pIParent && varStart.iVal > 0) + if( m_pIParent->get_accChild(varStart,&pvarEndUpAt->pdispVal) == S_OK) + { + pvarEndUpAt->vt = VT_DISPATCH; + return S_OK; + } + } + pvarEndUpAt->vt = VT_EMPTY; + return E_FAIL; + + } catch(...) { return E_FAIL; } +} + +/** +* For IAccessible2 implementation methods +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_nRelations( long __RPC_FAR *nRelations) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(nRelations == nullptr) + { + return E_INVALIDARG; + } + + *nRelations = 0; + + if (!m_xContext.is()) + return E_FAIL; + Reference<XAccessibleRelationSet> pRrelationSet = + m_xContext->getAccessibleRelationSet(); + if(!pRrelationSet.is()) + { + *nRelations = 0; + return S_OK; + } + + *nRelations = pRrelationSet->getRelationCount(); + return S_OK; + + } catch(...) { return E_FAIL; } +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_relation( long relationIndex, IAccessibleRelation __RPC_FAR *__RPC_FAR *relation) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(relation == nullptr) + { + return E_INVALIDARG; + } + + if (!m_xContext.is()) + return E_FAIL; + + + long nMax = 0; + get_nRelations(&nMax); + + *relation = static_cast<IAccessibleRelation*>(::CoTaskMemAlloc(sizeof(IAccessibleRelation))); + + // #CHECK Memory Allocation# + if(*relation == nullptr) + { + return E_FAIL; + } + + if( relationIndex < nMax ) + { + Reference<XAccessibleRelationSet> const pRrelationSet = + m_xContext->getAccessibleRelationSet(); + if(!pRrelationSet.is()) + { + + return E_FAIL; + } + + IAccessibleRelation* pRelation = nullptr; + HRESULT hr = createInstance<CAccRelation>(IID_IAccessibleRelation, + &pRelation); + if(SUCCEEDED(hr)) + { + IUNOXWrapper* wrapper = nullptr; + hr = pRelation->QueryInterface(IID_IUNOXWrapper, reinterpret_cast<void**>(&wrapper)); + if(SUCCEEDED(hr)) + { + AccessibleRelation accRelation = pRrelationSet->getRelation(relationIndex); + wrapper->put_XSubInterface( + reinterpret_cast<hyper>(&accRelation)); + wrapper->Release(); + *relation = pRelation; + return S_OK; + } + + } + } + + return E_FAIL; + + } catch(...) { return E_FAIL; } +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_relations( long, IAccessibleRelation __RPC_FAR *__RPC_FAR *relation, long __RPC_FAR *nRelations) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(relation == nullptr || nRelations == nullptr) + { + return E_INVALIDARG; + } + + if (!m_xContext.is()) + return E_FAIL; + + Reference<XAccessibleRelationSet> const pRrelationSet = + m_xContext->getAccessibleRelationSet(); + if(!pRrelationSet.is()) + { + *nRelations = 0; + return S_OK; + } + + long nCount = pRrelationSet->getRelationCount(); + + *relation = static_cast<IAccessibleRelation*>(::CoTaskMemAlloc(nCount*sizeof(IAccessibleRelation))); + + // #CHECK Memory Allocation# + if(*relation == nullptr) + { + return E_FAIL; + } + + for(int i=0; i<nCount ; i++) + { + IAccessibleRelation* pRelation = nullptr; + HRESULT hr = createInstance<CAccRelation>(IID_IAccessibleRelation, + &pRelation); + if(SUCCEEDED(hr)) + { + IUNOXWrapper* wrapper = nullptr; + hr = pRelation->QueryInterface(IID_IUNOXWrapper, reinterpret_cast<void**>(&wrapper)); + if(SUCCEEDED(hr)) + { + AccessibleRelation accRelation = pRrelationSet->getRelation(i); + wrapper->put_XSubInterface( + reinterpret_cast<hyper>(&accRelation)); + wrapper->Release(); + } + relation[i] = pRelation; + } + } + + *nRelations = nCount; + return S_OK; + + } catch(...) { return E_FAIL; } +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::role(long __RPC_FAR *role) +{ + SolarMutexGuard g; + + try { + + (*role) = m_iRole; + + return S_OK; + + } catch(...) { return E_FAIL; } +} + + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_nActions(long __RPC_FAR *nActions) +{ + SolarMutexGuard g; + + try + { + if (m_isDestroy) return S_FALSE; + + if(nActions == nullptr) + { + return E_INVALIDARG; + } + *nActions = 0; + IAccessibleAction* pAcc = nullptr; + HRESULT hr = QueryInterface(IID_IAccessibleAction, reinterpret_cast<void**>(&pAcc)); + if( hr == S_OK ) + { + pAcc->nActions(nActions); + pAcc->Release(); + } + + return S_OK; + } + catch(...) + { + *nActions = 0; + return S_OK; + } +} + + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::scrollToPoint(enum IA2CoordinateType, long, long) +{ + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::scrollTo(enum IA2ScrollType) +{ + return E_NOTIMPL; +} + +static XAccessible* getTheParentOfMember(XAccessible* pXAcc) +{ + if(pXAcc == nullptr) + { + return nullptr; + } + Reference<XAccessibleContext> pRContext = pXAcc->getAccessibleContext(); + Reference<XAccessibleRelationSet> pRrelationSet = pRContext->getAccessibleRelationSet(); + sal_Int32 nRelations = pRrelationSet->getRelationCount(); + for(sal_Int32 i=0 ; i<nRelations ; i++) + { + AccessibleRelation accRelation = pRrelationSet->getRelation(i); + if(accRelation.RelationType == 7) + { + Sequence< Reference< XInterface > > xTargets = accRelation.TargetSet; + return static_cast<XAccessible*>(xTargets[0].get()); + } + } + return nullptr; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_groupPosition(long __RPC_FAR *groupLevel,long __RPC_FAR *similarItemsInGroup,long __RPC_FAR *positionInGroup) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(groupLevel == nullptr || similarItemsInGroup == nullptr || positionInGroup == nullptr) + { + return E_INVALIDARG; + } + + if (!m_xAccessible.is()) + return E_FAIL; + + Reference<XAccessibleContext> const pRContext = + m_xAccessible->getAccessibleContext(); + if(!pRContext.is()) + return E_FAIL; + const sal_Int16 nRole = pRContext->getAccessibleRole(); + + *groupLevel = 0; + *similarItemsInGroup = 0; + *positionInGroup = 0; + + if (nRole != AccessibleRole::DOCUMENT && nRole != AccessibleRole::DOCUMENT_PRESENTATION && + nRole != AccessibleRole::DOCUMENT_SPREADSHEET && nRole != AccessibleRole::DOCUMENT_TEXT) + { + Reference< XAccessibleGroupPosition > xGroupPosition( pRContext, UNO_QUERY ); + if ( xGroupPosition.is() ) + { + Sequence< sal_Int32 > rSeq = xGroupPosition->getGroupPosition( Any( pRContext ) ); + if (rSeq.getLength() >= 3) + { + *groupLevel = rSeq[0]; + *similarItemsInGroup = rSeq[1]; + *positionInGroup = rSeq[2]; + return S_OK; + } + return S_OK; + } + } + + Reference< XAccessible> pParentAcc = pRContext->getAccessibleParent(); + if( !pParentAcc.is() ) + { + return S_OK; + } + + Reference<XAccessibleContext> pRParentContext = pParentAcc->getAccessibleContext(); + + if (nRole == AccessibleRole::RADIO_BUTTON) + { + int index = 0; + int number = 0; + Reference<XAccessibleRelationSet> pRrelationSet = pRContext->getAccessibleRelationSet(); + long nRel = pRrelationSet->getRelationCount(); + for(int i=0 ; i<nRel ; i++) + { + AccessibleRelation accRelation = pRrelationSet->getRelation(i); + if(accRelation.RelationType == 7) + { + Sequence< Reference< XInterface > > xTargets = accRelation.TargetSet; + + Reference<XInterface> pRAcc = xTargets[0]; + sal_Int64 nChildCount = pRParentContext->getAccessibleChildCount(); + assert(nChildCount < std::numeric_limits<long>::max()); + for (sal_Int64 j = 0; j< nChildCount; j++) + { + if( getTheParentOfMember(pRParentContext->getAccessibleChild(j).get()) + == static_cast<XAccessible*>(pRAcc.get()) && + pRParentContext->getAccessibleChild(j)->getAccessibleContext()->getAccessibleRole() == AccessibleRole::RADIO_BUTTON) + number++; + if (pRParentContext->getAccessibleChild(j).get() == m_xAccessible.get()) + index = number; + } + } + } + *groupLevel = 1; + *similarItemsInGroup = number; + *positionInGroup = index; + return S_OK; + } + + else if (nRole == AccessibleRole::COMBO_BOX) + { + *groupLevel = 1; + *similarItemsInGroup = 0; + *positionInGroup = -1; + + if (pRContext->getAccessibleChildCount() != 2) + { + return S_OK; + } + Reference<XAccessible> xList=pRContext->getAccessibleChild(1); + if (!xList.is()) + { + return S_OK; + } + Reference<XAccessibleContext> xListContext(xList,UNO_QUERY); + if (!xListContext.is()) + { + return S_OK; + } + Reference<XAccessibleSelection> xListSel(xList,UNO_QUERY); + if (!xListSel.is()) + { + return S_OK; + } + sal_Int64 nChildCount = xListContext->getAccessibleChildCount(); + assert(nChildCount < std::numeric_limits<long>::max()); + *similarItemsInGroup = nChildCount; + if (*similarItemsInGroup > 0 ) + { + try + { + Reference<XAccessible> xChild = xListSel->getSelectedAccessibleChild(0); + if (xChild.is()) + { + Reference<XAccessibleContext> xChildContext(xChild,UNO_QUERY); + if (xChildContext.is()) + { + *positionInGroup=xChildContext->getAccessibleIndexInParent() + 1 ; + return S_OK; + } + } + } + catch(...) + {} + } + return S_OK; + } + else if (nRole == AccessibleRole::PAGE_TAB) + { + *groupLevel = 1; + sal_Int64 nChildCount = pRParentContext->getAccessibleChildCount(); + assert(nChildCount < std::numeric_limits<long>::max()); + *similarItemsInGroup = nChildCount; + if (*similarItemsInGroup > 0 ) + { + *positionInGroup=pRContext->getAccessibleIndexInParent() + 1 ; + } + else + { + *positionInGroup = -1; + } + return S_OK; + } + + int level = 0; + bool isFound = false; + while( pParentAcc.is() && !isFound) + { + level++; + pRParentContext = pParentAcc->getAccessibleContext(); + const sal_Int16 nParentRole = pRParentContext->getAccessibleRole(); + if ((nParentRole == AccessibleRole::TREE) || (nParentRole == AccessibleRole::LIST)) + isFound = true; + pParentAcc = pRParentContext->getAccessibleParent(); + } + + if( isFound ) + { + Reference< XAccessible> pTempAcc = pRContext->getAccessibleParent(); + pRParentContext = pTempAcc->getAccessibleContext(); + *groupLevel = level; + sal_Int64 nChildCount = pRParentContext->getAccessibleChildCount(); + assert(nChildCount < std::numeric_limits<long>::max()); + *similarItemsInGroup = nChildCount; + *positionInGroup = pRContext->getAccessibleIndexInParent() + 1; + } + else + { + *groupLevel = 0; + *similarItemsInGroup = 0; + *positionInGroup = 0; + } + return S_OK; + + } catch(...) { return E_FAIL; } +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_extendedStates(long, BSTR __RPC_FAR *__RPC_FAR *, long __RPC_FAR *) +{ + return E_NOTIMPL; +} + + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_uniqueID(long __RPC_FAR *uniqueID) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(uniqueID == nullptr) + { + return E_INVALIDARG; + } + *uniqueID = m_dChildID; + return S_OK; + + } catch(...) { return E_FAIL; } +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_windowHandle(HWND __RPC_FAR *windowHandle) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(windowHandle == nullptr) + { + return E_INVALIDARG; + } + + HWND nHwnd = m_hwnd; + IAccessible* pParent = m_pIParent; + while((nHwnd==nullptr) && pParent) + { + if (CMAccessible* pChild = dynamic_cast<CMAccessible*>(pParent)) + { + pParent = pChild->m_pIParent; + nHwnd = pChild->m_hwnd; + } + else + pParent = nullptr; + } + + *windowHandle = nHwnd; + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* Get XAccessibleContext directly from UNO by the stored XAccessible pointer +* @param pXAcc, UNO XAccessible object point. +* @return XAccessibleContext*, the context of the pXAcc. +*/ +XAccessibleContext* CMAccessible::GetContextByXAcc( XAccessible* pXAcc ) +{ + Reference< XAccessibleContext > pRContext; + if( pXAcc == nullptr) + return nullptr; + + pRContext = pXAcc->getAccessibleContext(); + if( !pRContext.is() ) + return nullptr; + return pRContext.get(); +} + +/** +* When COM is created, UNO set XAccessible pointer to it +* in order to COM can operate UNO information +* @param pXAcc, the XAccessible object of current object. +* @return S_OK if successful. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::SetXAccessible(hyper pXAcc) +{ + // internal IMAccessible - no mutex meeded + + m_xAccessible = reinterpret_cast<XAccessible*>(pXAcc); + m_pEnumVar->PutSelection(/*XAccessibleSelection*/ + reinterpret_cast<hyper>(m_xAccessible.get())); + + m_xContext = m_xAccessible->getAccessibleContext(); + + return S_OK; +} + +/** +* accSelect method has many optional flags, needs to process comprehensively +* Mozilla and Microsoft do not implement SELFLAG_EXTENDSELECTION flag. +* The implementation of this flag is a little trouble-shooting,so we also +* do not implement it now +* @param flagsSelect, the selection flag of the select action. +* @param varChild, the child object pointer of current action. +* @return S_OK if successful. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::accSelect(long flagsSelect, VARIANT varChild) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + if( (flagsSelect&SELFLAG_ADDSELECTION) && + (SELFLAG_REMOVESELECTION&flagsSelect) ) + return E_INVALIDARG; + + if ( (flagsSelect&SELFLAG_TAKESELECTION) && + ( + (flagsSelect&SELFLAG_ADDSELECTION) || + (flagsSelect&SELFLAG_REMOVESELECTION) || + (flagsSelect&SELFLAG_EXTENDSELECTION ) + ) + ) + return E_INVALIDARG; + + if ( varChild.vt != VT_I4 ) + return E_INVALIDARG; + + IMAccessible* pSelectAcc; + if( varChild.lVal == CHILDID_SELF ) + { + pSelectAcc = this; + pSelectAcc->AddRef(); + } + else + { + pSelectAcc = GetChildInterface(varChild.lVal); + } + + if( pSelectAcc == nullptr ) + return E_INVALIDARG; + + if( flagsSelect&SELFLAG_TAKEFOCUS ) + { + union { + XAccessible* pTempUNO; + hyper nHyper = 0; + }; + pSelectAcc->GetUNOInterface(&nHyper); + + if( pTempUNO == nullptr ) + return 0; + + Reference<XAccessibleContext> pRContext = pTempUNO->getAccessibleContext(); + Reference< XAccessibleComponent > pRComponent(pRContext,UNO_QUERY); + Reference< XAccessible > pRParentXAcc = pRContext->getAccessibleParent(); + Reference< XAccessibleContext > pRParentContext = pRParentXAcc->getAccessibleContext(); + Reference< XAccessibleComponent > pRParentComponent(pRParentContext,UNO_QUERY); + Reference< XAccessibleSelection > pRParentSelection(pRParentContext,UNO_QUERY); + + + pRComponent->grabFocus(); + + if( flagsSelect & SELFLAG_TAKESELECTION ) + { + pRParentSelection->clearAccessibleSelection(); + pRParentSelection->selectAccessibleChild( pRContext->getAccessibleIndexInParent() ); + } + + if( flagsSelect & SELFLAG_ADDSELECTION ) + { + pRParentSelection->selectAccessibleChild( pRContext->getAccessibleIndexInParent() ); + } + + if( flagsSelect & SELFLAG_REMOVESELECTION ) + { + pRParentSelection->deselectAccessibleChild( pRContext->getAccessibleIndexInParent() ); + } + + if( flagsSelect & SELFLAG_EXTENDSELECTION ) + { + sal_Int64 indexInParrent = pRContext->getAccessibleIndexInParent(); + + if( pRParentSelection->isAccessibleChildSelected( indexInParrent + 1 ) || + pRParentSelection->isAccessibleChildSelected( indexInParrent - 1 ) ) + { + pRParentSelection->selectAccessibleChild( indexInParrent ); + } + } + + } + + pSelectAcc->Release(); + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* Return XAccessible interface pointer when needed +* @param pXAcc, [in, out] the Uno interface of the current object. +* @return S_OK if successful. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::GetUNOInterface(hyper * pXAcc) +{ + // internal IMAccessible - no mutex meeded + + if(pXAcc == nullptr) + return E_INVALIDARG; + + *pXAcc = reinterpret_cast<hyper>(m_xAccessible.get()); + return S_OK; +} + +/** +* Helper method for Implementation of get_accDefaultAction +* @param pAction, the default action point of the current object. +* @return S_OK if successful. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::SetDefaultAction(hyper pAction) +{ + // internal IMAccessible - no mutex meeded + + m_xAction = reinterpret_cast<XAccessibleAction*>(pAction); + return S_OK; +} + +/** +* This method is called when AT open some UI elements initially +* the UI element takes the default action defined here +* @param varChild, the child id of the defaultaction. +* @param pszDefaultAction,[in/out] the description of the current action. +* @return S_OK if successful. +*/ +COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE CMAccessible::get_accDefaultAction(VARIANT varChild, BSTR *pszDefaultAction) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if(pszDefaultAction == nullptr) + { + return E_INVALIDARG; + } + if(varChild.vt==VT_I4) + { + if(varChild.lVal==CHILDID_SELF) + { + if (!m_xAction.is()) + return DISP_E_MEMBERNOTFOUND; + SysFreeString(*pszDefaultAction); + *pszDefaultAction = SysAllocString(m_pszActionDescription); + return S_OK; + } + + long lVal = varChild.lVal; + varChild.lVal = CHILDID_SELF; + IMAccessible *pChild = this->GetChildInterface(lVal); + if(!pChild) + return E_FAIL; + return pChild->get_accDefaultAction(varChild,pszDefaultAction); + } + return S_FALSE; + + } catch(...) { return E_FAIL; } +} + +/** +* AT call this method to operate application +* @param varChild, the child id of the action object. +* @return S_OK if successful. +*/ +COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE CMAccessible::accDoDefaultAction(VARIANT varChild) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + if( varChild.vt != VT_I4 ) + return E_INVALIDARG; + if (!m_xAction.is()) + return E_FAIL; + if (m_xAction->getAccessibleActionCount() == 0) + return E_FAIL; + + if(varChild.lVal==CHILDID_SELF) + { + if (m_xAction->getAccessibleActionCount() > 0) + m_xAction->doAccessibleAction(0); + return S_OK; + } + + long lVal = varChild.lVal; + varChild.lVal = CHILDID_SELF; + IMAccessible *pChild = this->GetChildInterface(lVal); + if(!pChild) + return E_FAIL; + return pChild->accDoDefaultAction( varChild ); + + } catch(...) { return E_FAIL; } +} + +/** +* UNO set description information for action to COM. +* @param szAction, the action description of the current object. +* @return S_OK if successful. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_ActionDescription( const OLECHAR* szAction) +{ + // internal IMAccessible - no mutex meeded + + try { + if (m_isDestroy) return S_FALSE; + + if(szAction == nullptr) + { + return E_INVALIDARG; + } + SysFreeString(m_pszActionDescription ); + m_pszActionDescription = SysAllocString( szAction ); + return S_OK; + + } catch(...) { return E_FAIL; } +} + +bool CMAccessible::GetXInterfaceFromXAccessible(XAccessible* pXAcc, XInterface** ppXI, XInterfaceType eType) +{ + switch(eType) + { + case XInterfaceType::XI_COMPONENT: + return queryXInterface<XAccessibleComponent>(pXAcc, ppXI); + case XInterfaceType::XI_TEXT: + return queryXInterface<XAccessibleText>(pXAcc, ppXI); + case XInterfaceType::XI_EDITABLETEXT: + return queryXInterface<XAccessibleEditableText>(pXAcc, ppXI); + case XInterfaceType::XI_TABLE: + return queryXInterface<XAccessibleTable>(pXAcc, ppXI); + case XInterfaceType::XI_TABLECELL: + // needs specific handling, since there's no XInterface for table cells + return queryTableCell(pXAcc, ppXI); + case XInterfaceType::XI_SELECTION: + return queryXInterface<XAccessibleSelection>(pXAcc, ppXI); + case XInterfaceType::XI_EXTENDEDCOMP: + return queryXInterface<XAccessibleExtendedComponent>(pXAcc, ppXI); + case XInterfaceType::XI_KEYBINDING: + return queryXInterface<XAccessibleKeyBinding>(pXAcc, ppXI); + case XInterfaceType::XI_ACTION: + return queryXInterface<XAccessibleAction>(pXAcc, ppXI); + case XInterfaceType::XI_VALUE: + return queryXInterface<XAccessibleValue>(pXAcc, ppXI); + case XInterfaceType::XI_HYPERTEXT: + return queryXInterface<XAccessibleHypertext>(pXAcc, ppXI); + case XInterfaceType::XI_HYPERLINK: + return queryXInterface<XAccessibleHyperlink>(pXAcc, ppXI); + case XInterfaceType::XI_IMAGE: + return queryXInterface<XAccessibleImage>(pXAcc, ppXI); + default: + return false; + } +} + +template<typename T> static HRESULT +createAggInstance(CMAccessible &rOuter, void ** ppvObject) +{ + // Note: CComAggObject has special handling for IUnknown - must + // query for that when creating it! Otherwise we get a T member of it + // which will redirect QueryInterface back to CMAccessible infinitely. + // (CComAggObject has its own ref-count too which is not a problem + // since it is inserted in m_containedObjects.) + return CComCreator< CComAggObject<T> >::CreateInstance( + rOuter.GetControllingUnknown(), IID_IUnknown, ppvObject); +} + +typedef HRESULT (AggCreatorFunc)(CMAccessible &, void **); + +namespace { + +struct AggMapEntry +{ + const IID* piid; + AggCreatorFunc* pfnCreateInstance; + const XInterfaceType eXInterfaceType; +}; + +} + +static AggMapEntry g_CMAccessible_AggMap[] = { + { &IID_IAccessibleComponent, &createAggInstance<CAccComponent>, XInterfaceType::XI_COMPONENT }, + { &IID_IAccessibleText, &createAggInstance<CAccText>, XInterfaceType::XI_TEXT }, + { &IID_IAccessibleEditableText, &createAggInstance<CAccEditableText>, XInterfaceType::XI_EDITABLETEXT }, + { &IID_IAccessibleImage, &createAggInstance<CAccImage>, XInterfaceType::XI_IMAGE }, + { &IID_IAccessibleTable, &createAggInstance<CAccTable>, XInterfaceType::XI_TABLE }, + { &IID_IAccessibleTable2, &createAggInstance<CAccTable>, XInterfaceType::XI_TABLE }, + { &IID_IAccessibleTableCell, &createAggInstance<CAccTableCell>, XInterfaceType::XI_TABLECELL }, + { &IID_IAccessibleAction, &createAggInstance<CAccAction>, XInterfaceType::XI_ACTION }, + { &IID_IAccessibleValue, &createAggInstance<CAccValue>, XInterfaceType::XI_VALUE }, + { &IID_IAccessibleHypertext, &createAggInstance<CAccHypertext>, XInterfaceType::XI_HYPERTEXT }, + { &IID_IAccessibleHyperlink, &createAggInstance<CAccHyperLink>, XInterfaceType::XI_HYPERLINK } +}; + + +HRESULT WINAPI CMAccessible::SmartQI(void* /*pv*/, REFIID iid, void** ppvObject) +{ + try { + + if (m_isDestroy) return S_FALSE; + if (InlineIsEqualGUID(iid,IID_IAccIdentity) || + InlineIsEqualGUID(iid,IID_IStdMarshalInfo) || + InlineIsEqualGUID(iid,IID_IMarshal) || + InlineIsEqualGUID(iid,IID_IExternalConnection)|| + InlineIsEqualGUID(iid,IID_IOleWindow)) + { + return E_FAIL; + } + + for (const AggMapEntry& rEntry : g_CMAccessible_AggMap) + { + if (InlineIsEqualGUID(iid, *rEntry.piid)) + { + SolarMutexGuard g; + + XInterface* pXI = nullptr; + bool bFound = GetXInterfaceFromXAccessible(m_xAccessible.get(), + &pXI, rEntry.eXInterfaceType); + if(!bFound) + { + return E_FAIL; + } + + XGUIDToComObjHash::iterator pIndTemp = m_containedObjects.find( iid ); + if ( pIndTemp != m_containedObjects.end() ) + { + return pIndTemp->second.p->QueryInterface( iid, ppvObject ); + } + else + { + HRESULT hr = rEntry.pfnCreateInstance(*this, ppvObject); + assert(hr == S_OK); + if(hr == S_OK) + { + m_containedObjects.emplace(*rEntry.piid, static_cast<IUnknown*>(*ppvObject)); + IUNOXWrapper* wrapper = nullptr; + static_cast<IUnknown*>(*ppvObject)->QueryInterface(IID_IUNOXWrapper, reinterpret_cast<void**>(&wrapper)); + if(wrapper) + { + wrapper->put_XInterface( + reinterpret_cast<hyper>(m_xAccessible.get())); + wrapper->Release(); + } + return S_OK; + } + } + return E_FAIL; + } + } + return E_FAIL; + + } catch(...) { return E_FAIL; } +} + +IAccessible* CMAccessible::get_IAccessibleFromXAccessible(XAccessible* pXAcc) +{ + try + { + if (g_pAccObjectManager) + return g_pAccObjectManager->GetIAccessibleFromXAccessible(pXAcc); + } + catch(...) + { + } + return nullptr; +} + +void CMAccessible::ConvertAnyToVariant(const css::uno::Any &rAnyVal, VARIANT *pvData) +{ + if(rAnyVal.hasValue()) + { + // Clear VARIANT variable. + VariantClear(pvData); + + // Set value according to value type. + switch(rAnyVal.getValueTypeClass()) + { + case TypeClass_CHAR: + pvData->vt = VT_UI1; + memcpy(&pvData->bVal, rAnyVal.getValue(), sizeof(char)); + break; + + case TypeClass_BOOLEAN: + { + bool bBoolean(false); + rAnyVal >>= bBoolean; + pvData->vt = VT_BOOL; + pvData->boolVal = VARIANT_BOOL(bBoolean); // boolVal is a VARIANT_BOOL, a 16bit field + break; + } + case TypeClass_BYTE: + pvData->vt = VT_UI1; + memcpy(&pvData->bVal, rAnyVal.getValue(), sizeof(sal_Int8)); + break; + + case TypeClass_SHORT: + pvData->vt = VT_I2; + memcpy(&pvData->iVal, rAnyVal.getValue(), sizeof(sal_Int16)); + break; + + case TypeClass_UNSIGNED_SHORT: + pvData->vt = VT_I2; + memcpy(&pvData->iVal, rAnyVal.getValue(), sizeof(sal_uInt16)); + break; + + case TypeClass_LONG: + pvData->vt = VT_I4; + memcpy(&pvData->lVal, rAnyVal.getValue(), sizeof(sal_Int32)); + break; + + case TypeClass_UNSIGNED_LONG: + pvData->vt = VT_I4; + memcpy(&pvData->lVal, rAnyVal.getValue(), sizeof(sal_uInt32)); + break; + + case TypeClass_FLOAT: + pvData->vt = VT_R4; + memcpy(&pvData->fltVal, rAnyVal.getValue(), sizeof(float)); + break; + + case TypeClass_DOUBLE: + pvData->vt = VT_R8; + memcpy(&pvData->dblVal, rAnyVal.getValue(), sizeof(double)); + break; + + case TypeClass_STRING: + { + pvData->vt = VT_BSTR; + OUString val; + rAnyVal >>= val; + pvData->bstrVal = SysAllocString(o3tl::toW(val.getStr())); + break; + } + + case TypeClass_VOID: + case TypeClass_HYPER: + case TypeClass_UNSIGNED_HYPER: + case TypeClass_TYPE: + case TypeClass_ANY: + case TypeClass_ENUM: + case TypeClass_TYPEDEF: + case TypeClass_STRUCT: + case TypeClass_EXCEPTION: + case TypeClass_SEQUENCE: + case TypeClass_INTERFACE: + { + Reference< XAccessible > pXAcc; + if(rAnyVal >>= pXAcc) + { + if(pXAcc.is()) + { + IAccessible* pIAcc = get_IAccessibleFromXAccessible(pXAcc.get()); + if(pIAcc == nullptr) + { + Reference< XAccessibleContext > pXAccContext = pXAcc->getAccessibleContext(); + g_pAccObjectManager->InsertAccObj(pXAcc.get(),pXAccContext->getAccessibleParent().get()); + pIAcc = get_IAccessibleFromXAccessible(pXAcc.get()); + } + if(pIAcc) + { + pIAcc->AddRef(); + + pvData->vt = VT_UNKNOWN; + pvData->pdispVal = pIAcc; + break; + } + } + } + [[fallthrough]]; + } + case TypeClass_SERVICE: + case TypeClass_MODULE: + case TypeClass_INTERFACE_METHOD: + case TypeClass_INTERFACE_ATTRIBUTE: + case TypeClass_UNKNOWN: + case TypeClass_PROPERTY: + case TypeClass_CONSTANT: + case TypeClass_CONSTANTS: + case TypeClass_SINGLETON: + case TypeClass::TypeClass_MAKE_FIXED_SIZE: + // Output the type string, if there is other uno value type. + pvData->vt = VT_BSTR; + pvData->bstrVal = SysAllocString(o3tl::toW(rAnyVal.getValueTypeName().getStr())); + break; + + default: + break; + } + } + else + { + VariantClear(pvData); + } +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_states(AccessibleStates __RPC_FAR *states) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if (!m_xContext.is()) + return E_FAIL; + + sal_Int64 const nRStateSet = + m_xContext->getAccessibleStateSet(); + + *states = 0x0; + for (int i = 0; i < 63; ++i) + { + sal_Int64 nUnoState = sal_Int64(1) << i; + if (nRStateSet & nUnoState) + lcl_addIA2State(*states, nUnoState, m_xContext->getAccessibleRole()); + } + + return S_OK; + + + } catch(...) { return E_FAIL; } +} + +// return the UNO roles +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_extendedRole(BSTR __RPC_FAR *) +{ + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_localizedExtendedRole(BSTR __RPC_FAR *) +{ + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_nExtendedStates(long __RPC_FAR *) +{ + return E_NOTIMPL; +} + + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_localizedExtendedStates(long, BSTR __RPC_FAR *__RPC_FAR *, long __RPC_FAR *) +{ + return E_NOTIMPL; +} + + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_indexInParent(long __RPC_FAR *accParentIndex) +{ + try { + if (m_isDestroy) return S_FALSE; + + if(accParentIndex == nullptr) + return E_INVALIDARG; + + if (!m_xContext.is()) + return E_FAIL; + + sal_Int64 nIndex = m_xContext->getAccessibleIndexInParent(); + if (nIndex > std::numeric_limits<long>::max()) + { + SAL_WARN("iacc2", "CMAccessible::get_indexInParent: Child index exceeds maximum long value, " + "returning max long."); + nIndex = std::numeric_limits<long>::max(); + } + *accParentIndex = nIndex; + return S_OK; + + + } catch(...) { return E_FAIL; } +} +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_locale( IA2Locale __RPC_FAR *locale ) +{ + try { + if (m_isDestroy) return S_FALSE; + if(locale == nullptr) + return E_INVALIDARG; + + if (!m_xContext.is()) + return E_FAIL; + + css::lang::Locale unoLoc = m_xContext->getLocale(); + locale->language = SysAllocString(o3tl::toW(unoLoc.Language.getStr())); + locale->country = SysAllocString(o3tl::toW(unoLoc.Country.getStr())); + locale->variant = SysAllocString(o3tl::toW(unoLoc.Variant.getStr())); + + return S_OK; + + } catch(...) { return E_FAIL; } +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_appName(BSTR __RPC_FAR *name) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + if(name == nullptr) + return E_INVALIDARG; + + static const OUString sAppName = utl::ConfigManager::getProductName(); + *name = SysAllocString(o3tl::toW(sAppName.getStr())); + return S_OK; + } catch(...) { return E_FAIL; } +} +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_appVersion(BSTR __RPC_FAR *version) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + if(version == nullptr) + return E_INVALIDARG; + static const OUString sVersion = utl::ConfigManager::getProductVersion(); + *version=SysAllocString(o3tl::toW(sVersion.getStr())); + return S_OK; + } catch(...) { return E_FAIL; } +} +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_toolkitName(BSTR __RPC_FAR *name) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + if(name == nullptr) + return E_INVALIDARG; + *name = SysAllocString(OLESTR("VCL")); + return S_OK; + } catch(...) { return E_FAIL; } +} +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_toolkitVersion(BSTR __RPC_FAR *version) +{ + return get_appVersion(version); +} + + +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_attributes(/*[out]*/ BSTR *pAttr) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + + if (!m_xAccessible.is()) + return E_FAIL; + + Reference<XAccessibleContext> pRContext = m_xAccessible->getAccessibleContext(); + if( !pRContext.is() ) + { + return E_FAIL; + } + + OUString sAttributes; + Reference<XAccessibleExtendedAttributes> pRXI(pRContext,UNO_QUERY); + if (pRXI.is()) + { + css::uno::Reference<css::accessibility::XAccessibleExtendedAttributes> pRXAttr; + pRXAttr = pRXI.get(); + css::uno::Any anyVal = pRXAttr->getExtendedAttributes(); + + OUString val; + anyVal >>= val; + sAttributes += val; + } + + // some text-specific IAccessible2 object attributes (like text alignment + // of a paragraph) are handled as text attributes in LibreOffice + Reference<XAccessibleText> xText(pRContext, UNO_QUERY); + if (xText.is()) + { + sal_Int32 nStartOffset = 0; + sal_Int32 nEndOffset = 0; + sAttributes += AccessibleTextAttributeHelper::GetIAccessible2TextAttributes( + xText, IA2AttributeType::ObjectAttributes, 0, nStartOffset, nEndOffset); + } + + if (*pAttr) + SysFreeString(*pAttr); + *pAttr = SysAllocString(o3tl::toW(sAttributes.getStr())); + + return S_OK; + } catch(...) { return E_FAIL; } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |