diff options
Diffstat (limited to 'winaccessibility/source/UAccCOM/MAccessible.cxx')
-rw-r--r-- | winaccessibility/source/UAccCOM/MAccessible.cxx | 3162 |
1 files changed, 3162 insertions, 0 deletions
diff --git a/winaccessibility/source/UAccCOM/MAccessible.cxx b/winaccessibility/source/UAccCOM/MAccessible.cxx new file mode 100644 index 000000000..848960ff7 --- /dev/null +++ b/winaccessibility/source/UAccCOM/MAccessible.cxx @@ -0,0 +1,3162 @@ +/* -*- 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 "MAccessible.h" + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#endif +#include <UAccCOM.h> +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#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 <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; +using namespace com::sun::star::accessibility::AccessibleStateType; + +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; +} + +} + +// IA2 states mapping, and name +// maintenance the consistency, change one array, change the three all +long const IA2_STATES[] = +{ + IA2_STATE_ACTIVE, // = 0x1; + IA2_STATE_ARMED, // = 0x2; + IA2_STATE_DEFUNCT, // = 0x4; + IA2_STATE_EDITABLE, // = 0x8; + IA2_STATE_HORIZONTAL, // = 0x10; + IA2_STATE_ICONIFIED, // = 0x20; + IA2_STATE_INVALID_ENTRY, // = 0x80; + IA2_STATE_MANAGES_DESCENDANTS, // = 0x100; + IA2_STATE_MODAL, // = 0x200; + IA2_STATE_MULTI_LINE, // = 0x400; + IA2_STATE_OPAQUE, // = 0x800; + IA2_STATE_REQUIRED, // = 0x2000; + IA2_STATE_SELECTABLE_TEXT, // = 0x3000; + IA2_STATE_SINGLE_LINE, // = 0x4000; + IA2_STATE_STALE, // = 0x8000; + IA2_STATE_SUPPORTS_AUTOCOMPLETION, // = 0x10000; + IA2_STATE_TRANSIENT, //= 0x20000; + IA2_STATE_VERTICAL // = 0x40000; +}; +/* + +<=== map ===> + +*/ +short const UNO_STATES[] = +{ + ACTIVE, // = (sal_Int16)1; + ARMED, // = (sal_Int16)2; + DEFUNC, // = (sal_Int16)5; + EDITABLE, // = (sal_Int16)6; + HORIZONTAL, // = (sal_Int16)12; + ICONIFIED, // = (sal_Int16)13; + -1, //IA2_STATE_INVALID_ENTRY + MANAGES_DESCENDANTS, // = (sal_Int16)15; + MODAL, // = (sal_Int16)16; + MULTI_LINE, // = (sal_Int16)17; + OPAQUE, // = (sal_Int16)19; + -1, //IA2_STATE_REQUIRED + -1, //IA2_STATE_SELECTABLE_TEXT + SINGLE_LINE, // = (sal_Int16)26; + STALE, // = (sal_Int16)27; + -1, //IA2_STATE_SUPPORTS_AUTOCOMPLETION + TRANSIENT, //IA2_STATE_TRANSIENT + VERTICAL // = (sal_Int16)29; +}; + +using namespace com::sun::star::accessibility::AccessibleRole; + + +AccObjectManagerAgent* CMAccessible::g_pAgent = 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) +{ + m_sLocation.m_dLeft=0; + m_sLocation.m_dTop = 0; + m_sLocation.m_dWidth=0; + m_sLocation.m_dHeight=0; + 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; + // #CHECK# + 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; + // #CHECK# + if(pcountChildren == nullptr) + { + return E_INVALIDARG; + } + + if (!m_xAccessible.is()) + return S_FALSE; + + Reference<XAccessibleContext> const pRContext = + m_xAccessible->getAccessibleContext(); + if( pRContext.is() ) + { + *pcountChildren = pRContext->getAccessibleChildCount(); + } + + 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; + // #CHECK# + 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; + // #CHECK# + 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; + // #CHECK# + 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; + // #CHECK# + 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; + // #CHECK# + 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; + // #CHECK# + 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; + // #CHECK# + 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; + // #CHECK# + 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 = nullptr; + g_pAgent->GetIAccessibleFromResID(m_dFocusChildID,&pIMAcc); + 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; + // #CHECK# + 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; + // #CHECK# + 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()) + { + 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; + } + else + { + *pxLeft = m_sLocation.m_dLeft; + *pyTop = m_sLocation.m_dTop; + *pcxWidth = m_sLocation.m_dWidth; + *pcyHeight = m_sLocation.m_dHeight; + 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; + // #CHECK# + 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; + + try { + if (m_isDestroy) return S_FALSE; + // #CHECK# + if(pvarChild == nullptr) + { + return E_INVALIDARG; + } + long x, y, w, h; + VARIANT varSelf; + VariantInit(&varSelf); + varSelf.vt = VT_I4; + varSelf.lVal = CHILDID_SELF; + accLocation(&x,&y,&w,&h,varSelf); + if( (x < xLeft && (x + w) >xLeft) && (y < yTop && (y + h) >yTop) ) + { + int i, nCount; + pvarChild->vt = VT_EMPTY; + Reference< XAccessibleContext > pRContext = GetContextByXAcc(m_xAccessible.get()); + nCount = pRContext->getAccessibleChildCount(); + if(nCount > 256) + return E_FAIL; + IMAccessible* child = nullptr; + for( i = 0; i<nCount; i++) + { + + child = GetChildInterface(i + 1); + if(child && child->accHitTest(xLeft,yTop,pvarChild) == S_OK) + break; + } + + if(pvarChild->vt == VT_DISPATCH) + return S_OK; + + if( i < nCount) + { + pvarChild->vt = VT_DISPATCH; + pvarChild->pdispVal = child; + child->AddRef(); + } + else + { + pvarChild->vt = VT_I4; + pvarChild->lVal = CHILDID_SELF; + } + return S_OK; + } + return S_FALSE; + + } 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; +} + +/** +* Set the accessible name 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 szName, the name used to set the name of the proper object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::put_accName(VARIANT varChild, BSTR szName) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + if(varChild.vt==VT_I4) + { + if(varChild.lVal==CHILDID_SELF) + { + SysFreeString(m_pszName); + m_pszName=SysAllocString(szName); + return S_OK; + } + + long lVal = varChild.lVal; + varChild.lVal = CHILDID_SELF; + IMAccessible *pChild = this->GetChildInterface(lVal); + if(!pChild) + return E_FAIL; + return pChild->put_accName(varChild,szName); + } + return E_FAIL; + + } catch(...) { return E_FAIL; } +} + +/** +* 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; + // #CHECK# + 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; + // #CHECK# + 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 object location for the current COM object +* @param sLocation, the location of the current object. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccLocation(const Location sLocation) +{ + // internal IMAccessible - no mutex meeded + + this->m_sLocation = sLocation; + return S_OK; +} + +/** +* 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 AccObjectManagerAgent object pointer to COM +* @param pAgent, the AccObjectManagerAgent point. +* @return S_OK if successful and E_FAIL if failure. +*/ +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccAgent(hyper pAgent) +{ + // internal IMAccessible - no mutex meeded + + g_pAgent = reinterpret_cast<AccObjectManagerAgent*>(pAgent); + 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_pAgent) + { + IMAccessible* pIMAcc = nullptr; + g_pAgent->GetIAccessibleFromResID(dChildID,&pIMAcc); + 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; + + IAccessible* pChild = nullptr; + Reference< XAccessible > pXChild = pRContext->getAccessibleChild(dChildID-1); + bool isGet = get_IAccessibleFromXAccessible(pXChild.get(), &pChild); + + if(!isGet) + { + g_pAgent->InsertAccObj(pXChild.get(), m_xAccessible.get(), m_hwnd); + isGet = get_IAccessibleFromXAccessible(pXChild.get(), &pChild); + } + + if(isGet) + { + 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; + } + + int count = pXContext->getAccessibleChildCount(); + if(count<1) + { + return nullptr; + } + + IMAccessible* pCurChild = nullptr; + union { + XAccessible* pChildXAcc; + hyper nHyper = 0; + }; + Reference<XAccessible> pRChildXAcc; + XAccessibleContext* pChildContext = nullptr; + int 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_pAgent->InsertAccObj(pChildXAcc, m_xAccessible.get()); + return g_pAgent->GetIMAccByXAcc(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; + // #CHECK# + 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; + // #CHECK# + 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; + // #CHECK# + 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; + + // #CHECK# + 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; + // #CHECK# + 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; + + // #CHECK# + if(relation == nullptr || nRelations == nullptr) + { + return E_INVALIDARG; + } + // #CHECK XInterface# + + 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; + // #CHECK# + 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) +{ + // #CHECK# + 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; + // #CHECK# + 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; + long Role = pRContext->getAccessibleRole(); + + *groupLevel = 0; + *similarItemsInGroup = 0; + *positionInGroup = 0; + + if (Role != AccessibleRole::DOCUMENT && Role != AccessibleRole::DOCUMENT_PRESENTATION && + Role != AccessibleRole::DOCUMENT_SPREADSHEET && Role != 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( Role == 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]; + for(int j=0; j<pRParentContext->getAccessibleChildCount(); j++) + { + if( getTheParentOfMember(pRParentContext->getAccessibleChild(j).get()) + == static_cast<XAccessible*>(pRAcc.get()) && + pRParentContext->getAccessibleChild(j)->getAccessibleContext()->getAccessibleRole() == RADIO_BUTTON) + number++; + if (pRParentContext->getAccessibleChild(j).get() == m_xAccessible.get()) + index = number; + } + } + } + *groupLevel = 1; + *similarItemsInGroup = number; + *positionInGroup = index; + return S_OK; + } + + else if ( COMBO_BOX == Role ) + { + *groupLevel = 1; + *similarItemsInGroup = 0; + *positionInGroup = -1; + + long nCount = pRContext->getAccessibleChildCount(); + if( 2 != nCount) + { + 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; + } + *similarItemsInGroup = xListContext->getAccessibleChildCount(); + 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 ( PAGE_TAB == Role ) + { + *groupLevel = 1; + *similarItemsInGroup = pRParentContext->getAccessibleChildCount(); + + 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(); + Role = pRParentContext->getAccessibleRole(); + if( (Role == TREE) || (Role == LIST) ) + isFound = true; + pParentAcc = pRParentContext->getAccessibleParent(); + } + + if( isFound ) + { + Reference< XAccessible> pTempAcc = pRContext->getAccessibleParent(); + pRParentContext = pTempAcc->getAccessibleContext(); + *groupLevel = level; + *similarItemsInGroup = pRParentContext->getAccessibleChildCount(); + *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; + // #CHECK# + 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; + // #CHECK# + 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(); +} + +/** +* Return the member variable m_pXAccessibleSelection, instead of +* get XAccessibleSelection according to XAccessibleContext because if so,it will +* depend on the UNO implementation code,so when COM is created, put XAccessibleSelection +* by bridge management system +* @return XAccessibleSelection*, the selection of the current object. +*/ +Reference< XAccessibleSelection > CMAccessible::GetSelection() +{ + if (!m_xAccessible.is()) + return nullptr; + Reference<XAccessibleContext> const pRContext = + m_xAccessible->getAccessibleContext(); + if(pRContext.is()) + { + Reference< XAccessibleSelection > pRSelection(pRContext,UNO_QUERY); + return pRSelection; + } + return nullptr; +} + +/** +* Select one XAccessible item, for accSelect implementation +* @param pItem, the item should be selected. +* @return S_OK if successful. +*/ +HRESULT CMAccessible::SelectChild(XAccessible* pItem) +{ + + try { + if (m_isDestroy) return S_FALSE; + XAccessibleContext* pParentContext = GetContextByXAcc(m_xAccessible.get()); + XAccessibleContext* pContext = GetContextByXAcc( pItem ); + if( pParentContext == nullptr || pContext == nullptr ) + return E_FAIL; + + Reference< XAccessibleSelection > pRSelection = GetSelection(); + if( !pRSelection.is() ) + return E_FAIL; + long Index = pContext->getAccessibleIndexInParent(); + pRSelection->selectAccessibleChild( Index ); + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* Deselect one XAccessible item, for accSelect implementation +* @param pItem, the item should be deselected. +* @return S_OK if successful. +*/ +HRESULT CMAccessible::DeSelectChild(XAccessible* pItem) +{ + + try { + if (m_isDestroy) return S_FALSE; + XAccessibleContext* pParentContext = GetContextByXAcc(m_xAccessible.get()); + ; + XAccessibleContext* pContext = GetContextByXAcc( pItem ); + if( pParentContext == nullptr || pContext == nullptr ) + return E_INVALIDARG; + + Reference< XAccessibleSelection > pRSelection = GetSelection(); + if( !pRSelection.is() ) + return E_FAIL; + long Index = pContext->getAccessibleIndexInParent(); + pRSelection->deselectAccessibleChild( Index ); + + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* Select multiple XAccessible items,for implementation of accSelect +* @param pItem, the items should be selected. +* @param size, the size of the items. +* @return S_OK if successful. +*/ +HRESULT CMAccessible::SelectMultipleChidren( XAccessible** pItem,int size ) +{ + + try { + if (m_isDestroy) return S_FALSE; + // #CHECK# + if(pItem == nullptr) + { + return E_INVALIDARG; + } + for(int index = 0;index < size;index++) + { + SelectChild( pItem[index] ); + } + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* Deselect multiple XAccessible items,for implementation of accSelect +* @param pItem, the items should be selected. +* @param size, the size of the items. +* @return S_OK if successful. +*/ +HRESULT CMAccessible::DeSelectMultipleChildren( XAccessible** pItem,int size ) +{ + + try { + if (m_isDestroy) return S_FALSE; + // #CHECK# + if(pItem == nullptr) + { + return E_INVALIDARG; + } + for(int index = 0;index < size;index++) + { + DeSelectChild( pItem[index] ); + } + return S_OK; + + } catch(...) { return E_FAIL; } +} + +/** +* 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 ) + { + long 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; + // #CHECK# + 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; + // #CHECK# + 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; } +} + +bool CMAccessible::get_IAccessibleFromXAccessible(XAccessible* pXAcc, IAccessible** ppIA) +{ + try + { + // #CHECK# + if(ppIA == nullptr) + { + return false; + } + bool isGet = false; + if(g_pAgent) + isGet = g_pAgent->GetIAccessibleFromXAccessible(pXAcc, ppIA); + + return isGet; + } + catch(...) + { + return false; + } +} + +OUString CMAccessible::get_StringFromAny(Any const & pAny) +{ + switch(pAny.getValueTypeClass()) + { + case TypeClass_CHAR: + { + sal_Int8 val; + pAny >>= val; + return OUString::number(val); + } + case TypeClass_BOOLEAN: + { + bool val; + pAny >>= val; + return OUString::number(int(val)); + } + case TypeClass_BYTE: + { + sal_Int8 val; + pAny >>= val; + return OUString::number(val); + } + case TypeClass_SHORT: + { + sal_Int16 val; + pAny >>= val; + return OUString::number(val); + } + case TypeClass_UNSIGNED_SHORT: + { + sal_uInt16 val; + pAny >>= val; + return OUString::number(val); + } + case TypeClass_LONG: + { + sal_Int32 val; + pAny >>= val; + return OUString::number(val); + } + case TypeClass_UNSIGNED_LONG: + { + sal_uInt32 val; + pAny >>= val; + return OUString::number(val); + } + case TypeClass_FLOAT: + { + float val; + pAny >>= val; + return OUString::number(val); + } + case TypeClass_DOUBLE: + { + double val; + pAny >>= val; + return OUString::number(val); + } + case TypeClass_STRING: + { + OUString val; + pAny >>= val; + return val; + } + case TypeClass_SEQUENCE: + { + if(pAny.getValueType() == cppu::UnoType<Sequence< OUString >>::get()) + { + Sequence < OUString > val; + pAny >>= val; + + OUStringBuffer pString; + + for (const OUString& rElem : val) + pString.append(rElem); + + return pString.makeStringAndClear(); + } + else if (pAny.getValueType() == cppu::UnoType<Sequence< css::style::TabStop >>::get()) + { + Sequence < css::style::TabStop > val; + pAny >>= val; + + OUStringBuffer buf; + for (const css::style::TabStop& rSingleVal : val) + { + buf.append("Position="); + buf.append(rSingleVal.Position); + buf.append(",TabAlign="); + buf.append(sal_Int32(rSingleVal.Alignment)); + buf.append(","); + + buf.append("DecimalChar="); + if (rSingleVal.DecimalChar==';' || rSingleVal.DecimalChar == ':' || rSingleVal.DecimalChar == ',' || + rSingleVal.DecimalChar == '=' || rSingleVal.DecimalChar == '\\') + buf.append('\\'); + buf.append(rSingleVal.DecimalChar); + buf.append(","); + + buf.append("FillChar="); + if (rSingleVal.FillChar==';' || rSingleVal.FillChar == ':' || rSingleVal.FillChar == ',' || + rSingleVal.FillChar == '=' || rSingleVal.FillChar == '\\') + buf.append('\\'); + buf.append(rSingleVal.FillChar); + buf.append(","); + } + return buf.makeStringAndClear(); + } + break; + } + case TypeClass_ENUM: + { + if (pAny.getValueType() == cppu::UnoType<css::awt::FontSlant>::get()) + { + css::awt::FontSlant val; + pAny >>= val; + return OUString::number(sal_Int32(val)); + } + break; + } + case TypeClass_STRUCT: + { + if (pAny.getValueType() == cppu::UnoType<css::style::LineSpacing>::get()) + { + css::style::LineSpacing val; + pAny >>= val; + return "Mode=" + OUString::number(val.Mode) + ",Height=" + + OUString::number(val.Height) + ","; + } + else if (pAny.getValueType() == cppu::UnoType<css::accessibility::TextSegment>::get()) + { + css::accessibility::TextSegment val; + pAny >>= val; + return val.SegmentText; + } + break; + } + case TypeClass_VOID: + case TypeClass_HYPER: + case TypeClass_UNSIGNED_HYPER: + case TypeClass_TYPE: + case TypeClass_ANY: + case TypeClass_TYPEDEF: + case TypeClass_EXCEPTION: + case TypeClass_INTERFACE: + 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: + break; + default: + break; + } + return OUString(); +} + +OUString CMAccessible::get_String4Numbering(const Any& pAny, sal_Int16 numberingLevel,std::u16string_view numberingPrefix) +{ + Reference< css::container::XIndexReplace > pXIndex; + if((pAny>>=pXIndex) && (numberingLevel !=-1))//numbering level is -1,means invalid value + { + Any aAny = pXIndex->getByIndex(numberingLevel); + Sequence< css::beans::PropertyValue > aProps; + aAny >>= aProps; + OUStringBuffer buf("Numbering:NumberingLevel="); + buf.append(sal_Int32(numberingLevel)); + buf.append(','); + for (const css::beans::PropertyValue& rProp : aProps) + { + if( (rProp.Name == "BulletChar" ) || + (rProp.Name == "NumberingType" )) + { + buf.append(rProp.Name); + buf.append('='); + auto const pTemp = CMAccessible::get_StringFromAny(rProp.Value); + buf.append(pTemp); + buf.append(','); + + if (rProp.Name == "NumberingType" && !numberingPrefix.empty()) + { + buf.append("NumberingPrefix="); + buf.append(numberingPrefix); + } + } + } + return buf.makeStringAndClear(); + } + + //Because now have three types numbering level: + //1.real numbering list,numbering level>=0 and numbering Rule !=NULL; + //2.common paragraph, numbering level >=0, and numbering Rule == NULL; + //3.TOC paragraph, numbering level >0, and numbering Rule ==NULL; + // IAText:numberinglevel base on 0, but TOC's level base on 1, + // so NumberingLevel value will be decreased 1 in bridge code. + else if(numberingLevel >0) + { + return "Numbering:NumberingLevel=" + OUString::number(numberingLevel-1) + ",NumberingType=4,NumberingPrefix=,"; + } + else + { + return "Numbering:"; + } +} + +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 = nullptr; + get_IAccessibleFromXAccessible(pXAcc.get(), &pIAcc); + if(pIAcc == nullptr) + { + Reference< XAccessibleContext > pXAccContext = pXAcc->getAccessibleContext(); + g_pAgent->InsertAccObj(pXAcc.get(),pXAccContext->getAccessibleParent().get()); + get_IAccessibleFromXAccessible(pXAcc.get(), &pIAcc); + } + 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; + + Reference<XAccessibleStateSet> const pRStateSet = + m_xContext->getAccessibleStateSet(); + if(!pRStateSet.is()) + { + return S_OK; + } + Sequence<short> pStates = pRStateSet->getStates(); + + + long count = pStates.getLength() ; + *states = 0x0; + for( int i = 0; i < count; i++ ) + { + for( std::size_t j = 0; j < SAL_N_ELEMENTS(UNO_STATES); j++ ) + { + if( pStates[i] == UNO_STATES[j] ) + { + *states |= IA2_STATES[j]; + break; + } + } + } + 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; + // #CHECK# + if(accParentIndex == nullptr) + return E_INVALIDARG; + + if (!m_xContext.is()) + return E_FAIL; + + *accParentIndex = m_xContext->getAccessibleIndexInParent(); + 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; + + *name = SysAllocString(OLESTR("Hannover")); + 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; + *version=SysAllocString(OLESTR("3.0")); + 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(" ")); + return S_OK; + } catch(...) { return E_FAIL; } +} +COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_toolkitVersion(BSTR __RPC_FAR *version) +{ + SolarMutexGuard g; + + try { + if (m_isDestroy) return S_FALSE; + if(version == nullptr) + return E_INVALIDARG; + *version = SysAllocString(OLESTR(" ")); + return S_OK; + } catch(...) { return E_FAIL; } +} + + +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; + } + Reference<XAccessibleExtendedAttributes> pRXI(pRContext,UNO_QUERY); + if( !pRXI.is() ) + return E_FAIL; + else + { + css::uno::Reference<css::accessibility::XAccessibleExtendedAttributes> pRXAttr; + pRXAttr = pRXI.get(); + css::uno::Any anyVal = pRXAttr->getExtendedAttributes(); + + OUString val; + anyVal >>= val; + + if(*pAttr) + SysFreeString(*pAttr); + *pAttr = SysAllocString(o3tl::toW(val.getStr())); + + return S_OK; + } + } catch(...) { return E_FAIL; } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |