summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp')
-rw-r--r--src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp1213
1 files changed, 1213 insertions, 0 deletions
diff --git a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp
new file mode 100644
index 0000000000..8791bbdcfb
--- /dev/null
+++ b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp
@@ -0,0 +1,1213 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsmsaaaccessible.h"
+#include "qwindowsaccessibility.h"
+#include <oleacc.h>
+#include <servprov.h>
+#include <winuser.h>
+#include "comutils.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qsettings.h>
+#include <QtGui/qaccessible.h>
+#include <QtGui/qaccessible2.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qplatformnativeinterface_qpa.h>
+#include <QtGui/qwindow.h>
+#include <QtWidgets/qapplication.h>
+#include <QtWidgets/qgraphicsitem.h>
+#include <QtWidgets/qgraphicsview.h>
+#include <QtWidgets/qmessagebox.h>
+
+//#include <uiautomationcoreapi.h>
+#ifndef UiaRootObjectId
+#define UiaRootObjectId -25
+#endif
+
+#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU)
+#include <comdef.h>
+#endif
+
+#ifdef Q_OS_WINCE
+#include "../qguifunctions_wince.h"
+#endif
+
+#include "../qtwindows_additional.h"
+
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsEnumerate : public IEnumVARIANT
+{
+public:
+ QWindowsEnumerate(const QVector<int> &a)
+ : ref(0), current(0),array(a)
+ {
+ }
+
+ virtual ~QWindowsEnumerate() {}
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ HRESULT STDMETHODCALLTYPE Clone(IEnumVARIANT **ppEnum);
+ HRESULT STDMETHODCALLTYPE Next(unsigned long celt, VARIANT FAR* rgVar, unsigned long FAR* pCeltFetched);
+ HRESULT STDMETHODCALLTYPE Reset();
+ HRESULT STDMETHODCALLTYPE Skip(unsigned long celt);
+
+private:
+ ULONG ref;
+ ULONG current;
+ QVector<int> array;
+};
+
+HRESULT STDMETHODCALLTYPE QWindowsEnumerate::QueryInterface(REFIID id, LPVOID *iface)
+{
+ *iface = 0;
+ if (id == IID_IUnknown)
+ *iface = (IUnknown*)this;
+ else if (id == IID_IEnumVARIANT)
+ *iface = (IEnumVARIANT*)this;
+
+ if (*iface) {
+ AddRef();
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+ULONG STDMETHODCALLTYPE QWindowsEnumerate::AddRef()
+{
+ return ++ref;
+}
+
+ULONG STDMETHODCALLTYPE QWindowsEnumerate::Release()
+{
+ if (!--ref) {
+ delete this;
+ return 0;
+ }
+ return ref;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Clone(IEnumVARIANT **ppEnum)
+{
+ QWindowsEnumerate *penum = 0;
+ *ppEnum = 0;
+
+ penum = new QWindowsEnumerate(array);
+ if (!penum)
+ return E_OUTOFMEMORY;
+ penum->current = current;
+ penum->array = array;
+ penum->AddRef();
+ *ppEnum = penum;
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Next(unsigned long celt, VARIANT FAR* rgVar, unsigned long FAR* pCeltFetched)
+{
+ if (pCeltFetched)
+ *pCeltFetched = 0;
+
+ ULONG l;
+ for (l = 0; l < celt; l++) {
+ VariantInit(&rgVar[l]);
+ if ((current+1) > (ULONG)array.size()) {
+ *pCeltFetched = l;
+ return S_FALSE;
+ }
+
+ rgVar[l].vt = VT_I4;
+ rgVar[l].lVal = array[(int)current];
+ ++current;
+ }
+ *pCeltFetched = l;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Reset()
+{
+ current = 0;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Skip(unsigned long celt)
+{
+ current += celt;
+ if (current > (ULONG)array.size()) {
+ current = array.size();
+ return S_FALSE;
+ }
+ return S_OK;
+}
+
+static bool compareAccessible(QAccessibleInterface *one, QAccessibleInterface *other)
+{
+ if (one == other) return true;
+ if (!one || !other) return false;
+
+ if (one->object() && other->object() && (one->object() == other->object()))
+ return true;
+ QAIPointer onePar(one->parent());
+ QAIPointer otherPar(other->parent());
+
+ if (compareAccessible(onePar.data(), otherPar.data()))
+ return onePar->indexOfChild(one) == otherPar->indexOfChild(other);
+ return false;
+}
+
+#ifndef QT_NO_DEBUG
+bool debug_accessibility()
+{
+ static int debugging = -1;
+ if (debugging == -1)
+ debugging = qgetenv("QT_DEBUG_ACCESSIBILITY").toInt();
+ return !!debugging;
+}
+#endif
+
+#if defined(DEBUG_SHOW_ATCLIENT_COMMANDS)
+void accessibleDebugClientCalls_helper(const char* funcName, const QAccessibleInterface *iface)
+{
+ QString str;
+ QDebug dbg(&str);
+ dbg << iface << QLatin1String(funcName);
+ accessibleDebug("%s", qPrintable(str));
+}
+#endif
+
+/*
+ IDispatch
+*/
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::GetTypeInfoCount(unsigned int * pctinfo)
+{
+ // We don't use a type library
+ *pctinfo = 0;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::GetTypeInfo(unsigned int, unsigned long, ITypeInfo **pptinfo)
+{
+ // We don't use a type library
+ *pptinfo = 0;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::GetIDsOfNames(const _GUID &, wchar_t **rgszNames, unsigned int, unsigned long, long *rgdispid)
+{
+#if !defined(Q_CC_BOR) && !defined(Q_CC_GNU)
+ // PROPERTIES: Hierarchical
+ if (_bstr_t(rgszNames[0]) == _bstr_t(L"accParent"))
+ rgdispid[0] = DISPID_ACC_PARENT;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accChildCount"))
+ rgdispid[0] = DISPID_ACC_CHILDCOUNT;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accChild"))
+ rgdispid[0] = DISPID_ACC_CHILD;
+
+ // PROPERTIES: Descriptional
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accName("))
+ rgdispid[0] = DISPID_ACC_NAME;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accValue"))
+ rgdispid[0] = DISPID_ACC_VALUE;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accDescription"))
+ rgdispid[0] = DISPID_ACC_DESCRIPTION;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accRole"))
+ rgdispid[0] = DISPID_ACC_ROLE;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accState"))
+ rgdispid[0] = DISPID_ACC_STATE;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accHelp"))
+ rgdispid[0] = DISPID_ACC_HELP;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accHelpTopic"))
+ rgdispid[0] = DISPID_ACC_HELPTOPIC;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accKeyboardShortcut"))
+ rgdispid[0] = DISPID_ACC_KEYBOARDSHORTCUT;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accFocus"))
+ rgdispid[0] = DISPID_ACC_FOCUS;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accSelection"))
+ rgdispid[0] = DISPID_ACC_SELECTION;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accDefaultAction"))
+ rgdispid[0] = DISPID_ACC_DEFAULTACTION;
+
+ // METHODS
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accSelect"))
+ rgdispid[0] = DISPID_ACC_SELECT;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accLocation"))
+ rgdispid[0] = DISPID_ACC_LOCATION;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accNavigate"))
+ rgdispid[0] = DISPID_ACC_NAVIGATE;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accHitTest"))
+ rgdispid[0] = DISPID_ACC_HITTEST;
+ else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accDoDefaultAction"))
+ rgdispid[0] = DISPID_ACC_DODEFAULTACTION;
+ else
+ return DISP_E_UNKNOWNINTERFACE;
+
+ return S_OK;
+#else
+ Q_UNUSED(rgszNames);
+ Q_UNUSED(rgdispid);
+
+ return DISP_E_MEMBERNOTFOUND;
+#endif
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::Invoke(long dispIdMember,
+ const _GUID &,
+ unsigned long,
+ unsigned short wFlags,
+ tagDISPPARAMS *pDispParams,
+ tagVARIANT *pVarResult,
+ tagEXCEPINFO *, unsigned int *)
+{
+ HRESULT hr = DISP_E_MEMBERNOTFOUND;
+
+ switch (dispIdMember)
+ {
+ case DISPID_ACC_PARENT:
+ if (wFlags == DISPATCH_PROPERTYGET) {
+ if (!pVarResult)
+ return E_INVALIDARG;
+ hr = get_accParent(&pVarResult->pdispVal);
+ } else {
+ hr = DISP_E_MEMBERNOTFOUND;
+ }
+ break;
+
+ case DISPID_ACC_CHILDCOUNT:
+ if (wFlags == DISPATCH_PROPERTYGET) {
+ if (!pVarResult)
+ return E_INVALIDARG;
+ hr = get_accChildCount(&pVarResult->lVal);
+ } else {
+ hr = DISP_E_MEMBERNOTFOUND;
+ }
+ break;
+
+ case DISPID_ACC_CHILD:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accChild(pDispParams->rgvarg[0], &pVarResult->pdispVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_NAME:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accName(pDispParams->rgvarg[0], &pVarResult->bstrVal);
+ else if (wFlags == DISPATCH_PROPERTYPUT)
+ hr = put_accName(pDispParams->rgvarg[0], pVarResult->bstrVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_VALUE:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accValue(pDispParams->rgvarg[0], &pVarResult->bstrVal);
+ else if (wFlags == DISPATCH_PROPERTYPUT)
+ hr = put_accValue(pDispParams->rgvarg[0], pVarResult->bstrVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_DESCRIPTION:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accDescription(pDispParams->rgvarg[0], &pVarResult->bstrVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_ROLE:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accRole(pDispParams->rgvarg[0], pVarResult);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_STATE:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accState(pDispParams->rgvarg[0], pVarResult);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_HELP:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accHelp(pDispParams->rgvarg[0], &pVarResult->bstrVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_HELPTOPIC:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accHelpTopic(&pDispParams->rgvarg[2].bstrVal, pDispParams->rgvarg[1], &pDispParams->rgvarg[0].lVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_KEYBOARDSHORTCUT:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accKeyboardShortcut(pDispParams->rgvarg[0], &pVarResult->bstrVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_FOCUS:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accFocus(pVarResult);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_SELECTION:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accSelection(pVarResult);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_DEFAULTACTION:
+ if (wFlags == DISPATCH_PROPERTYGET)
+ hr = get_accDefaultAction(pDispParams->rgvarg[0], &pVarResult->bstrVal);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_SELECT:
+ if (wFlags == DISPATCH_METHOD)
+ hr = accSelect(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0]);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_LOCATION:
+ if (wFlags == DISPATCH_METHOD)
+ hr = accLocation(&pDispParams->rgvarg[4].lVal, &pDispParams->rgvarg[3].lVal, &pDispParams->rgvarg[2].lVal, &pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0]);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_NAVIGATE:
+ if (wFlags == DISPATCH_METHOD)
+ hr = accNavigate(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0], pVarResult);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_HITTEST:
+ if (wFlags == DISPATCH_METHOD)
+ hr = accHitTest(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0].lVal, pVarResult);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ case DISPID_ACC_DODEFAULTACTION:
+ if (wFlags == DISPATCH_METHOD)
+ hr = accDoDefaultAction(pDispParams->rgvarg[0]);
+ else
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+
+ default:
+ hr = DISP_E_MEMBERNOTFOUND;
+ break;
+ }
+
+ if (!SUCCEEDED(hr)) {
+ return hr;
+ }
+ return hr;
+}
+
+/*
+ IAccessible
+
+IAccessible::accHitTest documents the value returned in pvarID like this:
+
+| *Point location* | *vt member* | *Value member* |
++========================================================+=============+=========================+
+| Outside of the object's boundaries, and either inside | VT_EMPTY | None. |
+| or outside of the object's bounding rectangle. | | |
++--------------------------------------------------------+-------------+-------------------------+
+| Within the object but not within a child element or a | VT_I4 | lVal is CHILDID_SELF |
+| child object. | | |
++--------------------------------------------------------+-------------+-------------------------+
+| Within a child element. | VT_I4 | lVal contains |
+| | | the child ID. |
++--------------------------------------------------------+-------------+-------------------------+
+| Within a child object. | VT_DISPATCH | pdispVal is set to the |
+| | | child object's IDispatch|
+| | | interface pointer |
++--------------------------------------------------------+-------------+-------------------------+
+*/
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accHitTest(long xLeft, long yTop, VARIANT *pvarID)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QAccessibleInterface *child = accessible->childAt(xLeft, yTop);
+ if (child == 0) {
+ // no child found, return this item if it contains the coordinates
+ if (accessible->rect().contains(xLeft, yTop)) {
+ (*pvarID).vt = VT_I4;
+ (*pvarID).lVal = CHILDID_SELF;
+ return S_OK;
+ }
+ } else {
+ IAccessible *iface = QWindowsAccessibility::wrap(child);
+ if (iface) {
+ (*pvarID).vt = VT_DISPATCH;
+ (*pvarID).pdispVal = iface;
+ return S_OK;
+ }
+ }
+
+ // Did not find anything
+ (*pvarID).vt = VT_EMPTY;
+ return S_FALSE;
+}
+
+/*
+ It is recommended to read
+ "Implementing a Microsoft Active Accessibility (MSAA) Server.
+ Practical Tips for Developers and How Mozilla Does It"
+ (https://developer.mozilla.org/En/Accessibility/Implementing_an_MSAA_Server)
+
+ to get an overview of what's important to implement and what parts of MSAA
+ can be ignored. All stuff prefixed with "moz" are information from that page.
+*/
+// moz: [important]
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QRect rect;
+ if (varID.lVal) {
+ QAIPointer child = QAIPointer(accessible->child(varID.lVal - 1));
+ if (child->isValid())
+ rect = child->rect();
+ } else {
+ rect = accessible->rect();
+ }
+
+ *pxLeft = rect.x();
+ *pyTop = rect.y();
+ *pcxWidth = rect.width();
+ *pcyHeight = rect.height();
+
+ return S_OK;
+}
+
+// moz: [important, but no need to implement up/down/left/right]
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QAccessibleInterface *acc = 0;
+ switch (navDir) {
+ case NAVDIR_FIRSTCHILD:
+ acc = accessible->child(0);
+ break;
+ case NAVDIR_LASTCHILD:
+ acc = accessible->child(accessible->childCount() - 1);
+ break;
+ case NAVDIR_NEXT:
+ case NAVDIR_PREVIOUS:
+ if (!varStart.lVal){
+ QAccessibleInterface *parent = accessible->parent();
+ if (parent) {
+ int index = parent->indexOfChild(accessible);
+ index += (navDir == NAVDIR_NEXT) ? 1 : -1;
+ if (index >= 0 && index < parent->childCount())
+ acc = parent->child(index);
+ delete parent;
+ }
+ } else {
+ int index = varStart.lVal;
+ index += (navDir == NAVDIR_NEXT) ? 1 : -1;
+ if (index > 0 && index <= accessible->childCount())
+ acc = accessible->child(index - 1);
+ }
+ break;
+
+ // Geometrical
+ case NAVDIR_UP:
+ case NAVDIR_DOWN:
+ case NAVDIR_LEFT:
+ case NAVDIR_RIGHT:
+ if (QAccessibleInterface *pIface = accessible->parent()) {
+ const int indexOfOurself = pIface->indexOfChild(accessible);
+ QRect startg = accessible->rect();
+ QPoint startc = startg.center();
+ QAccessibleInterface *candidate = 0;
+ unsigned mindist = UINT_MAX; // will work on screen sizes at least up to 46340x46340
+ const int sibCount = pIface->childCount();
+ for (int i = 0; i < sibCount; ++i) {
+ QAccessibleInterface *sibling = 0;
+ sibling = pIface->child(i);
+ Q_ASSERT(sibling);
+ if (i == indexOfOurself || sibling->state().invisible) {
+ //ignore ourself and invisible siblings
+ delete sibling;
+ continue;
+ }
+
+ QRect sibg = sibling->rect();
+ QPoint sibc = sibg.center();
+ QPoint sibp;
+ QPoint startp;
+ QPoint distp;
+ switch (navDir) {
+ case NAVDIR_LEFT:
+ startp = QPoint(startg.left(), startg.top() + startg.height() / 2);
+ sibp = QPoint(sibg.right(), sibg.top() + sibg.height() / 2);
+ if (QPoint(sibc - startc).x() >= 0) {
+ delete sibling;
+ continue;
+ }
+ distp = sibp - startp;
+ break;
+ case NAVDIR_RIGHT:
+ startp = QPoint(startg.right(), startg.top() + startg.height() / 2);
+ sibp = QPoint(sibg.left(), sibg.top() + sibg.height() / 2);
+ if (QPoint(sibc - startc).x() <= 0) {
+ delete sibling;
+ continue;
+ }
+ distp = sibp - startp;
+ break;
+ case NAVDIR_UP:
+ startp = QPoint(startg.left() + startg.width() / 2, startg.top());
+ sibp = QPoint(sibg.left() + sibg.width() / 2, sibg.bottom());
+ if (QPoint(sibc - startc).y() >= 0) {
+ delete sibling;
+ continue;
+ }
+ distp = sibp - startp;
+ break;
+ case NAVDIR_DOWN:
+ startp = QPoint(startg.left() + startg.width() / 2, startg.bottom());
+ sibp = QPoint(sibg.left() + sibg.width() / 2, sibg.top());
+ if (QPoint(sibc - startc).y() <= 0) {
+ delete sibling;
+ continue;
+ }
+ distp = sibp - startp;
+ break;
+ default:
+ break;
+ }
+
+ // Since we're *comparing* (and not measuring) distances, we can compare the
+ // squared distance, (thus, no need to take the sqrt()).
+ unsigned dist = distp.x() * distp.x() + distp.y() * distp.y();
+ if (dist < mindist) {
+ delete candidate;
+ candidate = sibling;
+ mindist = dist;
+ } else {
+ delete sibling;
+ }
+ }
+ delete pIface;
+ acc = candidate;
+ }
+ break;
+ default:
+ break;
+ }
+ if (!acc) {
+ (*pvarEnd).vt = VT_EMPTY;
+ return S_FALSE;
+ }
+
+ if (IAccessible *iface = QWindowsAccessibility::wrap(acc)) {
+ (*pvarEnd).vt = VT_DISPATCH;
+ (*pvarEnd).pdispVal = iface;
+ return S_OK;
+ } else {
+ if (acc != accessible)
+ delete acc;
+ }
+
+ (*pvarEnd).vt = VT_EMPTY;
+ return S_FALSE;
+}
+
+// moz: [important]
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accChild(VARIANT varChildID, IDispatch** ppdispChild)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ if (varChildID.vt != VT_I4)
+ return E_INVALIDARG;
+
+
+ int childIndex = varChildID.lVal;
+ QAccessibleInterface *acc = 0;
+
+ if (childIndex < 0) {
+ const int entry = childIndex;
+ QPair<QObject*, int> ref = QWindowsAccessibility::getCachedObject(entry);
+ if (ref.first) {
+ acc = QAccessible::queryAccessibleInterface(ref.first);
+ if (acc && ref.second >= 0) {
+ QAccessibleInterface *res = acc->child(ref.second);
+ delete acc;
+ if (!res)
+ return E_INVALIDARG;
+ acc = res;
+ }
+ } else {
+ qWarning("get_accChild got a negative varChildID, but did not find it in cache");
+ }
+ } else {
+ if (childIndex) {
+ acc = accessible->child(childIndex - 1);
+ } else {
+ // Yes, some AT clients (Active Accessibility Object Inspector)
+ // actually ask for the same object. As a consequence, we need to clone ourselves:
+ if (QAccessibleInterface *par = accessible->parent()) {
+ const int indexOf = par->indexOfChild(accessible);
+ QAccessibleInterface *clone = par->child(indexOf);
+ delete par;
+ acc = clone;
+ }
+ }
+ }
+
+ if (acc) {
+ *ppdispChild = QWindowsAccessibility::wrap(acc);
+ return S_OK;
+ }
+
+ return E_FAIL;
+}
+
+// moz: [important]
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accChildCount(long* pcountChildren)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ *pcountChildren = accessible->childCount();
+ return S_OK;
+}
+
+// moz: [important]
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accParent(IDispatch** ppdispParent)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QAccessibleInterface *acc = accessible->parent();
+ if (acc) {
+ if (IAccessible *iface = QWindowsAccessibility::wrap(acc)) {
+ *ppdispParent = iface;
+ return S_OK;
+ } else {
+ delete acc;
+ }
+ }
+
+ *ppdispParent = 0;
+ return S_FALSE;
+}
+
+/*
+ Properties and methods
+*/
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accDoDefaultAction(VARIANT varID)
+{
+ Q_UNUSED(varID);
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ if (QAccessibleActionInterface *actionIface = accessible->actionInterface()) {
+ const QString def = actionIface->actionNames().value(0);
+ if (!def.isEmpty()) {
+ actionIface->doAction(def);
+ return S_OK;
+ }
+ }
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction)
+{
+ Q_UNUSED(varID);
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ *pszDefaultAction = 0;
+ if (QAccessibleActionInterface *actionIface = accessible->actionInterface()) {
+ const QString def = actionIface->actionNames().value(0);
+ if (!def.isEmpty())
+ *pszDefaultAction = QStringToBSTR(def);
+ }
+ return *pszDefaultAction ? S_OK : S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accDescription(VARIANT varID, BSTR* pszDescription)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+
+ QString descr;
+ if (varID.lVal) {
+ QAIPointer child = childPointer(varID);
+ if (!child)
+ return E_FAIL;
+ descr = child->text(QAccessible::Description);
+ } else {
+ descr = accessible->text(QAccessible::Description);
+ }
+ if (descr.size()) {
+ *pszDescription = QStringToBSTR(descr);
+ return S_OK;
+ }
+
+ *pszDescription = 0;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accHelp(VARIANT varID, BSTR *pszHelp)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QString help;
+ if (varID.lVal) {
+ QAIPointer child = childPointer(varID);
+ if (!child)
+ return E_FAIL;
+ help = child->text(QAccessible::Help);
+ } else {
+ help = accessible->text(QAccessible::Help);
+ }
+ if (help.size()) {
+ *pszHelp = QStringToBSTR(help);
+ return S_OK;
+ }
+
+ *pszHelp = 0;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accHelpTopic(BSTR *, VARIANT, long *)
+{
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accKeyboardShortcut(VARIANT varID, BSTR *pszKeyboardShortcut)
+{
+ Q_UNUSED(varID);
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ *pszKeyboardShortcut = 0;
+ if (QAccessibleActionInterface *actionIface = accessible->actionInterface()) {
+ const QString def = actionIface->actionNames().value(0);
+ if (!def.isEmpty()) {
+ const QString keyBoardShortCut = actionIface->keyBindingsForAction(def).value(0);
+ if (!keyBoardShortCut.isEmpty())
+ *pszKeyboardShortcut = QStringToBSTR(keyBoardShortCut);
+ }
+ }
+ return *pszKeyboardShortcut ? S_OK : S_FALSE;
+}
+
+static QAccessibleInterface *relatedInterface(QAccessibleInterface *iface, QAccessible::RelationFlag flag)
+{
+ typedef QPair<QAccessibleInterface *, QAccessible::Relation> RelationPair;
+ QVector<RelationPair> rels = iface->relations(flag);
+
+ for (int i = 1; i < rels.count(); ++i)
+ delete rels.at(i).first;
+
+ return rels.value(0).first;
+}
+
+// moz: [important]
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accName(VARIANT varID, BSTR* pszName)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QString name;
+ if (varID.lVal) {
+ QAIPointer child = childPointer(varID);
+ if (!child)
+ return E_FAIL;
+ name = child->text(QAccessible::Name);
+ if (name.isEmpty()) {
+ if (QAccessibleInterface *labelInterface = relatedInterface(child.data(), QAccessible::Label)) {
+ name = labelInterface->text(QAccessible::Name);
+ delete labelInterface;
+ }
+ }
+ } else {
+ name = accessible->text(QAccessible::Name);
+ if (name.isEmpty()) {
+ if (QAccessibleInterface *labelInterface = relatedInterface(accessible, QAccessible::Label)) {
+ name = labelInterface->text(QAccessible::Name);
+ delete labelInterface;
+ }
+ }
+ }
+ if (name.size()) {
+ *pszName = QStringToBSTR(name);
+ return S_OK;
+ }
+
+ *pszName = 0;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::put_accName(VARIANT, BSTR)
+{
+ accessibleDebugClientCalls(accessible);
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+// moz: [important]
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accRole(VARIANT varID, VARIANT *pvarRole)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QAccessible::Role role;
+ if (varID.lVal) {
+ QAIPointer child = childPointer(varID);
+ if (!child)
+ return E_FAIL;
+ role = child->role();
+ } else {
+ role = accessible->role();
+ }
+
+ if (role != QAccessible::NoRole) {
+ if (role == QAccessible::LayeredPane)
+ role = QAccessible::Pane;
+ (*pvarRole).vt = VT_I4;
+ (*pvarRole).lVal = role;
+ } else {
+ (*pvarRole).vt = VT_EMPTY;
+ }
+ return S_OK;
+}
+
+// moz: [important]
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accState(VARIANT varID, VARIANT *pvarState)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ QAccessible::State state;
+ if (varID.lVal) {
+ QAIPointer child = childPointer(varID);
+ if (!child.data())
+ return E_FAIL;
+ state = child->state();
+ } else {
+ state = accessible->state();
+ }
+
+ LONG st = 0;
+ if (state.animated)
+ st |= STATE_SYSTEM_ANIMATED;
+ if (state.busy)
+ st |= STATE_SYSTEM_BUSY;
+ if (state.checked)
+ st |= STATE_SYSTEM_CHECKED;
+ if (state.collapsed)
+ st |= STATE_SYSTEM_COLLAPSED;
+ if (state.defaultButton)
+ st |= STATE_SYSTEM_DEFAULT;
+ if (state.expanded)
+ st |= STATE_SYSTEM_EXPANDED;
+ if (state.extSelectable)
+ st |= STATE_SYSTEM_EXTSELECTABLE;
+ if (state.focusable)
+ st |= STATE_SYSTEM_FOCUSABLE;
+ if (state.focused)
+ st |= STATE_SYSTEM_FOCUSED;
+ if (state.hasPopup)
+ st |= STATE_SYSTEM_HASPOPUP;
+ if (state.hotTracked)
+ st |= STATE_SYSTEM_HOTTRACKED;
+ if (state.invisible)
+ st |= STATE_SYSTEM_INVISIBLE;
+ if (state.linked)
+ st |= STATE_SYSTEM_LINKED;
+ if (state.marqueed)
+ st |= STATE_SYSTEM_MARQUEED;
+ if (state.checkStateMixed)
+ st |= STATE_SYSTEM_MIXED;
+ if (state.movable)
+ st |= STATE_SYSTEM_MOVEABLE;
+ if (state.multiSelectable)
+ st |= STATE_SYSTEM_MULTISELECTABLE;
+ if (state.offscreen)
+ st |= STATE_SYSTEM_OFFSCREEN;
+ if (state.pressed)
+ st |= STATE_SYSTEM_PRESSED;
+ if (state.passwordEdit)
+ st |= STATE_SYSTEM_PROTECTED;
+ if (state.readOnly)
+ st |= STATE_SYSTEM_READONLY;
+ if (state.selectable)
+ st |= STATE_SYSTEM_SELECTABLE;
+ if (state.selected)
+ st |= STATE_SYSTEM_SELECTED;
+ if (state.selfVoicing)
+ st |= STATE_SYSTEM_SELFVOICING;
+ if (state.sizeable)
+ st |= STATE_SYSTEM_SIZEABLE;
+ if (state.traversed)
+ st |= STATE_SYSTEM_TRAVERSED;
+
+ (*pvarState).vt = VT_I4;
+ (*pvarState).lVal = st;
+ return S_OK;
+}
+
+// moz: [important]
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accValue(VARIANT varID, BSTR* pszValue)
+{
+ accessibleDebugClientCalls(accessible);
+ if (varID.vt != VT_I4)
+ return E_INVALIDARG;
+
+ if (!accessible->isValid() || varID.lVal) {
+ return E_FAIL;
+ }
+
+ QString value;
+ if (accessible->valueInterface()) {
+ value = QString::number(accessible->valueInterface()->currentValue().toDouble());
+ } else {
+ value = accessible->text(QAccessible::Value);
+ }
+ if (!value.isNull()) {
+ *pszValue = QStringToBSTR(value);
+ return S_OK;
+ }
+
+ *pszValue = 0;
+ accessibleDebug("return S_FALSE");
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::put_accValue(VARIANT, BSTR)
+{
+ accessibleDebugClientCalls(accessible);
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+// moz: [important]
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accSelect(long flagsSelect, VARIANT varID)
+{
+ Q_UNUSED(flagsSelect);
+ Q_UNUSED(varID);
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ bool res = false;
+
+/*
+ ### Check for accessibleTableInterface() or accessibleTextInterface()
+
+ ### and if there are no ia2 interfaces we should do nothing??
+ if (flagsSelect & SELFLAG_TAKEFOCUS)
+ res = accessible->doAction(SetFocus, varID.lVal, QVariantList());
+ if (flagsSelect & SELFLAG_TAKESELECTION) {
+ accessible->doAction(ClearSelection, 0, QVariantList());
+ res = accessible->doAction(AddToSelection, varID.lVal, QVariantList());
+ }
+ if (flagsSelect & SELFLAG_EXTENDSELECTION)
+ res = accessible->doAction(ExtendSelection, varID.lVal, QVariantList());
+ if (flagsSelect & SELFLAG_ADDSELECTION)
+ res = accessible->doAction(AddToSelection, varID.lVal, QVariantList());
+ if (flagsSelect & SELFLAG_REMOVESELECTION)
+ res = accessible->doAction(RemoveSelection, varID.lVal, QVariantList());
+*/
+ return res ? S_OK : S_FALSE;
+}
+
+/*!
+ \internal
+ Can return:
+
+ +-------------+------------------------------------------------------------------------------+
+ | VT_EMPTY | None. Neither this object nor any of its children has the keyboard focus. |
+ +-------------+------------------------------------------------------------------------------+
+ | VT_I4 | lVal is CHILDID_SELF. The object itself has the keyboard focus. |
+ +-------------+------------------------------------------------------------------------------+
+ | VT_I4 | lVal contains the child ID of the child element that has the keyboard focus. |
+ +-------------+------------------------------------------------------------------------------+
+ | VT_DISPATCH | pdispVal member is the address of the IDispatch interface for the child |
+ | | object that has the keyboard focus. |
+ +-------------+------------------------------------------------------------------------------+
+ moz: [important]
+*/
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accFocus(VARIANT *pvarID)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ if (QAccessibleInterface *acc = accessible->focusChild()) {
+ if (compareAccessible(acc, accessible)) {
+ (*pvarID).vt = VT_I4;
+ (*pvarID).lVal = CHILDID_SELF;
+ delete acc;
+ return S_OK;
+ } else {
+ if (IAccessible *iface = QWindowsAccessibility::wrap(acc)) {
+ (*pvarID).vt = VT_DISPATCH;
+ (*pvarID).pdispVal = iface;
+ return S_OK;
+ }
+ }
+ delete acc;
+ }
+ (*pvarID).vt = VT_EMPTY;
+ return S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accSelection(VARIANT *pvarChildren)
+{
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+
+ int cc = accessible->childCount();
+ QVector<int> sel(cc);
+ int selIndex = 0;
+ for (int i = 0; i < cc; ++i) {
+ bool isSelected = false;
+ QAccessibleInterface *child = accessible->child(i);
+ if (child) {
+ isSelected = child->state().selected;
+ delete child;
+ }
+ if (isSelected)
+ sel[selIndex++] = i+1;
+ }
+ sel.resize(selIndex);
+ if (sel.isEmpty()) {
+ (*pvarChildren).vt = VT_EMPTY;
+ return S_FALSE;
+ }
+ if (sel.size() == 1) {
+ (*pvarChildren).vt = VT_I4;
+ (*pvarChildren).lVal = sel[0];
+ return S_OK;
+ }
+ IEnumVARIANT *iface = new QWindowsEnumerate(sel);
+ IUnknown *uiface;
+ iface->QueryInterface(IID_IUnknown, (void**)&uiface);
+ (*pvarChildren).vt = VT_UNKNOWN;
+ (*pvarChildren).punkVal = uiface;
+
+ return S_OK;
+}
+
+/**************************************************************\
+ * IOleWindow *
+ **************************************************************/
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::GetWindow(HWND *phwnd)
+{
+ *phwnd = 0;
+ accessibleDebugClientCalls(accessible);
+ if (!accessible->isValid())
+ return E_FAIL;
+ if (!accessible->isValid())
+ return E_UNEXPECTED;
+
+ QWindow *window = QWindowsAccessibility::windowHelper(accessible);
+ if (!window)
+ return E_FAIL;
+
+ QPlatformNativeInterface *platform = QGuiApplication::platformNativeInterface();
+ Q_ASSERT(platform);
+ *phwnd = (HWND)platform->nativeResourceForWindow("handle", window);
+ accessibleDebug("QWindowsAccessible::GetWindow(): %p", *phwnd);
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::ContextSensitiveHelp(BOOL)
+{
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_ACCESSIBILITY