1
0
Fork 0
libreoffice/winaccessibility/source/UAccCOM/MAccessible.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

2781 lines
88 KiB
C++

/* -*- 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 <systools/win32/oleauto.hxx>
#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
#include <com/sun/star/accessibility/XAccessibleContext2.hpp>
#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
};
enum class NavigationDirection {
FIRST_CHILD,
LAST_CHILD,
NEXT_CHILD,
PREVIOUS_CHILD,
};
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_pszValue(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_pszValue!=nullptr)
{
SysFreeString(std::exchange(m_pszValue, 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())
{
// return error code if child count exceeds max long value
// (for Calc sheets which report all cells as children);
// tdf#153131: Windows Speech Recognition and apparently some other
// tools quering information via the a11y API seem to query all children unconditionally,
// so returning a large number (like std::numeric_limits<long>::max) would cause a freeze
SAL_WARN("iacc2", "CMAccessible::get_accChildCount: Child count exceeds maximum long value");
return S_FALSE;
}
*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)
{
if (!m_xAccessible.is())
return S_FALSE;
Reference<XAccessibleContext> xContext = m_xAccessible->getAccessibleContext();
if (!xContext.is())
return S_FALSE;
const OUString sName = xContext->getAccessibleName();
SysFreeString(*pszName);
*pszName = sal::systools::BStr::newBSTR(sName);
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 = sal::systools::BStr::newBSTR(sDescription);
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 == AccessibleRelationType_LABELED_BY)
{
accRelation = pRrelationSet->getRelation(i);
paccRelation = &accRelation;
}
}
if(paccRelation == nullptr)
return S_FALSE;
Sequence<Reference<XAccessible>> xTargets = paccRelation->TargetSet;
Reference<XAccessible> xAcc = xTargets[0];
Reference<XAccessibleContext> xLabelContext = xAcc->getAccessibleContext();
if (!xLabelContext.is())
return S_FALSE;
pRrelationSet = xLabelContext->getAccessibleRelationSet();
nRelCount = pRrelationSet->getRelationCount();
paccRelation = nullptr;
for(int j=0; j<nRelCount ; j++)
{
if (pRrelationSet->getRelation(j).RelationType == AccessibleRelationType_LABEL_FOR)
{
accRelation = pRrelationSet->getRelation(j);
paccRelation = &accRelation;
}
}
if(paccRelation)
{
xTargets = paccRelation->TargetSet;
xAcc = xTargets[0];
if (m_xAccessible.get() != xAcc.get())
return S_FALSE;
}
Reference<XAccessibleExtendedComponent> pRXIE(xLabelContext, 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 = sal::systools::BStr::newBSTR(wString);
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 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 eDirection, the navigation direction.
* @return IMAccessible*, the child of the end up node.
*/
IMAccessible* CMAccessible::GetNavigateChildForDM(VARIANT varCur, NavigationDirection eDirection)
{
XAccessibleContext* pXContext = GetContextByXAcc(m_xAccessible.get());
if(pXContext==nullptr)
{
return nullptr;
}
sal_Int64 count = pXContext->getAccessibleChildCount();
if(count<1)
{
return nullptr;
}
Reference<XAccessible> pRChildXAcc;
switch(eDirection)
{
case NavigationDirection::FIRST_CHILD:
pRChildXAcc = pXContext->getAccessibleChild(0);
break;
case NavigationDirection::LAST_CHILD:
pRChildXAcc = pXContext->getAccessibleChild(count-1);
break;
case NavigationDirection::NEXT_CHILD:
case NavigationDirection::PREVIOUS_CHILD:
{
IMAccessible* pCurChild = GetChildInterface(varCur.lVal);
if(pCurChild==nullptr)
{
return nullptr;
}
CMAccessible* pChildCMAcc = static_cast<CMAccessible*>(pCurChild);
XAccessible* pChildXAcc = pChildCMAcc->m_xAccessible.get();
if (!pChildXAcc)
return nullptr;
XAccessibleContext* pChildContext = GetContextByXAcc(pChildXAcc);
if(pChildContext == nullptr)
{
return nullptr;
}
const sal_Int64 delta = (eDirection == NavigationDirection::NEXT_CHILD) ? 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
const sal_Int64 index = pChildContext->getAccessibleIndexInParent() + delta;
if((index>=0)&&(index<=count-1))
{
pRChildXAcc = pXContext->getAccessibleChild(index);
}
break;
}
default:
break;
}
if(!pRChildXAcc.is())
{
return nullptr;
}
g_pAccObjectManager->InsertAccObj(pRChildXAcc.get(), m_xAccessible.get());
return g_pAccObjectManager->GetIAccessibleFromXAccessible(pRChildXAcc.get());
}
/**
*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, NavigationDirection::FIRST_CHILD);
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, NavigationDirection::LAST_CHILD);
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 == AccessibleRelationType_MEMBER_OF)
{
Sequence<Reference<XAccessible>> xTargets = accRelation.TargetSet;
return 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 == AccessibleRelationType_MEMBER_OF)
{
Sequence<Reference<XAccessible>> xTargets = accRelation.TargetSet;
Reference<XAccessible> xTarget = 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())
== xTarget.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 )
{
CMAccessible* pSelectCMAcc = static_cast<CMAccessible*>(pSelectAcc);
Reference<XAccessible> xSelectAcc = pSelectCMAcc->m_xAccessible;
if (!xSelectAcc.is())
return 0;
Reference<XAccessibleContext> pRContext = xSelectAcc->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; }
}
/**
* 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)
{
Reference<XAccessibleAction> xAction(m_xContext, UNO_QUERY);
if (!xAction.is() || xAction->getAccessibleActionCount() < 1)
return S_FALSE;
const OUString sActionDescription = xAction->getAccessibleActionDescription(0);
SysFreeString(*pszDefaultAction);
*pszDefaultAction = sal::systools::BStr::newBSTR(sActionDescription);
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 (varChild.lVal == CHILDID_SELF)
{
Reference<XAccessibleAction> xAction(m_xContext, UNO_QUERY);
if (!xAction.is() || xAction->getAccessibleActionCount() < 1)
return S_FALSE;
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; }
}
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 = sal::systools::BStr::newBSTR(val);
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 = sal::systools::BStr::newBSTR(rAnyVal.getValueTypeName());
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 = sal::systools::BStr::newBSTR(unoLoc.Language);
locale->country = sal::systools::BStr::newBSTR(unoLoc.Country);
locale->variant = sal::systools::BStr::newBSTR(unoLoc.Variant);
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 = sal::systools::BStr::newBSTR(sAppName);
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 = sal::systools::BStr::newBSTR(sVersion);
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);
}
// report accessible ID as "id" object attribute as specified in the
// IAccessible2 object attribute spec
Reference<XAccessibleContext2> xContext2(pRContext, UNO_QUERY);
if (xContext2.is())
{
const OUString sId = xContext2->getAccessibleId();
if (!sId.isEmpty())
sAttributes += "id:" + sId + ";";
}
if (*pAttr)
SysFreeString(*pAttr);
*pAttr = sal::systools::BStr::newBSTR(sAttributes);
return S_OK;
} catch(...) { return E_FAIL; }
}
// IAccessible2_2 methods
COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_attribute(BSTR, VARIANT*)
{
return E_NOTIMPL;
}
COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accessibleWithCaret(IUnknown**, long*)
{
return E_NOTIMPL;
}
COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_relationTargetsOfType(BSTR type,
long maxTargets,
IUnknown*** targets,
long* nTargets)
{
SolarMutexGuard g;
if (!type || !targets || !nTargets || maxTargets <= 0)
return E_INVALIDARG;
if (m_isDestroy)
return S_FALSE;
if (!m_xContext.is())
return E_FAIL;
try
{
Reference<XAccessibleRelationSet> xRelationSet = m_xContext->getAccessibleRelationSet();
if (!xRelationSet.is())
return S_FALSE;
const AccessibleRelationType eUnoRelationType = CAccRelation::mapToUnoRelationType(type);
if (eUnoRelationType == AccessibleRelationType_INVALID)
return S_FALSE;
AccessibleRelation aRelation = xRelationSet->getRelationByType(eUnoRelationType);
if (aRelation.RelationType != eUnoRelationType || !aRelation.TargetSet.hasElements())
return S_FALSE;
const sal_Int32 nRetCount
= std::min(sal_Int32(maxTargets), aRelation.TargetSet.getLength());
*targets = static_cast<IUnknown**>(CoTaskMemAlloc(nRetCount * sizeof(IUnknown*)));
assert(*targets && "Failed to allocate memory for relation targets");
for (sal_Int32 i = 0; i < nRetCount; i++)
{
Reference<XAccessible> xTarget = aRelation.TargetSet[i];
assert(xTarget.is());
IAccessible* pIAccessible = CMAccessible::get_IAccessibleFromXAccessible(xTarget.get());
if (!pIAccessible)
{
Reference<XAccessibleContext> xTargetContext = xTarget->getAccessibleContext();
if (!xTargetContext.is())
{
SAL_WARN("iacc2", "Relation target doesn't have an accessible context");
CoTaskMemFree(*targets);
return E_FAIL;
}
Reference<XAccessible> xParent = xTargetContext->getAccessibleParent();
CMAccessible::g_pAccObjectManager->InsertAccObj(xTarget.get(), xParent.get());
pIAccessible = CMAccessible::get_IAccessibleFromXAccessible(xTarget.get());
}
assert(pIAccessible && "Couldn't retrieve IAccessible object for relation target.");
pIAccessible->AddRef();
(*targets)[i] = pIAccessible;
}
*nTargets = nRetCount;
return S_OK;
}
catch (...)
{
return E_FAIL;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */