Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / winaccessibility / source / UAccCOM / MAccessible.cxx
blob24e3bde478c2fc3359939948caa853849de4445e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <UAccCOM.h>
21 #include "MAccessible.h"
23 #include <algorithm>
24 #include <cstddef>
26 #include "AccAction.h"
27 #include "AccRelation.h"
28 #include "AccComponent.h"
29 #include "AccText.h"
30 #include "AccEditableText.h"
31 #include "AccImage.h"
32 #include "AccTable.h"
33 #include "AccTableCell.h"
34 #include "AccValue.h"
35 #include "AccHypertext.h"
36 #include "AccHyperLink.h"
38 #include <rtl/ustrbuf.hxx>
39 #include <sal/log.hxx>
40 #include <unotools/configmgr.hxx>
41 #include <vcl/svapp.hxx>
42 #include <o3tl/char16_t2wchar_t.hxx>
43 #include <comphelper/AccessibleImplementationHelper.hxx>
45 #include <com/sun/star/accessibility/XAccessibleText.hpp>
46 #include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
47 #include <com/sun/star/accessibility/XAccessibleImage.hpp>
48 #include <com/sun/star/accessibility/XAccessibleTable.hpp>
49 #include <com/sun/star/accessibility/XAccessibleExtendedComponent.hpp>
50 #include <com/sun/star/accessibility/XAccessibleAction.hpp>
51 #include <com/sun/star/accessibility/XAccessibleKeyBinding.hpp>
52 #include <com/sun/star/accessibility/XAccessibleHypertext.hpp>
53 #include <com/sun/star/accessibility/XAccessibleHyperlink.hpp>
54 #include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
55 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
56 #include <com/sun/star/accessibility/AccessibleRole.hpp>
57 #include <com/sun/star/accessibility/XAccessibleGroupPosition.hpp>
58 #include <com/sun/star/accessibility/XAccessibleValue.hpp>
59 #include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp>
60 #include <com/sun/star/style/LineSpacing.hpp>
61 #include <com/sun/star/style/TabStop.hpp>
62 #include <com/sun/star/container/XIndexReplace.hpp>
65 using namespace com::sun::star::uno;
66 using namespace com::sun::star::accessibility;
67 using namespace com::sun::star::accessibility::AccessibleStateType;
69 namespace {
71 enum class XInterfaceType {
72 XI_COMPONENT,
73 XI_TEXT,
74 XI_TABLE,
75 XI_TABLECELL,
76 XI_EDITABLETEXT,
77 XI_IMAGE,
78 XI_SELECTION,
79 XI_EXTENDEDCOMP,
80 XI_VALUE,
81 XI_KEYBINDING,
82 XI_ACTION,
83 XI_HYPERTEXT,
84 XI_HYPERLINK,
85 XI_ATTRIBUTE
88 template <class Interface>
89 bool queryXInterface(XAccessible* pXAcc, XInterface** ppXI)
91 if (!pXAcc)
92 return false;
94 Reference<XAccessibleContext> pRContext = pXAcc->getAccessibleContext();
95 if (!pRContext.is())
96 return false;
98 Reference<Interface> pRXI(pRContext, UNO_QUERY);
99 if (!pRXI.is())
100 return false;
102 *ppXI = pRXI.get();
103 return true;
106 // Since there's no specific XInterface for table cells, this
107 // method checks that the accessible's parent is a table
108 // (implements XAccessibleTable) and pXAcc's context implements
109 // XAccessibleComponent.
110 bool queryTableCell(XAccessible* pXAcc, XInterface** ppXI)
112 XInterface* pXInterface = nullptr;
114 const bool bSupportsInterface = queryXInterface<XAccessibleComponent>(pXAcc, &pXInterface);
115 if (!bSupportsInterface)
116 return false;
118 // check whether parent is a table (its accessible context implements XAccessibleTable)
119 XInterface* pParentXInterface = nullptr;
120 Reference<XAccessible> xParentAcc = pXAcc->getAccessibleContext()->getAccessibleParent();
121 const bool bParentIsTable = queryXInterface<XAccessibleTable>(xParentAcc.get(), &pParentXInterface);
123 if (!bParentIsTable)
124 return false;
126 *ppXI = pXInterface;
127 return true;
132 // IA2 states mapping, and name
133 // maintenance the consistency, change one array, change the three all
134 long const IA2_STATES[] =
136 IA2_STATE_ACTIVE, // = 0x1;
137 IA2_STATE_ARMED, // = 0x2;
138 IA2_STATE_DEFUNCT, // = 0x4;
139 IA2_STATE_EDITABLE, // = 0x8;
140 IA2_STATE_HORIZONTAL, // = 0x10;
141 IA2_STATE_ICONIFIED, // = 0x20;
142 IA2_STATE_INVALID_ENTRY, // = 0x80;
143 IA2_STATE_MANAGES_DESCENDANTS, // = 0x100;
144 IA2_STATE_MODAL, // = 0x200;
145 IA2_STATE_MULTI_LINE, // = 0x400;
146 IA2_STATE_OPAQUE, // = 0x800;
147 IA2_STATE_REQUIRED, // = 0x2000;
148 IA2_STATE_SELECTABLE_TEXT, // = 0x3000;
149 IA2_STATE_SINGLE_LINE, // = 0x4000;
150 IA2_STATE_STALE, // = 0x8000;
151 IA2_STATE_SUPPORTS_AUTOCOMPLETION, // = 0x10000;
152 IA2_STATE_TRANSIENT, //= 0x20000;
153 IA2_STATE_VERTICAL // = 0x40000;
156 sal_Int64 const UNO_STATES[] =
158 ACTIVE,
159 ARMED,
160 DEFUNC,
161 EDITABLE,
162 HORIZONTAL,
163 ICONIFIED,
164 -1, //IA2_STATE_INVALID_ENTRY
165 MANAGES_DESCENDANTS,
166 MODAL,
167 MULTI_LINE,
168 OPAQUE,
169 -1, //IA2_STATE_REQUIRED
170 -1, //IA2_STATE_SELECTABLE_TEXT
171 SINGLE_LINE,
172 STALE,
173 -1, //IA2_STATE_SUPPORTS_AUTOCOMPLETION
174 TRANSIENT, //IA2_STATE_TRANSIENT
175 VERTICAL
178 using namespace com::sun::star::accessibility::AccessibleRole;
181 AccObjectManagerAgent* CMAccessible::g_pAgent = nullptr;
183 CMAccessible::CMAccessible():
184 m_pszName(nullptr),
185 m_pszValue(nullptr),
186 m_pszActionDescription(nullptr),
187 m_iRole(0x00),
188 m_dState(0x00),
189 m_pIParent(nullptr),
190 m_dChildID(0x00),
191 m_dFocusChildID(UACC_NO_FOCUS),
192 m_hwnd(nullptr),
193 m_isDestroy(false)
195 CEnumVariant::Create(&m_pEnumVar);
196 m_containedObjects.clear();
199 CMAccessible::~CMAccessible()
201 SolarMutexGuard g;
203 if(m_pszName!=nullptr)
205 SysFreeString(std::exchange(m_pszName, nullptr));
207 if(m_pszValue!=nullptr)
209 SysFreeString(std::exchange(m_pszValue, nullptr));
212 if(m_pszActionDescription!=nullptr)
214 SysFreeString(std::exchange(m_pszActionDescription, nullptr));
217 if(m_pIParent)
219 m_pIParent->Release();
220 m_pIParent=nullptr;
222 m_pEnumVar->Release();
223 m_containedObjects.clear();
227 * Returns the Parent IAccessible interface pointer to AT.
228 * It should add reference, and the client should release the component.
229 * It should return E_FAIL when the parent point is null.
230 * @param ppdispParent [in,out] used to return the parent interface point.
231 * when the point is null, should return null.
232 * @return S_OK if successful and E_FAIL if the m_pIParent is NULL.
234 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accParent(IDispatch **ppdispParent)
236 SolarMutexGuard g;
238 try {
239 if (m_isDestroy) return S_FALSE;
240 // #CHECK#
241 if(ppdispParent == nullptr)
243 return E_INVALIDARG;
246 if(m_pIParent)
248 *ppdispParent = m_pIParent;
249 (*ppdispParent)->AddRef();
250 return S_OK;
252 else if(m_hwnd)
254 HRESULT hr = AccessibleObjectFromWindow(m_hwnd, OBJID_WINDOW, IID_IAccessible, reinterpret_cast<void**>(ppdispParent));
255 if (!SUCCEEDED(hr) || !*ppdispParent)
257 return S_FALSE;
259 return S_OK;
261 return S_FALSE;
263 } catch(...) { return E_FAIL; }
267 * Returns child count of current COM object.
268 * @param pcountChildren [in,out] used to return the children count.
269 * @return S_OK if successful.
271 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accChildCount(long *pcountChildren)
273 SolarMutexGuard g;
275 try {
276 if (m_isDestroy) return S_FALSE;
277 // #CHECK#
278 if(pcountChildren == nullptr)
280 return E_INVALIDARG;
283 if (!m_xAccessible.is())
284 return S_FALSE;
286 Reference<XAccessibleContext> const pRContext =
287 m_xAccessible->getAccessibleContext();
288 if( pRContext.is() )
290 sal_Int64 nChildCount = pRContext->getAccessibleChildCount();
291 if (nChildCount > std::numeric_limits<long>::max())
293 SAL_WARN("iacc2", "CMAccessible::get_accChildCount: Child count exceeds maximum long value, "
294 "returning max long.");
295 nChildCount = std::numeric_limits<long>::max();
298 *pcountChildren = nChildCount;
301 return S_OK;
303 } catch(...) { return E_FAIL; }
307 * Returns child interface pointer for AT according to input child ID.
308 * @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID,
309 * the child ID specify child index from 0 to children count, 0 stands for object self.
310 * @param ppdispChild, [in,out] use to return the child interface point.
311 * @return S_OK if successful and S_FALSE if failure.
313 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accChild(VARIANT varChild, IDispatch **ppdispChild)
315 SolarMutexGuard g;
317 try {
318 if (m_isDestroy) return S_FALSE;
319 // #CHECK#
320 if(ppdispChild == nullptr)
322 return E_INVALIDARG;
324 if(varChild.vt==VT_I4)
326 //get child interface pointer due to child ID
327 if(varChild.lVal==CHILDID_SELF)
329 AddRef();
330 *ppdispChild = this;
331 return S_OK;
333 *ppdispChild = GetChildInterface(varChild.lVal);
334 if((*ppdispChild) == nullptr)
335 return E_FAIL;
336 (*ppdispChild)->AddRef();
337 return S_OK;
339 return S_FALSE;
341 } catch(...) { return E_FAIL; }
345 * Returns the accessible name of the current COM object self or its one child to AT.
346 * @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID,
347 * the child ID specify child index from 0 to children count, 0 stands for object self.
348 * @param pszName, [in,out] use to return the name of the proper object.
349 * @return S_OK if successful and S_FALSE if failure.
351 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accName(VARIANT varChild, BSTR *pszName)
353 SolarMutexGuard g;
355 try {
356 if (m_isDestroy) return S_FALSE;
357 // #CHECK#
358 if(pszName == nullptr)
360 return E_INVALIDARG;
362 if(varChild.vt==VT_I4)
364 if(varChild.lVal==CHILDID_SELF)
366 SysFreeString(*pszName);
367 *pszName = SysAllocString(m_pszName);
368 return S_OK;
371 long lVal = varChild.lVal;
372 varChild.lVal = CHILDID_SELF;
373 IMAccessible *pChild = this->GetChildInterface(lVal);
374 if(!pChild)
375 return E_FAIL;
376 return pChild->get_accName(varChild,pszName);
378 return S_FALSE;
380 } catch(...) { return E_FAIL; }
384 * Returns the accessible value of the current COM object self or its one child to AT.
385 * @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID,
386 * the child ID specify child index from 0 to children count, 0 stands for object self.
387 * @param pszValue, [in,out] use to return the value of the proper object.
388 * @return S_OK if successful and S_FALSE if failure.
390 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accValue(VARIANT varChild, BSTR *pszValue)
392 SolarMutexGuard g;
394 try {
395 if (m_isDestroy) return S_FALSE;
396 // #CHECK#
397 if( pszValue == nullptr )
399 return E_INVALIDARG;
401 if( varChild.vt==VT_I4 )
403 if(varChild.lVal==CHILDID_SELF)
405 if(m_dState & STATE_SYSTEM_PROTECTED)
406 return E_ACCESSDENIED;
408 if ( m_pszValue !=nullptr && wcslen(m_pszValue) == 0 )
409 return S_OK;
411 SysFreeString(*pszValue);
412 *pszValue = SysAllocString(m_pszValue);
413 return S_OK;
416 long lVal = varChild.lVal;
417 varChild.lVal = CHILDID_SELF;
418 IMAccessible *pChild = this->GetChildInterface(lVal);
419 if(!pChild)
420 return E_FAIL;
421 return pChild->get_accValue(varChild,pszValue);
423 return S_FALSE;
425 } catch(...) { return E_FAIL; }
429 * Returns the accessible description of the current COM object self or its one child to AT.
430 * @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID,
431 * the child ID specify child index from 0 to children count, 0 stands for object self.
432 * @param pszDescription, [in,out] use to return the description of the proper object.
433 * @return S_OK if successful and E_FAIL if failure.
435 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accDescription(VARIANT varChild, BSTR *pszDescription)
437 SolarMutexGuard g;
439 try {
440 if (m_isDestroy) return S_FALSE;
441 // #CHECK#
442 if(pszDescription == nullptr)
444 return E_INVALIDARG;
446 if(varChild.vt==VT_I4)
448 if(varChild.lVal==CHILDID_SELF)
450 if (!m_xAccessible.is())
451 return S_FALSE;
453 Reference<XAccessibleContext> xContext = m_xAccessible->getAccessibleContext();
454 if (!xContext.is())
455 return S_FALSE;
457 const OUString sDescription = xContext->getAccessibleDescription();
458 SysFreeString(*pszDescription);
459 *pszDescription = SysAllocString(o3tl::toW(sDescription.getStr()));
460 return S_OK;
463 long lVal = varChild.lVal;
464 varChild.lVal = CHILDID_SELF;
465 IMAccessible *pChild = this->GetChildInterface(lVal);
466 if(!pChild)
467 return E_FAIL;
468 return pChild->get_accDescription(varChild,pszDescription);
470 return S_FALSE;
472 } catch(...) { return E_FAIL; }
476 * Returns the accessible role of the current COM object self or its one child to AT.
477 * @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID,
478 * the child ID specify child index from 0 to children count, 0 stands for object self.
479 * @param pvarRole, [in,out] use to return the role of the proper object.
480 * @return S_OK if successful and S_FALSE if failure.
482 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accRole(VARIANT varChild, VARIANT *pvarRole)
484 SolarMutexGuard g;
486 try {
487 if (m_isDestroy) return S_FALSE;
488 // #CHECK#
489 if(pvarRole == nullptr)
491 return E_INVALIDARG;
493 if(varChild.vt == VT_I4)
496 if(varChild.lVal == CHILDID_SELF)
498 VariantInit(pvarRole);
499 pvarRole->vt = VT_I4;
501 if (m_iRole < IA2_ROLE_CAPTION)
502 pvarRole->lVal = m_iRole;
503 else
504 pvarRole->lVal = ROLE_SYSTEM_CLIENT;
506 return S_OK;
510 long lVal = varChild.lVal;
511 varChild.lVal = CHILDID_SELF;
512 IMAccessible *pChild = this->GetChildInterface(lVal);
513 if(!pChild)
514 return E_FAIL;
515 return pChild->get_accRole(varChild,pvarRole);
517 return S_FALSE;
519 } catch(...) { return E_FAIL; }
523 * Returns the accessible state of the current COM object self or its one child to AT.
524 * @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID,
525 * the child ID specify child index from 0 to children count, 0 stands for object self.
526 * @param pvarState, [in,out] use to return the state of the proper object.
527 * @return S_OK if successful and S_FALSE if failure.
529 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accState(VARIANT varChild, VARIANT *pvarState)
531 SolarMutexGuard g;
533 try {
534 if (m_isDestroy) return S_FALSE;
535 // #CHECK#
536 if(pvarState == nullptr)
538 return E_INVALIDARG;
540 if(varChild.vt==VT_I4)
542 if(varChild.lVal == CHILDID_SELF)
544 if (m_xAccessible.is())
546 Reference<XAccessibleContext> const pContext =
547 m_xAccessible->getAccessibleContext();
548 if(pContext.is())
550 // add the STATE_SYSTEM_LINKED state
551 Reference< XAccessibleHypertext > pRHypertext(pContext,UNO_QUERY);
552 if(pRHypertext.is())
554 if( pRHypertext->getHyperLinkCount() > 0 )
555 m_dState |= STATE_SYSTEM_LINKED;
556 else
557 m_dState &= ~STATE_SYSTEM_LINKED;
559 else
560 m_dState &= ~STATE_SYSTEM_LINKED;
564 VariantInit(pvarState);
565 pvarState->vt = VT_I4;
566 pvarState->lVal = m_dState;
567 return S_OK;
570 long lVal = varChild.lVal;
571 varChild.lVal = CHILDID_SELF;
572 IMAccessible *pChild = this->GetChildInterface(lVal);
573 if(!pChild)
574 return E_FAIL;
575 return pChild->get_accState(varChild,pvarState);
577 return S_FALSE;
579 } catch(...) { return E_FAIL; }
583 * Returns the accessible helpString of the current COM object self or its one child to AT.
584 * @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID,
585 * the child ID specify child index from 0 to children count, 0 stands for object self.
586 * @param pszHelp, [in,out] use to return the helpString of the proper object.
587 * @return S_OK if successful and E_FAIL if failure.
589 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accHelp(VARIANT, BSTR *)
591 return E_NOTIMPL;
595 * Returns the accessible HelpTopic of the current COM object self or its one child to AT.
596 * @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID,
597 * the child ID specify child index from 0 to children count, 0 stands for object self.
598 * @param pszHelpFile, [in,out] use to return the HelpTopic of the proper object.
599 * @param pidTopic, use to return the HelpTopic ID of the proper object.
600 * @return S_OK if successful and E_FAIL if failure.
601 * Not implemented yet
603 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accHelpTopic(BSTR *, VARIANT, long *)
605 return E_NOTIMPL;
608 static bool GetMnemonicChar( const OUString& aStr, sal_Unicode* wStr)
610 for (sal_Int32 i = 0;; i += 2) {
611 i = aStr.indexOf('~', i);
612 if (i == -1 || i == aStr.getLength() - 1) {
613 return false;
615 auto c = aStr[i + 1];
616 if (c != '~') {
617 *wStr = c;
618 return true;
624 * Returns the accessible keyboard shortcut of the current COM object self or its one child to AT.
625 * @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID,
626 * the child ID specify child index from 0 to children count, 0 stands for object self.
627 * @param pszKeyboardShortcut, [in,out] use to return the kbshortcut of the proper object.
628 * @return S_OK if successful and E_FAIL if failure.
630 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut)
632 SolarMutexGuard g;
634 try {
636 if (m_isDestroy) return S_FALSE;
637 // #CHECK#
638 if(pszKeyboardShortcut == nullptr)
640 return E_INVALIDARG;
643 if(varChild.vt==VT_I4)
645 if(varChild.lVal == CHILDID_SELF)
647 if (m_xAccessible.is())
649 Reference<XAccessibleContext> const pRContext =
650 m_xAccessible->getAccessibleContext();
651 if( !pRContext.is() )
652 return S_FALSE;
654 Reference<XAccessibleAction> pRXI(pRContext,UNO_QUERY);
656 OUString wString;
658 if( pRXI.is() && pRXI->getAccessibleActionCount() >= 1)
660 Reference< XAccessibleKeyBinding > binding = pRXI->getAccessibleActionKeyBinding(0);
661 if( binding.is() )
663 long nCount = binding->getAccessibleKeyBindingCount();
664 if(nCount >= 1)
666 wString = comphelper::GetkeyBindingStrByXkeyBinding( binding->getAccessibleKeyBinding(0) );
670 if(wString.isEmpty())
672 Reference<XAccessibleRelationSet> pRrelationSet = pRContext->getAccessibleRelationSet();
673 if(!pRrelationSet.is())
675 return S_FALSE;
678 long nRelCount = pRrelationSet->getRelationCount();
680 // Modified by Steve Yin, for SODC_1552
681 if( /*nRelCount <= 0 &&*/ m_iRole == ROLE_SYSTEM_TEXT )
683 VARIANT varParentRole;
684 VariantInit( &varParentRole );
686 if (m_pIParent
687 && SUCCEEDED(m_pIParent->get_accRole(varChild, &varParentRole))
688 && varParentRole.lVal == ROLE_SYSTEM_COMBOBOX) // edit in comboBox
690 m_pIParent->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
691 return S_OK;
695 AccessibleRelation *paccRelation = nullptr;
696 AccessibleRelation accRelation;
697 for(int i=0; i<nRelCount ; i++)
699 if( pRrelationSet->getRelation(i).RelationType == 6 )
701 accRelation = pRrelationSet->getRelation(i);
702 paccRelation = &accRelation;
706 if(paccRelation == nullptr)
707 return S_FALSE;
709 Sequence< Reference< XInterface > > xTargets = paccRelation->TargetSet;
710 Reference<XInterface> pRAcc = xTargets[0];
712 XAccessible* pXAcc = static_cast<XAccessible*>(pRAcc.get());
714 Reference<XAccessibleContext> pRLebelContext = pXAcc->getAccessibleContext();
715 if(!pRLebelContext.is())
716 return S_FALSE;
718 pRrelationSet = pRLebelContext->getAccessibleRelationSet();
719 nRelCount = pRrelationSet->getRelationCount();
721 paccRelation = nullptr;
722 for(int j=0; j<nRelCount ; j++)
724 if( pRrelationSet->getRelation(j).RelationType == 5 )
726 accRelation = pRrelationSet->getRelation(j);
727 paccRelation = &accRelation;
731 if(paccRelation)
733 xTargets = paccRelation->TargetSet;
734 pRAcc = xTargets[0];
735 if (m_xAccessible.get() != static_cast<XAccessible*>(pRAcc.get()))
736 return S_FALSE;
739 Reference<XAccessibleExtendedComponent> pRXIE(pRLebelContext,UNO_QUERY);
740 if(!pRXIE.is())
741 return S_FALSE;
743 OUString ouStr = pRXIE->getTitledBorderText();
744 sal_Unicode key;
745 if(GetMnemonicChar(ouStr, &key))
747 wString = "Alt+" + OUStringChar(key);
749 else
750 return S_FALSE;
753 SysFreeString(*pszKeyboardShortcut);
754 *pszKeyboardShortcut = SysAllocString(o3tl::toW(wString.getStr()));
756 return S_OK;
758 else
760 return S_FALSE;
764 long lVal = varChild.lVal;
765 varChild.lVal = CHILDID_SELF;
766 IMAccessible *pChild = this->GetChildInterface(lVal);
767 if(!pChild)
768 return E_FAIL;
770 return pChild->get_accKeyboardShortcut(varChild,pszKeyboardShortcut);
772 return S_FALSE;
774 } catch(...) { return E_FAIL; }
778 * Returns the current focused child to AT.
779 * @param pvarChild, [in,out] vt member of pvarChild must be VT_I4,and lVal member stores the child ID,
780 * the child ID specify child index from 0 to children count, 0 stands for object self.
781 * @return S_OK if successful and E_FAIL if failure.
783 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accFocus(VARIANT *pvarChild)
785 SolarMutexGuard g;
787 try {
788 if (m_isDestroy) return S_FALSE;
789 // #CHECK#
790 if(pvarChild == nullptr)
792 return E_INVALIDARG;
794 if( m_dFocusChildID==UACC_NO_FOCUS )
796 pvarChild->vt = VT_EMPTY;//no focus on the object and its children
797 return S_OK;
799 //if the descendant of current object has focus indicated by m_dFocusChildID, return the IDispatch of this focused object
800 else
802 IMAccessible* pIMAcc = nullptr;
803 g_pAgent->GetIAccessibleFromResID(m_dFocusChildID,&pIMAcc);
804 if (pIMAcc == nullptr)
806 return E_FAIL;
808 pIMAcc->AddRef();
809 pvarChild->vt = VT_DISPATCH;
810 pvarChild->pdispVal = pIMAcc;
813 return S_OK;
815 } catch(...) { return E_FAIL; }
819 * Returns the selection of the current COM object to AT.
820 * @param pvarChildren,[in,out]
821 * if selection num is 0,return VT_EMPTY for vt,
822 * if selection num is 1,return VT_I4 for vt,and child index for lVal
823 * if selection num >1,return VT_UNKNOWN for vt, and IEnumVariant* for punkVal
824 * @return S_OK if successful and S_FALSE if failure.
826 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_accSelection(VARIANT *pvarChildren)
828 SolarMutexGuard g;
830 try {
831 if (m_isDestroy) return S_FALSE;
832 // #CHECK#
833 if(pvarChildren == nullptr)
835 return E_INVALIDARG;
837 switch(m_pEnumVar->GetCountOfElements())
839 case 0:
840 pvarChildren->vt = VT_EMPTY;
841 break;
842 case 1:
843 VARIANT varTmp[1];
844 ULONG count;
845 VariantInit(&varTmp[0]);
846 m_pEnumVar->Next(1,varTmp,&count);
847 if(count!=1)
848 return S_FALSE;
849 pvarChildren->vt = VT_DISPATCH;
850 pvarChildren->pdispVal = varTmp[0].pdispVal;
851 pvarChildren->pdispVal->AddRef();
852 VariantClear(&varTmp[0]);
853 m_pEnumVar->Reset();
854 break;
855 default:
856 pvarChildren->vt = VT_UNKNOWN;
857 IEnumVARIANT* pClone;
858 m_pEnumVar->Clone(&pClone);
859 pClone->Reset();
860 pvarChildren->punkVal = pClone;
861 break;
863 return S_OK;
865 } catch(...) { return E_FAIL; }
869 * Returns the location of the current COM object self or its one child to AT.
870 * @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID,
871 * the child ID specify child index from 0 to children count, 0 stands for object self.
872 * @param pxLeft, [in,out] use to return the x-coordination of the proper object.
873 * @param pyTop, [in,out] use to return the y-coordination of the proper object.
874 * @param pcxWidth, [in,out] use to return the x-coordination width of the proper object.
875 * @param pcyHeight, [in,out] use to return the y-coordination height of the proper object.
876 * @return S_OK if successful and S_FALSE if failure.
878 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varChild)
880 SolarMutexGuard g;
882 try {
883 if (m_isDestroy) return S_FALSE;
884 // #CHECK#
885 if(pxLeft == nullptr || pyTop == nullptr || pcxWidth == nullptr || pcyHeight == nullptr)
887 return E_INVALIDARG;
890 if(varChild.vt==VT_I4)
892 if(varChild.lVal==CHILDID_SELF)
894 if (!m_xAccessible.is())
895 return S_FALSE;
897 Reference<XAccessibleContext> const pRContext =
898 m_xAccessible->getAccessibleContext();
899 if( !pRContext.is() )
900 return S_FALSE;
901 Reference< XAccessibleComponent > pRComponent(pRContext,UNO_QUERY);
902 if( !pRComponent.is() )
903 return S_FALSE;
905 css::awt::Point pCPoint = pRComponent->getLocationOnScreen();
906 css::awt::Size pCSize = pRComponent->getSize();
907 *pxLeft = pCPoint.X;
908 *pyTop = pCPoint.Y;
909 *pcxWidth = pCSize.Width;
910 *pcyHeight = pCSize.Height;
911 return S_OK;
914 return S_FALSE;
916 } catch(...) { return E_FAIL; }
920 * Returns the current focused child to AT.
921 * @param navDir, the direction flag of the navigation.
922 * @param varStart, the start child id of this navigation action.
923 * @param pvarEndUpAt, [in,out] the end up child of this navigation action.
924 * @return S_OK if successful and E_FAIL if failure.
926 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEndUpAt)
928 SolarMutexGuard g;
930 try {
931 if (m_isDestroy) return S_FALSE;
932 // #CHECK#
933 if(pvarEndUpAt == nullptr)
935 return E_INVALIDARG;
937 HRESULT ret = E_FAIL;
938 switch (navDir)
940 case NAVDIR_FIRSTCHILD:
941 ret = GetFirstChild(varStart,pvarEndUpAt);
942 break;
943 case NAVDIR_LASTCHILD:
944 ret = GetLastChild(varStart,pvarEndUpAt);
945 break;
946 case NAVDIR_NEXT:
947 ret = GetNextSibling(varStart,pvarEndUpAt);
948 break;
949 case NAVDIR_PREVIOUS:
950 ret = GetPreSibling(varStart,pvarEndUpAt);
951 break;
952 case NAVDIR_DOWN://do not implement temporarily
953 break;
954 case NAVDIR_UP://do not implement temporarily
955 break;
956 case NAVDIR_LEFT://do not implement temporarily
957 break;
958 case NAVDIR_RIGHT://do not implement temporarily
959 break;
960 default:
961 break;
963 return ret;
965 } catch(...) { return E_FAIL; }
968 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::accHitTest(long xLeft, long yTop, VARIANT *pvarChild)
970 SolarMutexGuard g;
972 try {
973 if (m_isDestroy) return S_FALSE;
974 // #CHECK#
975 if(pvarChild == nullptr)
977 return E_INVALIDARG;
979 long x, y, w, h;
980 VARIANT varSelf;
981 VariantInit(&varSelf);
982 varSelf.vt = VT_I4;
983 varSelf.lVal = CHILDID_SELF;
984 accLocation(&x,&y,&w,&h,varSelf);
985 if( (x < xLeft && (x + w) >xLeft) && (y < yTop && (y + h) >yTop) )
987 sal_Int64 i, nCount;
988 pvarChild->vt = VT_EMPTY;
989 Reference< XAccessibleContext > pRContext = GetContextByXAcc(m_xAccessible.get());
990 nCount = pRContext->getAccessibleChildCount();
991 if(nCount > 256)
992 return E_FAIL;
993 IMAccessible* child = nullptr;
994 for( i = 0; i<nCount; i++)
997 child = GetChildInterface(i + 1);
998 if(child && child->accHitTest(xLeft,yTop,pvarChild) == S_OK)
999 break;
1002 if(pvarChild->vt == VT_DISPATCH)
1003 return S_OK;
1005 if( i < nCount)
1007 pvarChild->vt = VT_DISPATCH;
1008 pvarChild->pdispVal = child;
1009 child->AddRef();
1011 else
1013 pvarChild->vt = VT_I4;
1014 pvarChild->lVal = CHILDID_SELF;
1016 return S_OK;
1018 return S_FALSE;
1020 } catch(...) { return E_FAIL; }
1024 * Get The other Interface from CMAccessible.
1025 * @param guidService, must be IID_IAccessible here.
1026 * @param riid, the IID interface .
1027 * @return S_OK if successful and S_FALSE if failure.
1029 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::QueryService(REFGUID guidService, REFIID riid, void** ppvObject)
1031 if( InlineIsEqualGUID(guidService, IID_IAccessible) )
1032 return QueryInterface(riid, ppvObject);
1033 return S_FALSE;
1037 * No longer supported according to IAccessible doc.
1038 * Servers should return E_NOTIMPL
1040 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::put_accName(VARIANT, BSTR)
1042 return E_NOTIMPL;
1046 * Set the accessible value of the current COM object self or its one child from UNO.
1047 * @param varChild, vt member of varChild must be VT_I4,and lVal member stores the child ID,
1048 * the child ID specify child index from 0 to children count, 0 stands for object self.
1049 * @param szValue, the value used to set the value of the proper object.
1050 * @return S_OK if successful and E_FAIL if failure.
1052 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::put_accValue(VARIANT varChild, BSTR szValue)
1054 SolarMutexGuard g;
1056 try {
1057 if (m_isDestroy) return S_FALSE;
1058 if(varChild.vt==VT_I4)
1060 if(varChild.lVal==CHILDID_SELF)
1062 SysFreeString(m_pszValue);
1063 m_pszValue=SysAllocString(szValue);
1064 return S_OK;
1067 long lVal = varChild.lVal;
1068 varChild.lVal = CHILDID_SELF;
1069 IMAccessible *pChild = this->GetChildInterface(lVal);
1070 if(!pChild)
1071 return E_FAIL;
1072 return pChild->put_accValue(varChild,szValue);
1074 return E_FAIL;
1076 } catch(...) { return E_FAIL; }
1080 * Set the accessible name of the current COM object self from UNO.
1081 * @param pszName, the name value used to set the name of the current object.
1082 * @return S_OK if successful and E_FAIL if failure.
1084 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccName(const OLECHAR __RPC_FAR *pszName)
1086 // internal IMAccessible - no mutex meeded
1088 try {
1089 if (m_isDestroy) return S_FALSE;
1090 // #CHECK#
1091 if(pszName == nullptr)
1093 return E_INVALIDARG;
1096 SysFreeString(m_pszName);
1097 m_pszName = SysAllocString(pszName);
1098 if(m_pszName==nullptr)
1099 return E_FAIL;
1100 return S_OK;
1102 } catch(...) { return E_FAIL; }
1106 * Set the accessible role of the current COM object self from UNO.
1107 * @param pRole, the role value used to set the role of the current object.
1108 * @return S_OK if successful and E_FAIL if failure.
1110 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccRole(unsigned short pRole)
1112 // internal IMAccessible - no mutex meeded
1114 m_iRole = pRole;
1115 return S_OK;
1119 * Add one state into the current state set for the current COM object from UNO.
1120 * @param pXSate, the state used to set the name of the current object.
1121 * @return S_OK if successful and E_FAIL if failure.
1123 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::DecreaseState(DWORD pXSate)
1125 // internal IMAccessible - no mutex meeded
1127 m_dState &= (~pXSate);
1128 return S_OK;
1132 * Delete one state into the current state set for the current COM object from UNO.
1133 * @param pXSate, the state used to set the name of the current object.
1134 * @return S_OK if successful and E_FAIL if failure.
1136 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::IncreaseState(DWORD pXSate)
1138 // internal IMAccessible - no mutex meeded
1140 m_dState |= pXSate;
1141 return S_OK;
1145 * Set state into the current state set for the current COM object from UNO.
1146 * @param pXSate, the state used to set the name of the current object.
1147 * @return S_OK if successful and E_FAIL if failure.
1149 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::SetState(DWORD pXSate)
1151 // internal IMAccessible - no mutex meeded
1153 m_dState = pXSate;
1154 return S_OK;
1158 * Set the accessible value of the current COM object self from UNO.
1159 * @param pszAccValue, the name used to set the value of the current object.
1160 * @return S_OK if successful and E_FAIL if failure.
1162 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccValue(const OLECHAR __RPC_FAR *pszAccValue)
1164 // internal IMAccessible - no mutex meeded
1166 try {
1167 if (m_isDestroy) return S_FALSE;
1168 // #CHECK#
1169 if(pszAccValue == nullptr)
1171 return E_INVALIDARG;
1173 SysFreeString(m_pszValue);
1174 m_pszValue = SysAllocString(pszAccValue);
1175 if(m_pszValue==nullptr)
1176 return E_FAIL;
1177 return S_OK;
1179 } catch(...) { return E_FAIL; }
1183 * Set the HWND value of the current COM object self from UNO. It should set the parent IAccessible
1184 * Object through the method AccessibleObjectFromWindow(...).
1185 * @param hwnd, the HWND used to set the value of the current object.
1186 * @return S_OK if successful and E_FAIL if failure.
1188 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccWindowHandle(HWND hwnd)
1190 // internal IMAccessible - no mutex meeded
1192 try {
1193 if (m_isDestroy) return S_FALSE;
1194 m_hwnd = hwnd;
1195 return S_OK;
1197 } catch(...) { return E_FAIL; }
1201 * Set accessible focus by specifying child ID
1202 * @param dChildID, the child id identifies the focus child.
1203 * @return S_OK if successful and E_FAIL if failure.
1205 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccFocus(long dChildID)
1207 // internal IMAccessible - no mutex meeded
1209 try {
1210 if (m_isDestroy) return S_FALSE;
1212 if(dChildID==CHILDID_SELF)
1214 if(m_pIParent)
1216 m_pIParent->Put_XAccFocus(m_dChildID);
1219 else
1221 m_dFocusChildID = dChildID;
1222 //traverse all ancestors to set the focused child ID so that when the get_accFocus is called on
1223 //any of the ancestors, this id can be used to get the IAccessible of focused object.
1224 if(m_pIParent)
1226 m_pIParent->Put_XAccFocus(dChildID);
1229 return S_OK;
1231 } catch(...) { return E_FAIL; }
1235 * Set accessible parent object for the current COM object if
1236 * the current object is a child of some COM object
1237 * @param pIParent, the parent of the current object.
1238 * @return S_OK if successful and E_FAIL if failure.
1240 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccParent(IMAccessible __RPC_FAR *pIParent)
1242 // internal IMAccessible - no mutex meeded
1244 this->m_pIParent = pIParent;
1246 if(pIParent)
1247 m_pIParent->AddRef();
1249 return S_OK;
1253 * Set unique child id to COM
1254 * @param dChildID, the id of the current object.
1255 * @return S_OK if successful and E_FAIL if failure.
1257 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccChildID(long dChildID)
1259 // internal IMAccessible - no mutex meeded
1261 this->m_dChildID = dChildID;
1262 return S_OK;
1266 * Set AccObjectManagerAgent object pointer to COM
1267 * @param pAgent, the AccObjectManagerAgent point.
1268 * @return S_OK if successful and E_FAIL if failure.
1270 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_XAccAgent(hyper pAgent)
1272 // internal IMAccessible - no mutex meeded
1274 g_pAgent = reinterpret_cast<AccObjectManagerAgent*>(pAgent);
1275 return S_OK;
1279 * When a UNO control disposing, it disposes its listeners,
1280 * then notify AccObject in bridge management, then notify
1281 * COM that the XAccessible is invalid, so set m_xAccessible as NULL
1282 * @return S_OK if successful and E_FAIL if failure.
1284 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::NotifyDestroy()
1286 // internal IMAccessible - no mutex meeded
1288 m_isDestroy = true;
1289 m_xAccessible.clear();
1290 return S_OK;
1294 *private methods that help implement public functions
1298 * Return child interface pointer by child ID,note: need to call AddRef()
1299 * @param lChildID, specify child index,which AT(such as Inspect32) gives.
1300 * @return IMAccessible*, pointer to the corresponding child object.
1302 IMAccessible* CMAccessible::GetChildInterface(long dChildID)//for test
1304 if(dChildID<0)
1306 if(g_pAgent)
1308 IMAccessible* pIMAcc = nullptr;
1309 g_pAgent->GetIAccessibleFromResID(dChildID,&pIMAcc);
1310 return pIMAcc;
1312 return nullptr;
1314 else
1316 if (!m_xAccessible.is())
1317 return nullptr;
1319 Reference<XAccessibleContext> const pRContext =
1320 m_xAccessible->getAccessibleContext();
1321 if( !pRContext.is() )
1322 return nullptr;
1324 if(dChildID<1 || dChildID>pRContext->getAccessibleChildCount())
1325 return nullptr;
1327 IAccessible* pChild = nullptr;
1328 Reference< XAccessible > pXChild = pRContext->getAccessibleChild(dChildID-1);
1329 bool isGet = get_IAccessibleFromXAccessible(pXChild.get(), &pChild);
1331 if(!isGet)
1333 g_pAgent->InsertAccObj(pXChild.get(), m_xAccessible.get(), m_hwnd);
1334 isGet = get_IAccessibleFromXAccessible(pXChild.get(), &pChild);
1337 if(isGet)
1339 IMAccessible* pIMAcc = static_cast<IMAccessible*>(pChild);
1340 return pIMAcc;
1344 return nullptr;
1348 * for descendantmanager circumstance,provide child interface when navigate
1349 * @param varCur, the current child.
1350 * @param flags, the navigation direction.
1351 * @return IMAccessible*, the child of the end up node.
1353 IMAccessible* CMAccessible::GetNavigateChildForDM(VARIANT varCur, short flags)
1356 XAccessibleContext* pXContext = GetContextByXAcc(m_xAccessible.get());
1357 if(pXContext==nullptr)
1359 return nullptr;
1362 sal_Int64 count = pXContext->getAccessibleChildCount();
1363 if(count<1)
1365 return nullptr;
1368 IMAccessible* pCurChild = nullptr;
1369 union {
1370 XAccessible* pChildXAcc;
1371 hyper nHyper = 0;
1373 Reference<XAccessible> pRChildXAcc;
1374 XAccessibleContext* pChildContext = nullptr;
1375 sal_Int64 index = 0, delta = 0;
1376 switch(flags)
1378 case DM_FIRSTCHILD:
1379 pRChildXAcc = pXContext->getAccessibleChild(0);
1380 break;
1381 case DM_LASTCHILD:
1382 pRChildXAcc = pXContext->getAccessibleChild(count-1);
1383 break;
1384 case DM_NEXTCHILD:
1385 case DM_PREVCHILD:
1386 pCurChild = GetChildInterface(varCur.lVal);
1387 if(pCurChild==nullptr)
1389 return nullptr;
1391 pCurChild->GetUNOInterface(&nHyper);
1392 if(pChildXAcc==nullptr)
1394 return nullptr;
1396 pChildContext = GetContextByXAcc(pChildXAcc);
1397 if(pChildContext == nullptr)
1399 return nullptr;
1401 delta = (flags==DM_NEXTCHILD)?1:-1;
1402 //currently, getAccessibleIndexInParent is error in UNO for
1403 //some kind of List,such as ValueSet, the index will be less 1 than
1404 //what should be, need to fix UNO code
1405 index = pChildContext->getAccessibleIndexInParent()+delta;
1406 if((index>=0)&&(index<=count-1))
1408 pRChildXAcc = pXContext->getAccessibleChild(index);
1410 break;
1411 default:
1412 break;
1415 if(!pRChildXAcc.is())
1417 return nullptr;
1419 pChildXAcc = pRChildXAcc.get();
1420 g_pAgent->InsertAccObj(pChildXAcc, m_xAccessible.get());
1421 return g_pAgent->GetIMAccByXAcc(pChildXAcc);
1425 *the following 4 private methods are for accNavigate implementation
1429 * Return first child for parent container, process differently according
1430 * to whether it is descendant manage
1431 * @param varStart, the start child id of this navigation action.
1432 * @param pvarEndUpAt, [in,out] the end up child of this navigation action.
1433 * @return S_OK if successful and E_FAIL if failure.
1435 HRESULT CMAccessible::GetFirstChild(VARIANT varStart,VARIANT* pvarEndUpAt)
1438 try {
1439 if (m_isDestroy) return S_FALSE;
1440 // #CHECK#
1441 if(pvarEndUpAt == nullptr)
1443 return E_INVALIDARG;
1445 if(varStart.vt != VT_I4)
1447 pvarEndUpAt->vt = VT_EMPTY;
1448 return E_INVALIDARG;
1451 pvarEndUpAt->pdispVal = GetNavigateChildForDM(varStart, DM_FIRSTCHILD);
1452 if(pvarEndUpAt->pdispVal)
1454 pvarEndUpAt->pdispVal->AddRef();
1455 pvarEndUpAt->vt = VT_DISPATCH;
1456 return S_OK;
1459 pvarEndUpAt->vt = VT_EMPTY;
1460 return E_FAIL;
1462 } catch(...) { return E_FAIL; }
1466 * Return last child for parent container, process differently according
1467 * to whether it is descendant manage
1468 * @param varStart, the start child id of this navigation action.
1469 * @param pvarEndUpAt, [in,out] the end up child of this navigation action.
1470 * @return S_OK if successful and E_FAIL if failure.
1472 HRESULT CMAccessible::GetLastChild(VARIANT varStart,VARIANT* pvarEndUpAt)
1475 try {
1476 if (m_isDestroy) return S_FALSE;
1477 // #CHECK#
1478 if(pvarEndUpAt == nullptr)
1480 return E_INVALIDARG;
1482 if(varStart.vt != VT_I4)
1484 pvarEndUpAt->vt = VT_EMPTY;
1485 return E_INVALIDARG;
1488 pvarEndUpAt->pdispVal = GetNavigateChildForDM(varStart, DM_LASTCHILD);
1489 if(pvarEndUpAt->pdispVal)
1491 pvarEndUpAt->pdispVal->AddRef();
1492 pvarEndUpAt->vt = VT_DISPATCH;
1493 return S_OK;
1495 pvarEndUpAt->vt = VT_EMPTY;
1496 return E_FAIL;
1498 } catch(...) { return E_FAIL; }
1502 * The method GetNextSibling is general, whatever it is descendant manage or not
1503 * Get the next sibling object.
1504 * @param varStart, the start child id of this navigation action.
1505 * @param pvarEndUpAt, [in,out] the end up child of this navigation action.
1506 * @return S_OK if successful and E_FAIL if failure.
1508 HRESULT CMAccessible::GetNextSibling(VARIANT varStart,VARIANT* pvarEndUpAt)
1511 try {
1512 if (m_isDestroy) return S_FALSE;
1513 if(varStart.vt != VT_I4)
1515 pvarEndUpAt->vt = VT_EMPTY;
1516 return E_INVALIDARG;
1519 Reference<XAccessibleContext> const pRContext =
1520 GetContextByXAcc(m_xAccessible.get());
1521 if(pRContext.is())
1523 varStart.iVal = sal_Int16(pRContext->getAccessibleIndexInParent() + 2);
1524 if(m_pIParent)
1525 if( m_pIParent->get_accChild(varStart,&pvarEndUpAt->pdispVal) == S_OK)
1527 pvarEndUpAt->vt = VT_DISPATCH;
1528 return S_OK;
1531 pvarEndUpAt->vt = VT_EMPTY;
1532 return E_FAIL;
1534 } catch(...) { return E_FAIL; }
1538 *the method GetPreSibling is general, whatever it is descendant manage or not
1539 * @param varStart, the start child id of this navigation action.
1540 * @param pvarEndUpAt, [in,out] the end up child of this navigation action.
1541 * @return S_OK if successful and E_FAIL if failure.
1543 HRESULT CMAccessible::GetPreSibling(VARIANT varStart,VARIANT* pvarEndUpAt)
1546 try {
1547 if (m_isDestroy) return S_FALSE;
1548 // #CHECK#
1549 if(pvarEndUpAt == nullptr)
1551 return E_INVALIDARG;
1553 if(varStart.vt != VT_I4)
1555 pvarEndUpAt->vt = VT_EMPTY;
1556 return E_INVALIDARG;
1559 Reference<XAccessibleContext> const pRContext =
1560 GetContextByXAcc(m_xAccessible.get());
1561 if(pRContext.is())
1563 varStart.iVal = sal_Int16(pRContext->getAccessibleIndexInParent());
1564 if(m_pIParent && varStart.iVal > 0)
1565 if( m_pIParent->get_accChild(varStart,&pvarEndUpAt->pdispVal) == S_OK)
1567 pvarEndUpAt->vt = VT_DISPATCH;
1568 return S_OK;
1571 pvarEndUpAt->vt = VT_EMPTY;
1572 return E_FAIL;
1574 } catch(...) { return E_FAIL; }
1578 * For IAccessible2 implementation methods
1580 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_nRelations( long __RPC_FAR *nRelations)
1582 SolarMutexGuard g;
1584 try {
1585 if (m_isDestroy) return S_FALSE;
1587 // #CHECK#
1588 if(nRelations == nullptr)
1590 return E_INVALIDARG;
1593 *nRelations = 0;
1595 if (!m_xContext.is())
1596 return E_FAIL;
1597 Reference<XAccessibleRelationSet> pRrelationSet =
1598 m_xContext->getAccessibleRelationSet();
1599 if(!pRrelationSet.is())
1601 *nRelations = 0;
1602 return S_OK;
1605 *nRelations = pRrelationSet->getRelationCount();
1606 return S_OK;
1608 } catch(...) { return E_FAIL; }
1611 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_relation( long relationIndex, IAccessibleRelation __RPC_FAR *__RPC_FAR *relation)
1613 SolarMutexGuard g;
1615 try {
1616 if (m_isDestroy) return S_FALSE;
1617 // #CHECK#
1618 if(relation == nullptr)
1620 return E_INVALIDARG;
1623 if (!m_xContext.is())
1624 return E_FAIL;
1627 long nMax = 0;
1628 get_nRelations(&nMax);
1630 *relation = static_cast<IAccessibleRelation*>(::CoTaskMemAlloc(sizeof(IAccessibleRelation)));
1632 // #CHECK Memory Allocation#
1633 if(*relation == nullptr)
1635 return E_FAIL;
1638 if( relationIndex < nMax )
1640 Reference<XAccessibleRelationSet> const pRrelationSet =
1641 m_xContext->getAccessibleRelationSet();
1642 if(!pRrelationSet.is())
1645 return E_FAIL;
1648 IAccessibleRelation* pRelation = nullptr;
1649 HRESULT hr = createInstance<CAccRelation>(IID_IAccessibleRelation,
1650 &pRelation);
1651 if(SUCCEEDED(hr))
1653 IUNOXWrapper* wrapper = nullptr;
1654 hr = pRelation->QueryInterface(IID_IUNOXWrapper, reinterpret_cast<void**>(&wrapper));
1655 if(SUCCEEDED(hr))
1657 AccessibleRelation accRelation = pRrelationSet->getRelation(relationIndex);
1658 wrapper->put_XSubInterface(
1659 reinterpret_cast<hyper>(&accRelation));
1660 wrapper->Release();
1661 *relation = pRelation;
1662 return S_OK;
1668 return E_FAIL;
1670 } catch(...) { return E_FAIL; }
1673 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_relations( long, IAccessibleRelation __RPC_FAR *__RPC_FAR *relation, long __RPC_FAR *nRelations)
1675 SolarMutexGuard g;
1677 try {
1678 if (m_isDestroy) return S_FALSE;
1680 // #CHECK#
1681 if(relation == nullptr || nRelations == nullptr)
1683 return E_INVALIDARG;
1685 // #CHECK XInterface#
1687 if (!m_xContext.is())
1688 return E_FAIL;
1690 Reference<XAccessibleRelationSet> const pRrelationSet =
1691 m_xContext->getAccessibleRelationSet();
1692 if(!pRrelationSet.is())
1694 *nRelations = 0;
1695 return S_OK;
1698 long nCount = pRrelationSet->getRelationCount();
1700 *relation = static_cast<IAccessibleRelation*>(::CoTaskMemAlloc(nCount*sizeof(IAccessibleRelation)));
1702 // #CHECK Memory Allocation#
1703 if(*relation == nullptr)
1705 return E_FAIL;
1708 for(int i=0; i<nCount ; i++)
1710 IAccessibleRelation* pRelation = nullptr;
1711 HRESULT hr = createInstance<CAccRelation>(IID_IAccessibleRelation,
1712 &pRelation);
1713 if(SUCCEEDED(hr))
1715 IUNOXWrapper* wrapper = nullptr;
1716 hr = pRelation->QueryInterface(IID_IUNOXWrapper, reinterpret_cast<void**>(&wrapper));
1717 if(SUCCEEDED(hr))
1719 AccessibleRelation accRelation = pRrelationSet->getRelation(i);
1720 wrapper->put_XSubInterface(
1721 reinterpret_cast<hyper>(&accRelation));
1722 wrapper->Release();
1724 relation[i] = pRelation;
1728 *nRelations = nCount;
1729 return S_OK;
1731 } catch(...) { return E_FAIL; }
1734 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::role(long __RPC_FAR *role)
1736 SolarMutexGuard g;
1738 try {
1740 (*role) = m_iRole;
1742 return S_OK;
1744 } catch(...) { return E_FAIL; }
1748 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_nActions(long __RPC_FAR *nActions)
1750 SolarMutexGuard g;
1754 if (m_isDestroy) return S_FALSE;
1755 // #CHECK#
1756 if(nActions == nullptr)
1758 return E_INVALIDARG;
1760 *nActions = 0;
1761 IAccessibleAction* pAcc = nullptr;
1762 HRESULT hr = QueryInterface(IID_IAccessibleAction, reinterpret_cast<void**>(&pAcc));
1763 if( hr == S_OK )
1765 pAcc->nActions(nActions);
1766 pAcc->Release();
1769 return S_OK;
1771 catch(...)
1773 *nActions = 0;
1774 return S_OK;
1779 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::scrollToPoint(enum IA2CoordinateType, long, long)
1781 return E_NOTIMPL;
1784 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::scrollTo(enum IA2ScrollType)
1786 return E_NOTIMPL;
1789 static XAccessible* getTheParentOfMember(XAccessible* pXAcc)
1791 // #CHECK#
1792 if(pXAcc == nullptr)
1794 return nullptr;
1796 Reference<XAccessibleContext> pRContext = pXAcc->getAccessibleContext();
1797 Reference<XAccessibleRelationSet> pRrelationSet = pRContext->getAccessibleRelationSet();
1798 sal_Int32 nRelations = pRrelationSet->getRelationCount();
1799 for(sal_Int32 i=0 ; i<nRelations ; i++)
1801 AccessibleRelation accRelation = pRrelationSet->getRelation(i);
1802 if(accRelation.RelationType == 7)
1804 Sequence< Reference< XInterface > > xTargets = accRelation.TargetSet;
1805 return static_cast<XAccessible*>(xTargets[0].get());
1808 return nullptr;
1811 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_groupPosition(long __RPC_FAR *groupLevel,long __RPC_FAR *similarItemsInGroup,long __RPC_FAR *positionInGroup)
1813 SolarMutexGuard g;
1815 try {
1816 if (m_isDestroy) return S_FALSE;
1817 // #CHECK#
1818 if(groupLevel == nullptr || similarItemsInGroup == nullptr || positionInGroup == nullptr)
1820 return E_INVALIDARG;
1823 if (!m_xAccessible.is())
1824 return E_FAIL;
1826 Reference<XAccessibleContext> const pRContext =
1827 m_xAccessible->getAccessibleContext();
1828 if(!pRContext.is())
1829 return E_FAIL;
1830 long Role = pRContext->getAccessibleRole();
1832 *groupLevel = 0;
1833 *similarItemsInGroup = 0;
1834 *positionInGroup = 0;
1836 if (Role != AccessibleRole::DOCUMENT && Role != AccessibleRole::DOCUMENT_PRESENTATION &&
1837 Role != AccessibleRole::DOCUMENT_SPREADSHEET && Role != AccessibleRole::DOCUMENT_TEXT)
1839 Reference< XAccessibleGroupPosition > xGroupPosition( pRContext, UNO_QUERY );
1840 if ( xGroupPosition.is() )
1842 Sequence< sal_Int32 > rSeq = xGroupPosition->getGroupPosition( Any( pRContext ) );
1843 if (rSeq.getLength() >= 3)
1845 *groupLevel = rSeq[0];
1846 *similarItemsInGroup = rSeq[1];
1847 *positionInGroup = rSeq[2];
1848 return S_OK;
1850 return S_OK;
1854 Reference< XAccessible> pParentAcc = pRContext->getAccessibleParent();
1855 if( !pParentAcc.is() )
1857 return S_OK;
1860 Reference<XAccessibleContext> pRParentContext = pParentAcc->getAccessibleContext();
1862 if( Role == RADIO_BUTTON )
1864 int index = 0;
1865 int number = 0;
1866 Reference<XAccessibleRelationSet> pRrelationSet = pRContext->getAccessibleRelationSet();
1867 long nRel = pRrelationSet->getRelationCount();
1868 for(int i=0 ; i<nRel ; i++)
1870 AccessibleRelation accRelation = pRrelationSet->getRelation(i);
1871 if(accRelation.RelationType == 7)
1873 Sequence< Reference< XInterface > > xTargets = accRelation.TargetSet;
1875 Reference<XInterface> pRAcc = xTargets[0];
1876 sal_Int64 nChildCount = pRParentContext->getAccessibleChildCount();
1877 assert(nChildCount < std::numeric_limits<long>::max());
1878 for (sal_Int64 j = 0; j< nChildCount; j++)
1880 if( getTheParentOfMember(pRParentContext->getAccessibleChild(j).get())
1881 == static_cast<XAccessible*>(pRAcc.get()) &&
1882 pRParentContext->getAccessibleChild(j)->getAccessibleContext()->getAccessibleRole() == RADIO_BUTTON)
1883 number++;
1884 if (pRParentContext->getAccessibleChild(j).get() == m_xAccessible.get())
1885 index = number;
1889 *groupLevel = 1;
1890 *similarItemsInGroup = number;
1891 *positionInGroup = index;
1892 return S_OK;
1895 else if ( COMBO_BOX == Role )
1897 *groupLevel = 1;
1898 *similarItemsInGroup = 0;
1899 *positionInGroup = -1;
1901 if (pRContext->getAccessibleChildCount() != 2)
1903 return S_OK;
1905 Reference<XAccessible> xList=pRContext->getAccessibleChild(1);
1906 if (!xList.is())
1908 return S_OK;
1910 Reference<XAccessibleContext> xListContext(xList,UNO_QUERY);
1911 if (!xListContext.is())
1913 return S_OK;
1915 Reference<XAccessibleSelection> xListSel(xList,UNO_QUERY);
1916 if (!xListSel.is())
1918 return S_OK;
1920 sal_Int64 nChildCount = xListContext->getAccessibleChildCount();
1921 assert(nChildCount < std::numeric_limits<long>::max());
1922 *similarItemsInGroup = nChildCount;
1923 if (*similarItemsInGroup > 0 )
1927 Reference<XAccessible> xChild = xListSel->getSelectedAccessibleChild(0);
1928 if (xChild.is())
1930 Reference<XAccessibleContext> xChildContext(xChild,UNO_QUERY);
1931 if (xChildContext.is())
1933 *positionInGroup=xChildContext->getAccessibleIndexInParent() + 1 ;
1934 return S_OK;
1938 catch(...)
1941 return S_OK;
1943 else if ( PAGE_TAB == Role )
1945 *groupLevel = 1;
1946 sal_Int64 nChildCount = pRParentContext->getAccessibleChildCount();
1947 assert(nChildCount < std::numeric_limits<long>::max());
1948 *similarItemsInGroup = nChildCount;
1949 if (*similarItemsInGroup > 0 )
1951 *positionInGroup=pRContext->getAccessibleIndexInParent() + 1 ;
1953 else
1955 *positionInGroup = -1;
1957 return S_OK;
1960 int level = 0;
1961 bool isFound = false;
1962 while( pParentAcc.is() && !isFound)
1964 level++;
1965 pRParentContext = pParentAcc->getAccessibleContext();
1966 Role = pRParentContext->getAccessibleRole();
1967 if( (Role == TREE) || (Role == LIST) )
1968 isFound = true;
1969 pParentAcc = pRParentContext->getAccessibleParent();
1972 if( isFound )
1974 Reference< XAccessible> pTempAcc = pRContext->getAccessibleParent();
1975 pRParentContext = pTempAcc->getAccessibleContext();
1976 *groupLevel = level;
1977 sal_Int64 nChildCount = pRParentContext->getAccessibleChildCount();
1978 assert(nChildCount < std::numeric_limits<long>::max());
1979 *similarItemsInGroup = nChildCount;
1980 *positionInGroup = pRContext->getAccessibleIndexInParent() + 1;
1982 else
1984 *groupLevel = 0;
1985 *similarItemsInGroup = 0;
1986 *positionInGroup = 0;
1988 return S_OK;
1990 } catch(...) { return E_FAIL; }
1993 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_extendedStates(long, BSTR __RPC_FAR *__RPC_FAR *, long __RPC_FAR *)
1995 return E_NOTIMPL;
1999 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_uniqueID(long __RPC_FAR *uniqueID)
2001 SolarMutexGuard g;
2003 try {
2004 if (m_isDestroy) return S_FALSE;
2005 // #CHECK#
2006 if(uniqueID == nullptr)
2008 return E_INVALIDARG;
2010 *uniqueID = m_dChildID;
2011 return S_OK;
2013 } catch(...) { return E_FAIL; }
2016 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_windowHandle(HWND __RPC_FAR *windowHandle)
2018 SolarMutexGuard g;
2020 try {
2021 if (m_isDestroy) return S_FALSE;
2022 // #CHECK#
2023 if(windowHandle == nullptr)
2025 return E_INVALIDARG;
2028 HWND nHwnd = m_hwnd;
2029 IAccessible* pParent = m_pIParent;
2030 while((nHwnd==nullptr) && pParent)
2032 if (CMAccessible* pChild = dynamic_cast<CMAccessible*>(pParent))
2034 pParent = pChild->m_pIParent;
2035 nHwnd = pChild->m_hwnd;
2037 else
2038 pParent = nullptr;
2041 *windowHandle = nHwnd;
2042 return S_OK;
2044 } catch(...) { return E_FAIL; }
2048 * Get XAccessibleContext directly from UNO by the stored XAccessible pointer
2049 * @param pXAcc, UNO XAccessible object point.
2050 * @return XAccessibleContext*, the context of the pXAcc.
2052 XAccessibleContext* CMAccessible::GetContextByXAcc( XAccessible* pXAcc )
2054 Reference< XAccessibleContext > pRContext;
2055 if( pXAcc == nullptr)
2056 return nullptr;
2058 pRContext = pXAcc->getAccessibleContext();
2059 if( !pRContext.is() )
2060 return nullptr;
2061 return pRContext.get();
2065 * When COM is created, UNO set XAccessible pointer to it
2066 * in order to COM can operate UNO information
2067 * @param pXAcc, the XAccessible object of current object.
2068 * @return S_OK if successful.
2070 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::SetXAccessible(hyper pXAcc)
2072 // internal IMAccessible - no mutex meeded
2074 m_xAccessible = reinterpret_cast<XAccessible*>(pXAcc);
2075 m_pEnumVar->PutSelection(/*XAccessibleSelection*/
2076 reinterpret_cast<hyper>(m_xAccessible.get()));
2078 m_xContext = m_xAccessible->getAccessibleContext();
2080 return S_OK;
2084 * accSelect method has many optional flags, needs to process comprehensively
2085 * Mozilla and Microsoft do not implement SELFLAG_EXTENDSELECTION flag.
2086 * The implementation of this flag is a little trouble-shooting,so we also
2087 * do not implement it now
2088 * @param flagsSelect, the selection flag of the select action.
2089 * @param varChild, the child object pointer of current action.
2090 * @return S_OK if successful.
2092 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::accSelect(long flagsSelect, VARIANT varChild)
2094 SolarMutexGuard g;
2096 try {
2097 if (m_isDestroy) return S_FALSE;
2098 if( (flagsSelect&SELFLAG_ADDSELECTION) &&
2099 (SELFLAG_REMOVESELECTION&flagsSelect) )
2100 return E_INVALIDARG;
2102 if ( (flagsSelect&SELFLAG_TAKESELECTION) &&
2104 (flagsSelect&SELFLAG_ADDSELECTION) ||
2105 (flagsSelect&SELFLAG_REMOVESELECTION) ||
2106 (flagsSelect&SELFLAG_EXTENDSELECTION )
2109 return E_INVALIDARG;
2111 if ( varChild.vt != VT_I4 )
2112 return E_INVALIDARG;
2114 IMAccessible* pSelectAcc;
2115 if( varChild.lVal == CHILDID_SELF )
2117 pSelectAcc = this;
2118 pSelectAcc->AddRef();
2120 else
2122 pSelectAcc = GetChildInterface(varChild.lVal);
2125 if( pSelectAcc == nullptr )
2126 return E_INVALIDARG;
2128 if( flagsSelect&SELFLAG_TAKEFOCUS )
2130 union {
2131 XAccessible* pTempUNO;
2132 hyper nHyper = 0;
2134 pSelectAcc->GetUNOInterface(&nHyper);
2136 if( pTempUNO == nullptr )
2137 return 0;
2139 Reference<XAccessibleContext> pRContext = pTempUNO->getAccessibleContext();
2140 Reference< XAccessibleComponent > pRComponent(pRContext,UNO_QUERY);
2141 Reference< XAccessible > pRParentXAcc = pRContext->getAccessibleParent();
2142 Reference< XAccessibleContext > pRParentContext = pRParentXAcc->getAccessibleContext();
2143 Reference< XAccessibleComponent > pRParentComponent(pRParentContext,UNO_QUERY);
2144 Reference< XAccessibleSelection > pRParentSelection(pRParentContext,UNO_QUERY);
2147 pRComponent->grabFocus();
2149 if( flagsSelect & SELFLAG_TAKESELECTION )
2151 pRParentSelection->clearAccessibleSelection();
2152 pRParentSelection->selectAccessibleChild( pRContext->getAccessibleIndexInParent() );
2155 if( flagsSelect & SELFLAG_ADDSELECTION )
2157 pRParentSelection->selectAccessibleChild( pRContext->getAccessibleIndexInParent() );
2160 if( flagsSelect & SELFLAG_REMOVESELECTION )
2162 pRParentSelection->deselectAccessibleChild( pRContext->getAccessibleIndexInParent() );
2165 if( flagsSelect & SELFLAG_EXTENDSELECTION )
2167 sal_Int64 indexInParrent = pRContext->getAccessibleIndexInParent();
2169 if( pRParentSelection->isAccessibleChildSelected( indexInParrent + 1 ) ||
2170 pRParentSelection->isAccessibleChildSelected( indexInParrent - 1 ) )
2172 pRParentSelection->selectAccessibleChild( indexInParrent );
2178 pSelectAcc->Release();
2179 return S_OK;
2181 } catch(...) { return E_FAIL; }
2185 * Return XAccessible interface pointer when needed
2186 * @param pXAcc, [in, out] the Uno interface of the current object.
2187 * @return S_OK if successful.
2189 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::GetUNOInterface(hyper * pXAcc)
2191 // internal IMAccessible - no mutex meeded
2193 if(pXAcc == nullptr)
2194 return E_INVALIDARG;
2196 *pXAcc = reinterpret_cast<hyper>(m_xAccessible.get());
2197 return S_OK;
2201 * Helper method for Implementation of get_accDefaultAction
2202 * @param pAction, the default action point of the current object.
2203 * @return S_OK if successful.
2205 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::SetDefaultAction(hyper pAction)
2207 // internal IMAccessible - no mutex meeded
2209 m_xAction = reinterpret_cast<XAccessibleAction*>(pAction);
2210 return S_OK;
2214 * This method is called when AT open some UI elements initially
2215 * the UI element takes the default action defined here
2216 * @param varChild, the child id of the defaultaction.
2217 * @param pszDefaultAction,[in/out] the description of the current action.
2218 * @return S_OK if successful.
2220 COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE CMAccessible::get_accDefaultAction(VARIANT varChild, BSTR *pszDefaultAction)
2222 SolarMutexGuard g;
2224 try {
2225 if (m_isDestroy) return S_FALSE;
2226 // #CHECK#
2227 if(pszDefaultAction == nullptr)
2229 return E_INVALIDARG;
2231 if(varChild.vt==VT_I4)
2233 if(varChild.lVal==CHILDID_SELF)
2235 if (!m_xAction.is())
2236 return DISP_E_MEMBERNOTFOUND;
2237 SysFreeString(*pszDefaultAction);
2238 *pszDefaultAction = SysAllocString(m_pszActionDescription);
2239 return S_OK;
2242 long lVal = varChild.lVal;
2243 varChild.lVal = CHILDID_SELF;
2244 IMAccessible *pChild = this->GetChildInterface(lVal);
2245 if(!pChild)
2246 return E_FAIL;
2247 return pChild->get_accDefaultAction(varChild,pszDefaultAction);
2249 return S_FALSE;
2251 } catch(...) { return E_FAIL; }
2255 * AT call this method to operate application
2256 * @param varChild, the child id of the action object.
2257 * @return S_OK if successful.
2259 COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE CMAccessible::accDoDefaultAction(VARIANT varChild)
2261 SolarMutexGuard g;
2263 try {
2264 if (m_isDestroy) return S_FALSE;
2265 if( varChild.vt != VT_I4 )
2266 return E_INVALIDARG;
2267 if (!m_xAction.is())
2268 return E_FAIL;
2269 if (m_xAction->getAccessibleActionCount() == 0)
2270 return E_FAIL;
2272 if(varChild.lVal==CHILDID_SELF)
2274 if (m_xAction->getAccessibleActionCount() > 0)
2275 m_xAction->doAccessibleAction(0);
2276 return S_OK;
2279 long lVal = varChild.lVal;
2280 varChild.lVal = CHILDID_SELF;
2281 IMAccessible *pChild = this->GetChildInterface(lVal);
2282 if(!pChild)
2283 return E_FAIL;
2284 return pChild->accDoDefaultAction( varChild );
2286 } catch(...) { return E_FAIL; }
2290 * UNO set description information for action to COM.
2291 * @param szAction, the action description of the current object.
2292 * @return S_OK if successful.
2294 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::Put_ActionDescription( const OLECHAR* szAction)
2296 // internal IMAccessible - no mutex meeded
2298 try {
2299 if (m_isDestroy) return S_FALSE;
2300 // #CHECK#
2301 if(szAction == nullptr)
2303 return E_INVALIDARG;
2305 SysFreeString(m_pszActionDescription );
2306 m_pszActionDescription = SysAllocString( szAction );
2307 return S_OK;
2309 } catch(...) { return E_FAIL; }
2312 bool CMAccessible::GetXInterfaceFromXAccessible(XAccessible* pXAcc, XInterface** ppXI, XInterfaceType eType)
2314 switch(eType)
2316 case XInterfaceType::XI_COMPONENT:
2317 return queryXInterface<XAccessibleComponent>(pXAcc, ppXI);
2318 case XInterfaceType::XI_TEXT:
2319 return queryXInterface<XAccessibleText>(pXAcc, ppXI);
2320 case XInterfaceType::XI_EDITABLETEXT:
2321 return queryXInterface<XAccessibleEditableText>(pXAcc, ppXI);
2322 case XInterfaceType::XI_TABLE:
2323 return queryXInterface<XAccessibleTable>(pXAcc, ppXI);
2324 case XInterfaceType::XI_TABLECELL:
2325 // needs specific handling, since there's no XInterface for table cells
2326 return queryTableCell(pXAcc, ppXI);
2327 case XInterfaceType::XI_SELECTION:
2328 return queryXInterface<XAccessibleSelection>(pXAcc, ppXI);
2329 case XInterfaceType::XI_EXTENDEDCOMP:
2330 return queryXInterface<XAccessibleExtendedComponent>(pXAcc, ppXI);
2331 case XInterfaceType::XI_KEYBINDING:
2332 return queryXInterface<XAccessibleKeyBinding>(pXAcc, ppXI);
2333 case XInterfaceType::XI_ACTION:
2334 return queryXInterface<XAccessibleAction>(pXAcc, ppXI);
2335 case XInterfaceType::XI_VALUE:
2336 return queryXInterface<XAccessibleValue>(pXAcc, ppXI);
2337 case XInterfaceType::XI_HYPERTEXT:
2338 return queryXInterface<XAccessibleHypertext>(pXAcc, ppXI);
2339 case XInterfaceType::XI_HYPERLINK:
2340 return queryXInterface<XAccessibleHyperlink>(pXAcc, ppXI);
2341 case XInterfaceType::XI_IMAGE:
2342 return queryXInterface<XAccessibleImage>(pXAcc, ppXI);
2343 default:
2344 return false;
2348 template<typename T> static HRESULT
2349 createAggInstance(CMAccessible &rOuter, void ** ppvObject)
2351 // Note: CComAggObject has special handling for IUnknown - must
2352 // query for that when creating it! Otherwise we get a T member of it
2353 // which will redirect QueryInterface back to CMAccessible infinitely.
2354 // (CComAggObject has its own ref-count too which is not a problem
2355 // since it is inserted in m_containedObjects.)
2356 return CComCreator< CComAggObject<T> >::CreateInstance(
2357 rOuter.GetControllingUnknown(), IID_IUnknown, ppvObject);
2360 typedef HRESULT (AggCreatorFunc)(CMAccessible &, void **);
2362 namespace {
2364 struct AggMapEntry
2366 const IID* piid;
2367 AggCreatorFunc* pfnCreateInstance;
2368 const XInterfaceType eXInterfaceType;
2373 static AggMapEntry g_CMAccessible_AggMap[] = {
2374 { &IID_IAccessibleComponent, &createAggInstance<CAccComponent>, XInterfaceType::XI_COMPONENT },
2375 { &IID_IAccessibleText, &createAggInstance<CAccText>, XInterfaceType::XI_TEXT },
2376 { &IID_IAccessibleEditableText, &createAggInstance<CAccEditableText>, XInterfaceType::XI_EDITABLETEXT },
2377 { &IID_IAccessibleImage, &createAggInstance<CAccImage>, XInterfaceType::XI_IMAGE },
2378 { &IID_IAccessibleTable, &createAggInstance<CAccTable>, XInterfaceType::XI_TABLE },
2379 { &IID_IAccessibleTable2, &createAggInstance<CAccTable>, XInterfaceType::XI_TABLE },
2380 { &IID_IAccessibleTableCell, &createAggInstance<CAccTableCell>, XInterfaceType::XI_TABLECELL },
2381 { &IID_IAccessibleAction, &createAggInstance<CAccAction>, XInterfaceType::XI_ACTION },
2382 { &IID_IAccessibleValue, &createAggInstance<CAccValue>, XInterfaceType::XI_VALUE },
2383 { &IID_IAccessibleHypertext, &createAggInstance<CAccHypertext>, XInterfaceType::XI_HYPERTEXT },
2384 { &IID_IAccessibleHyperlink, &createAggInstance<CAccHyperLink>, XInterfaceType::XI_HYPERLINK }
2388 HRESULT WINAPI CMAccessible::SmartQI(void* /*pv*/, REFIID iid, void** ppvObject)
2390 try {
2392 if (m_isDestroy) return S_FALSE;
2393 if (InlineIsEqualGUID(iid,IID_IAccIdentity) ||
2394 InlineIsEqualGUID(iid,IID_IStdMarshalInfo) ||
2395 InlineIsEqualGUID(iid,IID_IMarshal) ||
2396 InlineIsEqualGUID(iid,IID_IExternalConnection)||
2397 InlineIsEqualGUID(iid,IID_IOleWindow))
2399 return E_FAIL;
2402 for (const AggMapEntry& rEntry : g_CMAccessible_AggMap)
2404 if (InlineIsEqualGUID(iid, *rEntry.piid))
2406 SolarMutexGuard g;
2408 XInterface* pXI = nullptr;
2409 bool bFound = GetXInterfaceFromXAccessible(m_xAccessible.get(),
2410 &pXI, rEntry.eXInterfaceType);
2411 if(!bFound)
2413 return E_FAIL;
2416 XGUIDToComObjHash::iterator pIndTemp = m_containedObjects.find( iid );
2417 if ( pIndTemp != m_containedObjects.end() )
2419 return pIndTemp->second.p->QueryInterface( iid, ppvObject );
2421 else
2423 HRESULT hr = rEntry.pfnCreateInstance(*this, ppvObject);
2424 assert(hr == S_OK);
2425 if(hr == S_OK)
2427 m_containedObjects.emplace(*rEntry.piid, static_cast<IUnknown*>(*ppvObject));
2428 IUNOXWrapper* wrapper = nullptr;
2429 static_cast<IUnknown*>(*ppvObject)->QueryInterface(IID_IUNOXWrapper, reinterpret_cast<void**>(&wrapper));
2430 if(wrapper)
2432 wrapper->put_XInterface(
2433 reinterpret_cast<hyper>(m_xAccessible.get()));
2434 wrapper->Release();
2436 return S_OK;
2439 return E_FAIL;
2442 return E_FAIL;
2444 } catch(...) { return E_FAIL; }
2447 bool CMAccessible::get_IAccessibleFromXAccessible(XAccessible* pXAcc, IAccessible** ppIA)
2451 // #CHECK#
2452 if(ppIA == nullptr)
2454 return false;
2456 bool isGet = false;
2457 if(g_pAgent)
2458 isGet = g_pAgent->GetIAccessibleFromXAccessible(pXAcc, ppIA);
2460 return isGet;
2462 catch(...)
2464 return false;
2468 OUString CMAccessible::get_StringFromAny(Any const & pAny)
2470 switch(pAny.getValueTypeClass())
2472 case TypeClass_CHAR:
2474 sal_Int8 val;
2475 pAny >>= val;
2476 return OUString::number(val);
2478 case TypeClass_BOOLEAN:
2480 bool val;
2481 pAny >>= val;
2482 return OUString::number(int(val));
2484 case TypeClass_BYTE:
2486 sal_Int8 val;
2487 pAny >>= val;
2488 return OUString::number(val);
2490 case TypeClass_SHORT:
2492 sal_Int16 val;
2493 pAny >>= val;
2494 return OUString::number(val);
2496 case TypeClass_UNSIGNED_SHORT:
2498 sal_uInt16 val;
2499 pAny >>= val;
2500 return OUString::number(val);
2502 case TypeClass_LONG:
2504 sal_Int32 val;
2505 pAny >>= val;
2506 return OUString::number(val);
2508 case TypeClass_UNSIGNED_LONG:
2510 sal_uInt32 val;
2511 pAny >>= val;
2512 return OUString::number(val);
2514 case TypeClass_FLOAT:
2516 float val;
2517 pAny >>= val;
2518 return OUString::number(val);
2520 case TypeClass_DOUBLE:
2522 double val;
2523 pAny >>= val;
2524 return OUString::number(val);
2526 case TypeClass_STRING:
2528 OUString val;
2529 pAny >>= val;
2530 return val;
2532 case TypeClass_SEQUENCE:
2534 if(pAny.getValueType() == cppu::UnoType<Sequence< OUString >>::get())
2536 Sequence < OUString > val;
2537 pAny >>= val;
2539 OUStringBuffer pString;
2541 for (const OUString& rElem : val)
2542 pString.append(rElem);
2544 return pString.makeStringAndClear();
2546 else if (pAny.getValueType() == cppu::UnoType<Sequence< css::style::TabStop >>::get())
2548 Sequence < css::style::TabStop > val;
2549 pAny >>= val;
2551 OUStringBuffer buf;
2552 for (const css::style::TabStop& rSingleVal : val)
2554 buf.append(
2555 "Position=" + OUString::number(rSingleVal.Position) + ",TabAlign="
2556 + OUString::number(sal_Int32(rSingleVal.Alignment)) + ",DecimalChar=");
2557 if (rSingleVal.DecimalChar==';' || rSingleVal.DecimalChar == ':' || rSingleVal.DecimalChar == ',' ||
2558 rSingleVal.DecimalChar == '=' || rSingleVal.DecimalChar == '\\')
2559 buf.append('\\');
2560 buf.append(OUStringChar(rSingleVal.DecimalChar) + ",FillChar=");
2561 if (rSingleVal.FillChar==';' || rSingleVal.FillChar == ':' || rSingleVal.FillChar == ',' ||
2562 rSingleVal.FillChar == '=' || rSingleVal.FillChar == '\\')
2563 buf.append('\\');
2564 buf.append(OUStringChar(rSingleVal.FillChar) + ",");
2566 return buf.makeStringAndClear();
2568 break;
2570 case TypeClass_ENUM:
2572 if (pAny.getValueType() == cppu::UnoType<css::awt::FontSlant>::get())
2574 css::awt::FontSlant val;
2575 pAny >>= val;
2576 return OUString::number(sal_Int32(val));
2578 break;
2580 case TypeClass_STRUCT:
2582 if (pAny.getValueType() == cppu::UnoType<css::style::LineSpacing>::get())
2584 css::style::LineSpacing val;
2585 pAny >>= val;
2586 return "Mode=" + OUString::number(val.Mode) + ",Height="
2587 + OUString::number(val.Height) + ",";
2589 else if (pAny.getValueType() == cppu::UnoType<css::accessibility::TextSegment>::get())
2591 css::accessibility::TextSegment val;
2592 pAny >>= val;
2593 return val.SegmentText;
2595 break;
2597 case TypeClass_VOID:
2598 case TypeClass_HYPER:
2599 case TypeClass_UNSIGNED_HYPER:
2600 case TypeClass_TYPE:
2601 case TypeClass_ANY:
2602 case TypeClass_TYPEDEF:
2603 case TypeClass_EXCEPTION:
2604 case TypeClass_INTERFACE:
2605 case TypeClass_SERVICE:
2606 case TypeClass_MODULE:
2607 case TypeClass_INTERFACE_METHOD:
2608 case TypeClass_INTERFACE_ATTRIBUTE:
2609 case TypeClass_UNKNOWN:
2610 case TypeClass_PROPERTY:
2611 case TypeClass_CONSTANT:
2612 case TypeClass_CONSTANTS:
2613 case TypeClass_SINGLETON:
2614 break;
2615 default:
2616 break;
2618 return OUString();
2621 OUString CMAccessible::get_String4Numbering(const Any& pAny, sal_Int16 numberingLevel,std::u16string_view numberingPrefix)
2623 Reference< css::container::XIndexReplace > pXIndex;
2624 if((pAny>>=pXIndex) && (numberingLevel !=-1))//numbering level is -1,means invalid value
2626 Any aAny = pXIndex->getByIndex(numberingLevel);
2627 Sequence< css::beans::PropertyValue > aProps;
2628 aAny >>= aProps;
2629 OUStringBuffer buf("Numbering:NumberingLevel=" + OUString::number(numberingLevel) + ",");
2630 for (const css::beans::PropertyValue& rProp : aProps)
2632 if( (rProp.Name == "BulletChar" ) ||
2633 (rProp.Name == "NumberingType" ))
2635 buf.append(rProp.Name + "=");
2636 auto const pTemp = CMAccessible::get_StringFromAny(rProp.Value);
2637 buf.append(pTemp + ",");
2639 if (rProp.Name == "NumberingType" && !numberingPrefix.empty())
2641 buf.append(OUString::Concat("NumberingPrefix=") + numberingPrefix);
2645 return buf.makeStringAndClear();
2648 //Because now have three types numbering level:
2649 //1.real numbering list,numbering level>=0 and numbering Rule !=NULL;
2650 //2.common paragraph, numbering level >=0, and numbering Rule == NULL;
2651 //3.TOC paragraph, numbering level >0, and numbering Rule ==NULL;
2652 // IAText:numberinglevel base on 0, but TOC's level base on 1,
2653 // so NumberingLevel value will be decreased 1 in bridge code.
2654 else if(numberingLevel >0)
2656 return "Numbering:NumberingLevel=" + OUString::number(numberingLevel-1) + ",NumberingType=4,NumberingPrefix=,";
2658 else
2660 return "Numbering:";
2664 void CMAccessible::ConvertAnyToVariant(const css::uno::Any &rAnyVal, VARIANT *pvData)
2666 if(rAnyVal.hasValue())
2668 // Clear VARIANT variable.
2669 VariantClear(pvData);
2671 // Set value according to value type.
2672 switch(rAnyVal.getValueTypeClass())
2674 case TypeClass_CHAR:
2675 pvData->vt = VT_UI1;
2676 memcpy(&pvData->bVal, rAnyVal.getValue(), sizeof(char));
2677 break;
2679 case TypeClass_BOOLEAN:
2681 bool bBoolean(false);
2682 rAnyVal >>= bBoolean;
2683 pvData->vt = VT_BOOL;
2684 pvData->boolVal = VARIANT_BOOL(bBoolean); // boolVal is a VARIANT_BOOL, a 16bit field
2685 break;
2687 case TypeClass_BYTE:
2688 pvData->vt = VT_UI1;
2689 memcpy(&pvData->bVal, rAnyVal.getValue(), sizeof(sal_Int8));
2690 break;
2692 case TypeClass_SHORT:
2693 pvData->vt = VT_I2;
2694 memcpy(&pvData->iVal, rAnyVal.getValue(), sizeof(sal_Int16));
2695 break;
2697 case TypeClass_UNSIGNED_SHORT:
2698 pvData->vt = VT_I2;
2699 memcpy(&pvData->iVal, rAnyVal.getValue(), sizeof(sal_uInt16));
2700 break;
2702 case TypeClass_LONG:
2703 pvData->vt = VT_I4;
2704 memcpy(&pvData->lVal, rAnyVal.getValue(), sizeof(sal_Int32));
2705 break;
2707 case TypeClass_UNSIGNED_LONG:
2708 pvData->vt = VT_I4;
2709 memcpy(&pvData->lVal, rAnyVal.getValue(), sizeof(sal_uInt32));
2710 break;
2712 case TypeClass_FLOAT:
2713 pvData->vt = VT_R4;
2714 memcpy(&pvData->fltVal, rAnyVal.getValue(), sizeof(float));
2715 break;
2717 case TypeClass_DOUBLE:
2718 pvData->vt = VT_R8;
2719 memcpy(&pvData->dblVal, rAnyVal.getValue(), sizeof(double));
2720 break;
2722 case TypeClass_STRING:
2724 pvData->vt = VT_BSTR;
2725 OUString val;
2726 rAnyVal >>= val;
2727 pvData->bstrVal = SysAllocString(o3tl::toW(val.getStr()));
2728 break;
2731 case TypeClass_VOID:
2732 case TypeClass_HYPER:
2733 case TypeClass_UNSIGNED_HYPER:
2734 case TypeClass_TYPE:
2735 case TypeClass_ANY:
2736 case TypeClass_ENUM:
2737 case TypeClass_TYPEDEF:
2738 case TypeClass_STRUCT:
2739 case TypeClass_EXCEPTION:
2740 case TypeClass_SEQUENCE:
2741 case TypeClass_INTERFACE:
2743 Reference< XAccessible > pXAcc;
2744 if(rAnyVal >>= pXAcc)
2746 if(pXAcc.is())
2748 IAccessible* pIAcc = nullptr;
2749 get_IAccessibleFromXAccessible(pXAcc.get(), &pIAcc);
2750 if(pIAcc == nullptr)
2752 Reference< XAccessibleContext > pXAccContext = pXAcc->getAccessibleContext();
2753 g_pAgent->InsertAccObj(pXAcc.get(),pXAccContext->getAccessibleParent().get());
2754 get_IAccessibleFromXAccessible(pXAcc.get(), &pIAcc);
2756 if(pIAcc)
2758 pIAcc->AddRef();
2760 pvData->vt = VT_UNKNOWN;
2761 pvData->pdispVal = pIAcc;
2762 break;
2766 [[fallthrough]];
2768 case TypeClass_SERVICE:
2769 case TypeClass_MODULE:
2770 case TypeClass_INTERFACE_METHOD:
2771 case TypeClass_INTERFACE_ATTRIBUTE:
2772 case TypeClass_UNKNOWN:
2773 case TypeClass_PROPERTY:
2774 case TypeClass_CONSTANT:
2775 case TypeClass_CONSTANTS:
2776 case TypeClass_SINGLETON:
2777 case TypeClass::TypeClass_MAKE_FIXED_SIZE:
2778 // Output the type string, if there is other uno value type.
2779 pvData->vt = VT_BSTR;
2780 pvData->bstrVal = SysAllocString(o3tl::toW(rAnyVal.getValueTypeName().getStr()));
2781 break;
2783 default:
2784 break;
2787 else
2789 VariantClear(pvData);
2793 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_states(AccessibleStates __RPC_FAR *states)
2795 SolarMutexGuard g;
2797 try {
2798 if (m_isDestroy) return S_FALSE;
2800 if (!m_xContext.is())
2801 return E_FAIL;
2803 sal_Int64 const nRStateSet =
2804 m_xContext->getAccessibleStateSet();
2806 *states = 0x0;
2807 for( std::size_t j = 0; j < SAL_N_ELEMENTS(UNO_STATES); j++ )
2809 if( (UNO_STATES[j] != -1) && (nRStateSet & UNO_STATES[j]) )
2811 *states |= IA2_STATES[j];
2814 return S_OK;
2817 } catch(...) { return E_FAIL; }
2820 // return the UNO roles
2821 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_extendedRole(BSTR __RPC_FAR *)
2823 return E_NOTIMPL;
2826 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_localizedExtendedRole(BSTR __RPC_FAR *)
2828 return E_NOTIMPL;
2831 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_nExtendedStates(long __RPC_FAR *)
2833 return E_NOTIMPL;
2837 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_localizedExtendedStates(long, BSTR __RPC_FAR *__RPC_FAR *, long __RPC_FAR *)
2839 return E_NOTIMPL;
2843 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_indexInParent(long __RPC_FAR *accParentIndex)
2845 try {
2846 if (m_isDestroy) return S_FALSE;
2847 // #CHECK#
2848 if(accParentIndex == nullptr)
2849 return E_INVALIDARG;
2851 if (!m_xContext.is())
2852 return E_FAIL;
2854 sal_Int64 nIndex = m_xContext->getAccessibleIndexInParent();
2855 if (nIndex > std::numeric_limits<long>::max())
2857 SAL_WARN("iacc2", "CMAccessible::get_indexInParent: Child index exceeds maximum long value, "
2858 "returning max long.");
2859 nIndex = std::numeric_limits<long>::max();
2861 *accParentIndex = nIndex;
2862 return S_OK;
2865 } catch(...) { return E_FAIL; }
2867 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_locale( IA2Locale __RPC_FAR *locale )
2869 try {
2870 if (m_isDestroy) return S_FALSE;
2871 if(locale == nullptr)
2872 return E_INVALIDARG;
2874 if (!m_xContext.is())
2875 return E_FAIL;
2877 css::lang::Locale unoLoc = m_xContext->getLocale();
2878 locale->language = SysAllocString(o3tl::toW(unoLoc.Language.getStr()));
2879 locale->country = SysAllocString(o3tl::toW(unoLoc.Country.getStr()));
2880 locale->variant = SysAllocString(o3tl::toW(unoLoc.Variant.getStr()));
2882 return S_OK;
2884 } catch(...) { return E_FAIL; }
2887 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_appName(BSTR __RPC_FAR *name)
2889 SolarMutexGuard g;
2891 try {
2892 if (m_isDestroy) return S_FALSE;
2893 if(name == nullptr)
2894 return E_INVALIDARG;
2896 static const OUString sAppName = utl::ConfigManager::getProductName();
2897 *name = SysAllocString(o3tl::toW(sAppName.getStr()));
2898 return S_OK;
2899 } catch(...) { return E_FAIL; }
2901 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_appVersion(BSTR __RPC_FAR *version)
2903 SolarMutexGuard g;
2905 try {
2906 if (m_isDestroy) return S_FALSE;
2907 if(version == nullptr)
2908 return E_INVALIDARG;
2909 static const OUString sVersion = utl::ConfigManager::getProductVersion();
2910 *version=SysAllocString(o3tl::toW(sVersion.getStr()));
2911 return S_OK;
2912 } catch(...) { return E_FAIL; }
2914 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_toolkitName(BSTR __RPC_FAR *name)
2916 SolarMutexGuard g;
2918 try {
2919 if (m_isDestroy) return S_FALSE;
2920 if(name == nullptr)
2921 return E_INVALIDARG;
2922 *name = SysAllocString(OLESTR("VCL"));
2923 return S_OK;
2924 } catch(...) { return E_FAIL; }
2926 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_toolkitVersion(BSTR __RPC_FAR *version)
2928 return get_appVersion(version);
2932 COM_DECLSPEC_NOTHROW STDMETHODIMP CMAccessible::get_attributes(/*[out]*/ BSTR *pAttr)
2934 SolarMutexGuard g;
2936 try {
2937 if (m_isDestroy) return S_FALSE;
2939 if (!m_xAccessible.is())
2940 return E_FAIL;
2942 Reference<XAccessibleContext> pRContext = m_xAccessible->getAccessibleContext();
2943 if( !pRContext.is() )
2945 return E_FAIL;
2947 Reference<XAccessibleExtendedAttributes> pRXI(pRContext,UNO_QUERY);
2948 if( !pRXI.is() )
2949 return E_FAIL;
2950 else
2952 css::uno::Reference<css::accessibility::XAccessibleExtendedAttributes> pRXAttr;
2953 pRXAttr = pRXI.get();
2954 css::uno::Any anyVal = pRXAttr->getExtendedAttributes();
2956 OUString val;
2957 anyVal >>= val;
2959 if(*pAttr)
2960 SysFreeString(*pAttr);
2961 *pAttr = SysAllocString(o3tl::toW(val.getStr()));
2963 return S_OK;
2965 } catch(...) { return E_FAIL; }
2968 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */