From 02d75eaaf9f2e4984f39b1e081e841966d1dde0c Mon Sep 17 00:00:00 2001 From: Jan-Arve Saether Date: Thu, 6 Oct 2011 09:41:34 +0200 Subject: Add support for IAccessible2 on Windows Change-Id: Ia955ab46dc5037ed1c74e0acc525e98b02552c97 Reviewed-by: Frederik Gladhorn --- .../platforms/windows/accessible/accessible.pri | 15 + .../platforms/windows/accessible/comutils.cpp | 641 ++++++++ .../platforms/windows/accessible/comutils.h | 61 + .../platforms/windows/accessible/iaccessible2.cpp | 1467 +++++++++++++++++++ .../platforms/windows/accessible/iaccessible2.h | 375 +++++ .../windows/accessible/qwindowsaccessibility.cpp | 301 ++++ .../windows/accessible/qwindowsaccessibility.h | 73 + .../windows/accessible/qwindowsmsaaaccessible.cpp | 1228 ++++++++++++++++ .../windows/accessible/qwindowsmsaaaccessible.h | 138 ++ .../platforms/windows/qwindowsaccessibility.cpp | 1525 -------------------- .../platforms/windows/qwindowsaccessibility.h | 67 - src/plugins/platforms/windows/qwindowscontext.cpp | 2 +- .../platforms/windows/qwindowsintegration.cpp | 2 +- src/plugins/platforms/windows/windows.pro | 3 +- 14 files changed, 4302 insertions(+), 1596 deletions(-) create mode 100644 src/plugins/platforms/windows/accessible/accessible.pri create mode 100644 src/plugins/platforms/windows/accessible/comutils.cpp create mode 100644 src/plugins/platforms/windows/accessible/comutils.h create mode 100644 src/plugins/platforms/windows/accessible/iaccessible2.cpp create mode 100644 src/plugins/platforms/windows/accessible/iaccessible2.h create mode 100644 src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp create mode 100644 src/plugins/platforms/windows/accessible/qwindowsaccessibility.h create mode 100644 src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp create mode 100644 src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.h delete mode 100644 src/plugins/platforms/windows/qwindowsaccessibility.cpp delete mode 100644 src/plugins/platforms/windows/qwindowsaccessibility.h (limited to 'src/plugins') diff --git a/src/plugins/platforms/windows/accessible/accessible.pri b/src/plugins/platforms/windows/accessible/accessible.pri new file mode 100644 index 0000000000..bf25ad870c --- /dev/null +++ b/src/plugins/platforms/windows/accessible/accessible.pri @@ -0,0 +1,15 @@ + +SOURCES += \ + $$PWD/qwindowsmsaaaccessible.cpp \ + $$PWD/qwindowsaccessibility.cpp \ + $$PWD/iaccessible2.cpp \ + $$PWD/comutils.cpp + +HEADERS += \ + $$PWD/qwindowsmsaaaccessible.h \ + $$PWD/qwindowsaccessibility.h \ + $$PWD/iaccessible2.h \ + $$PWD/comutils.h + + +include(../../../../3rdparty/iaccessible2/iaccessible2.pri) diff --git a/src/plugins/platforms/windows/accessible/comutils.cpp b/src/plugins/platforms/windows/accessible/comutils.cpp new file mode 100644 index 0000000000..9a0fce20b8 --- /dev/null +++ b/src/plugins/platforms/windows/accessible/comutils.cpp @@ -0,0 +1,641 @@ +/**************************************************************************** +** +** 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 +#include + +#include "comutils.h" +#include +#include +#include + + +#include +#include +#include + +static DATE QDateTimeToDATE(const QDateTime &dt) +{ + if (!dt.isValid() || dt.isNull()) + return 949998; // Special value for no date (01/01/4501) + + SYSTEMTIME stime; + memset(&stime, 0, sizeof(stime)); + QDate date = dt.date(); + QTime time = dt.time(); + if (date.isValid() && !date.isNull()) { + stime.wDay = date.day(); + stime.wMonth = date.month(); + stime.wYear = date.year(); + } + if (time.isValid() && !time.isNull()) { + stime.wMilliseconds = time.msec(); + stime.wSecond = time.second(); + stime.wMinute = time.minute(); + stime.wHour = time.hour(); + } + + double vtime; + SystemTimeToVariantTime(&stime, &vtime); + + return vtime; +} + +inline uint QColorToOLEColor(const QColor &col) +{ + return qRgba(col.blue(), col.green(), col.red(), 0x00); +} + +bool QVariantToVARIANT(const QVariant &var, VARIANT &arg, const QByteArray &typeName, bool out) +{ + QVariant qvar = var; + // "type" is the expected type, so coerce if necessary + QVariant::Type proptype = typeName.isEmpty() ? QVariant::Invalid : QVariant::nameToType(typeName); + if (proptype == QVariant::UserType && !typeName.isEmpty()) { + if (typeName == "short" || typeName == "char") + proptype = QVariant::Int; + else if (typeName == "float") + proptype = QVariant::Double; + } + if (proptype != QVariant::Invalid && proptype != QVariant::UserType && proptype != qvar.type()) { + if (qvar.canConvert(proptype)) + qvar.convert(proptype); + else + qvar = QVariant(proptype); + } + + if (out && arg.vt == (VT_VARIANT|VT_BYREF) && arg.pvarVal) { + return QVariantToVARIANT(var, *arg.pvarVal, typeName, false); + } + + if (out && proptype == QVariant::UserType && typeName == "QVariant") { + VARIANT *pVariant = new VARIANT; + QVariantToVARIANT(var, *pVariant, QByteArray(), false); + arg.vt = VT_VARIANT|VT_BYREF; + arg.pvarVal = pVariant; + return true; + } + + switch ((int)qvar.type()) { + case QVariant::String: + if (out && arg.vt == (VT_BSTR|VT_BYREF)) { + if (*arg.pbstrVal) + SysFreeString(*arg.pbstrVal); + *arg.pbstrVal = QStringToBSTR(qvar.toString()); + arg.vt = VT_BSTR|VT_BYREF; + } else { + arg.vt = VT_BSTR; + arg.bstrVal = QStringToBSTR(qvar.toString()); + if (out) { + arg.pbstrVal = new BSTR(arg.bstrVal); + arg.vt |= VT_BYREF; + } + } + break; + + case QVariant::Int: + if (out && arg.vt == (VT_I4|VT_BYREF)) { + *arg.plVal = qvar.toInt(); + } else { + arg.vt = VT_I4; + arg.lVal = qvar.toInt(); + if (out) { + if (typeName == "short") { + arg.vt = VT_I2; + arg.piVal = new short(arg.lVal); + } else if (typeName == "char") { + arg.vt = VT_I1; + arg.pcVal= new char(arg.lVal); + } else { + arg.plVal = new long(arg.lVal); + } + arg.vt |= VT_BYREF; + } + } + break; + + case QVariant::UInt: + if (out && (arg.vt == (VT_UINT|VT_BYREF) || arg.vt == (VT_I4|VT_BYREF))) { + *arg.puintVal = qvar.toUInt(); + } else { + arg.vt = VT_UINT; + arg.uintVal = qvar.toUInt(); + if (out) { + arg.puintVal = new uint(arg.uintVal); + arg.vt |= VT_BYREF; + } + } + break; + + case QVariant::LongLong: + if (out && arg.vt == (VT_CY|VT_BYREF)) { + arg.pcyVal->int64 = qvar.toLongLong(); +#if !defined(Q_OS_WINCE) && defined(_MSC_VER) && _MSC_VER >= 1400 + } else if (out && arg.vt == (VT_I8|VT_BYREF)) { + *arg.pllVal = qvar.toLongLong(); + } else { + arg.vt = VT_I8; + arg.llVal = qvar.toLongLong(); + if (out) { + arg.pllVal = new LONGLONG(arg.llVal); + arg.vt |= VT_BYREF; + } + } +#else + } else { + arg.vt = VT_CY; + arg.cyVal.int64 = qvar.toLongLong(); + if (out) { + arg.pcyVal = new CY(arg.cyVal); + arg.vt |= VT_BYREF; + } + } +#endif + break; + + case QVariant::ULongLong: + if (out && arg.vt == (VT_CY|VT_BYREF)) { + arg.pcyVal->int64 = qvar.toULongLong(); +#if !defined(Q_OS_WINCE) && defined(_MSC_VER) && _MSC_VER >= 1400 + } else if (out && arg.vt == (VT_UI8|VT_BYREF)) { + *arg.pullVal = qvar.toULongLong(); + } else { + arg.vt = VT_UI8; + arg.ullVal = qvar.toULongLong(); + if (out) { + arg.pullVal = new ULONGLONG(arg.ullVal); + arg.vt |= VT_BYREF; + } + } +#else + } else { + arg.vt = VT_CY; + arg.cyVal.int64 = qvar.toULongLong(); + if (out) { + arg.pcyVal = new CY(arg.cyVal); + arg.vt |= VT_BYREF; + } + } + +#endif + + break; + + case QVariant::Bool: + if (out && arg.vt == (VT_BOOL|VT_BYREF)) { + *arg.pboolVal = qvar.toBool() ? VARIANT_TRUE : VARIANT_FALSE; + } else { + arg.vt = VT_BOOL; + arg.boolVal = qvar.toBool() ? VARIANT_TRUE : VARIANT_FALSE; + if (out) { + arg.pboolVal = new short(arg.boolVal); + arg.vt |= VT_BYREF; + } + } + break; + case QVariant::Double: + if (out && arg.vt == (VT_R8|VT_BYREF)) { + *arg.pdblVal = qvar.toDouble(); + } else { + arg.vt = VT_R8; + arg.dblVal = qvar.toDouble(); + if (out) { + if (typeName == "float") { + arg.vt = VT_R4; + arg.pfltVal = new float(arg.dblVal); + } else { + arg.pdblVal = new double(arg.dblVal); + } + arg.vt |= VT_BYREF; + } + } + break; + case QVariant::Color: + if (out && arg.vt == (VT_COLOR|VT_BYREF)) { + + *arg.plVal = QColorToOLEColor(qvariant_cast(qvar)); + } else { + arg.vt = VT_COLOR; + arg.lVal = QColorToOLEColor(qvariant_cast(qvar)); + if (out) { + arg.plVal = new long(arg.lVal); + arg.vt |= VT_BYREF; + } + } + break; + + case QVariant::Date: + case QVariant::Time: + case QVariant::DateTime: + if (out && arg.vt == (VT_DATE|VT_BYREF)) { + *arg.pdate = QDateTimeToDATE(qvar.toDateTime()); + } else { + arg.vt = VT_DATE; + arg.date = QDateTimeToDATE(qvar.toDateTime()); + if (out) { + arg.pdate = new DATE(arg.date); + arg.vt |= VT_BYREF; + } + } + break; +#if 0 // not a value with min/max semantics + case QVariant::Font: + if (out && arg.vt == (VT_DISPATCH|VT_BYREF)) { + if (*arg.ppdispVal) + (*arg.ppdispVal)->Release(); + *arg.ppdispVal = QFontToIFont(qvariant_cast(qvar)); + } else { + arg.vt = VT_DISPATCH; + arg.pdispVal = QFontToIFont(qvariant_cast(qvar)); + if (out) { + arg.ppdispVal = new IDispatch*(arg.pdispVal); + arg.vt |= VT_BYREF; + } + } + break; + case QVariant::Pixmap: + if (out && arg.vt == (VT_DISPATCH|VT_BYREF)) { + if (*arg.ppdispVal) + (*arg.ppdispVal)->Release(); + *arg.ppdispVal = QPixmapToIPicture(qvariant_cast(qvar)); + } else { + arg.vt = VT_DISPATCH; + arg.pdispVal = QPixmapToIPicture(qvariant_cast(qvar)); + if (out) { + arg.ppdispVal = new IDispatch*(arg.pdispVal); + arg.vt |= VT_BYREF; + } + } + break; + case QVariant::Cursor: + { +#ifndef QT_NO_CURSOR + int shape = qvariant_cast(qvar).shape(); + if (out && (arg.vt & VT_BYREF)) { + switch (arg.vt & ~VT_BYREF) { + case VT_I4: + *arg.plVal = shape; + break; + case VT_I2: + *arg.piVal = shape; + break; + case VT_UI4: + *arg.pulVal = shape; + break; + case VT_UI2: + *arg.puiVal = shape; + break; + case VT_INT: + *arg.pintVal = shape; + break; + case VT_UINT: + *arg.puintVal = shape; + break; + } + } else { + arg.vt = VT_I4; + arg.lVal = shape; + if (out) { + arg.plVal = new long(arg.lVal); + arg.vt |= VT_BYREF; + } + } +#endif + } + break; + + case QVariant::List: + { + const QList list = qvar.toList(); + const int count = list.count(); + VARTYPE vt = VT_VARIANT; + QVariant::Type listType = QVariant::LastType; // == QVariant + if (!typeName.isEmpty() && typeName.startsWith("QList<")) { + const QByteArray listTypeName = typeName.mid(6, typeName.length() - 7); // QList -> int + listType = QVariant::nameToType(listTypeName); + } + + VARIANT variant; + void *pElement = &variant; + switch (listType) { + case QVariant::Int: + vt = VT_I4; + pElement = &variant.lVal; + break; + case QVariant::Double: + vt = VT_R8; + pElement = &variant.dblVal; + break; + case QVariant::DateTime: + vt = VT_DATE; + pElement = &variant.date; + break; + case QVariant::Bool: + vt = VT_BOOL; + pElement = &variant.boolVal; + break; + case QVariant::LongLong: +#if !defined(Q_OS_WINCE) && defined(_MSC_VER) && _MSC_VER >= 1400 + vt = VT_I8; + pElement = &variant.llVal; +#else + vt = VT_CY; + pElement = &variant.cyVal; +#endif + break; + default: + break; + } + SAFEARRAY *array = 0; + bool is2D = false; + // If the first element in the array is a list the whole list is + // treated as a 2D array. The column count is taken from the 1st element. + if (count) { + QVariantList col = list.at(0).toList(); + int maxColumns = col.count(); + if (maxColumns) { + is2D = true; + SAFEARRAYBOUND rgsabound[2] = { {0} }; + rgsabound[0].cElements = count; + rgsabound[1].cElements = maxColumns; + array = SafeArrayCreate(VT_VARIANT, 2, rgsabound); + LONG rgIndices[2]; + for (LONG i = 0; i < count; ++i) { + rgIndices[0] = i; + QVariantList columns = list.at(i).toList(); + int columnCount = qMin(maxColumns, columns.count()); + for (LONG j = 0; j < columnCount; ++j) { + QVariant elem = columns.at(j); + VariantInit(&variant); + QVariantToVARIANT(elem, variant, elem.typeName()); + rgIndices[1] = j; + SafeArrayPutElement(array, rgIndices, pElement); + clearVARIANT(&variant); + } + } + + } + } + if (!is2D) { + array = SafeArrayCreateVector(vt, 0, count); + for (LONG index = 0; index < count; ++index) { + QVariant elem = list.at(index); + if (listType != QVariant::LastType) + elem.convert(listType); + VariantInit(&variant); + QVariantToVARIANT(elem, variant, elem.typeName()); + SafeArrayPutElement(array, &index, pElement); + clearVARIANT(&variant); + } + } + if (out && arg.vt == (VT_ARRAY|vt|VT_BYREF)) { + if (*arg.pparray) + SafeArrayDestroy(*arg.pparray); + *arg.pparray = array; + } else { + arg.vt = VT_ARRAY|vt; + arg.parray = array; + if (out) { + arg.pparray = new SAFEARRAY*(arg.parray); + arg.vt |= VT_BYREF; + } + } + } + break; + + case QVariant::StringList: + { + const QStringList list = qvar.toStringList(); + const int count = list.count(); + SAFEARRAY *array = SafeArrayCreateVector(VT_BSTR, 0, count); + for (LONG index = 0; index < count; ++index) { + QString elem = list.at(index); + BSTR bstr = QStringToBSTR(elem); + SafeArrayPutElement(array, &index, bstr); + SysFreeString(bstr); + } + + if (out && arg.vt == (VT_ARRAY|VT_BSTR|VT_BYREF)) { + if (*arg.pparray) + SafeArrayDestroy(*arg.pparray); + *arg.pparray = array; + } else { + arg.vt = VT_ARRAY|VT_BSTR; + arg.parray = array; + if (out) { + arg.pparray = new SAFEARRAY*(arg.parray); + arg.vt |= VT_BYREF; + } + } + } + break; + + case QVariant::ByteArray: + { + const QByteArray bytes = qvar.toByteArray(); + const uint count = bytes.count(); + SAFEARRAY *array = SafeArrayCreateVector(VT_UI1, 0, count); + if (count) { + const char *data = bytes.constData(); + char *dest; + SafeArrayAccessData(array, (void **)&dest); + memcpy(dest, data, count); + SafeArrayUnaccessData(array); + } + + if (out && arg.vt == (VT_ARRAY|VT_UI1|VT_BYREF)) { + if (*arg.pparray) + SafeArrayDestroy(*arg.pparray); + *arg.pparray = array; + } else { + arg.vt = VT_ARRAY|VT_UI1; + arg.parray = array; + if (out) { + arg.pparray = new SAFEARRAY*(arg.parray); + arg.vt |= VT_BYREF; + } + } + } + break; + +#ifdef QAX_SERVER + case QVariant::Rect: + case QVariant::Size: + case QVariant::Point: + { + typedef HRESULT(WINAPI* PGetRecordInfoFromTypeInfo)(ITypeInfo *, IRecordInfo **); + static PGetRecordInfoFromTypeInfo pGetRecordInfoFromTypeInfo = 0; + static bool resolved = false; + if (!resolved) { + QSystemLibrary oleaut32(QLatin1String("oleaut32")); + pGetRecordInfoFromTypeInfo = (PGetRecordInfoFromTypeInfo)oleaut32.resolve("GetRecordInfoFromTypeInfo"); + resolved = true; + } + if (!pGetRecordInfoFromTypeInfo) + break; + + ITypeInfo *typeInfo = 0; + IRecordInfo *recordInfo = 0; + CLSID clsid = qvar.type() == QVariant::Rect ? CLSID_QRect + :qvar.type() == QVariant::Size ? CLSID_QSize + :CLSID_QPoint; + qAxTypeLibrary->GetTypeInfoOfGuid(clsid, &typeInfo); + if (!typeInfo) + break; + pGetRecordInfoFromTypeInfo(typeInfo, &recordInfo); + typeInfo->Release(); + if (!recordInfo) + break; + + void *record = 0; + switch (qvar.type()) { + case QVariant::Rect: + { + QRect qrect(qvar.toRect()); + recordInfo->RecordCreateCopy(&qrect, &record); + } + break; + case QVariant::Size: + { + QSize qsize(qvar.toSize()); + recordInfo->RecordCreateCopy(&qsize, &record); + } + break; + case QVariant::Point: + { + QPoint qpoint(qvar.toPoint()); + recordInfo->RecordCreateCopy(&qpoint, &record); + } + break; + } + + arg.vt = VT_RECORD; + arg.pRecInfo = recordInfo, + arg.pvRecord = record; + if (out) { + qWarning("QVariantToVARIANT: out-parameter not supported for records"); + return false; + } + } + break; +#endif // QAX_SERVER + case QVariant::UserType: + { + QByteArray subType = qvar.typeName(); +#ifdef QAX_SERVER + if (subType.endsWith('*')) + subType.truncate(subType.length() - 1); +#endif + if (!qstrcmp(qvar.typeName(), "IDispatch*")) { + arg.vt = VT_DISPATCH; + arg.pdispVal = *(IDispatch**)qvar.data(); + if (arg.pdispVal) + arg.pdispVal->AddRef(); + if (out) { + qWarning("QVariantToVARIANT: out-parameter not supported for IDispatch"); + return false; + } + } else if (!qstrcmp(qvar.typeName(), "IDispatch**")) { + arg.vt = VT_DISPATCH; + arg.ppdispVal = *(IDispatch***)qvar.data(); + if (out) + arg.vt |= VT_BYREF; + } else if (!qstrcmp(qvar.typeName(), "IUnknown*")) { + arg.vt = VT_UNKNOWN; + arg.punkVal = *(IUnknown**)qvar.data(); + if (arg.punkVal) + arg.punkVal->AddRef(); + if (out) { + qWarning("QVariantToVARIANT: out-parameter not supported for IUnknown"); + return false; + } +#ifdef QAX_SERVER + } else if (qAxFactory()->metaObject(QString::fromLatin1(subType.constData()))) { + arg.vt = VT_DISPATCH; + void *user = *(void**)qvar.constData(); +// qVariantGet(qvar, user, qvar.typeName()); + if (!user) { + arg.pdispVal = 0; + } else { + qAxFactory()->createObjectWrapper(static_cast(user), &arg.pdispVal); + } + if (out) { + qWarning("QVariantToVARIANT: out-parameter not supported for subtype"); + return false; + } +#else + } else if (QMetaType::type(subType)) { + QAxObject *object = *(QAxObject**)qvar.constData(); +// qVariantGet(qvar, object, subType); + arg.vt = VT_DISPATCH; + object->queryInterface(IID_IDispatch, (void**)&arg.pdispVal); + if (out) { + qWarning("QVariantToVARIANT: out-parameter not supported for subtype"); + return false; + } +#endif + } else { + return false; + } + } + break; +#endif + + case QVariant::Invalid: // default-parameters not set + if (out && arg.vt == (VT_ERROR|VT_BYREF)) { + *arg.plVal = DISP_E_PARAMNOTFOUND; + } else { + arg.vt = VT_ERROR; + arg.lVal = DISP_E_PARAMNOTFOUND; + if (out) { + arg.plVal = new long(arg.lVal); + arg.vt |= VT_BYREF; + } + } + break; + + default: + return false; + } + + Q_ASSERT(!out || (arg.vt & VT_BYREF)); + return true; +} + diff --git a/src/plugins/platforms/windows/accessible/comutils.h b/src/plugins/platforms/windows/accessible/comutils.h new file mode 100644 index 0000000000..08420cc46c --- /dev/null +++ b/src/plugins/platforms/windows/accessible/comutils.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#ifndef COMUTILS_H +#define COMUTILS_H + +#if !defined(_WINDOWS_) && !defined(_WINDOWS_H) && !defined(__WINDOWS__) +#error Must include windows.h first! +#endif + +#include +#include + +class QVariant; + +bool QVariantToVARIANT(const QVariant &var, VARIANT &arg, const QByteArray &typeName, bool out); + +inline BSTR QStringToBSTR(const QString &str) +{ + return SysAllocStringLen((OLECHAR*)str.unicode(), str.length()); +} + +#endif // COMUTILS_H + diff --git a/src/plugins/platforms/windows/accessible/iaccessible2.cpp b/src/plugins/platforms/windows/accessible/iaccessible2.cpp new file mode 100644 index 0000000000..f22349714f --- /dev/null +++ b/src/plugins/platforms/windows/accessible/iaccessible2.cpp @@ -0,0 +1,1467 @@ +/**************************************************************************** +** +** 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 +#ifndef QT_NO_ACCESSIBILITY + +#include "iaccessible2.h" +#include "qwindowsaccessibility.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/**************************************************************\ + * AccessibleApplication * + **************************************************************/ +// IUnknown +HRESULT STDMETHODCALLTYPE AccessibleApplication::QueryInterface(REFIID id, LPVOID *iface) +{ + *iface = 0; + if (id == IID_IUnknown) { + accessibleDebug("AccessibleApplication::QI(): IID_IUnknown"); + *iface = (IUnknown*)this; + } else if (id == IID_IAccessibleApplication) { + accessibleDebug("AccessibleApplication::QI(): IID_IAccessibleApplication"); + *iface = static_cast(this); + } + + if (*iface) { + AddRef(); + return S_OK; + } + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE AccessibleApplication::AddRef() +{ + return ++m_ref; +} + +ULONG STDMETHODCALLTYPE AccessibleApplication::Release() +{ + if (!--m_ref) { + delete this; + return 0; + } + return m_ref; +} + +/* IAccessibleApplication */ +HRESULT STDMETHODCALLTYPE AccessibleApplication::get_appName(/* [retval][out] */ BSTR *name) +{ + const QString appName = QGuiApplication::applicationName(); + *name = QStringToBSTR(appName); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE AccessibleApplication::get_appVersion(/* [retval][out] */ BSTR *version) +{ + const QString appName = QGuiApplication::applicationVersion(); + *version = QStringToBSTR(appName); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE AccessibleApplication::get_toolkitName(/* [retval][out] */ BSTR *name) +{ + *name = ::SysAllocString(L"Qt"); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE AccessibleApplication::get_toolkitVersion(/* [retval][out] */ BSTR *version) +{ + *version = ::SysAllocString(QT_UNICODE_LITERAL(QT_VERSION_STR)); + return S_OK; +} + + + +/*! + \internal + Client allocates and deallocates array + (see "Special Consideration when using Arrays", in Accessible2.idl) + */ +HRESULT STDMETHODCALLTYPE AccessibleRelation::get_target( + /* [in] */ long targetIndex, + /* [retval][out] */ IUnknown **target) +{ + if (targetIndex >= 0 && targetIndex < m_targets.count()) { + QAccessibleInterface *iface = m_targets.at(targetIndex); + *target = QWindowsAccessibility::wrap(iface); + if (*target) + return S_OK; + return E_FAIL; + } + return E_INVALIDARG; +} + +/*! + \internal + Client allocates and deallocates \a targets array + (see "Special Consideration when using Arrays", in Accessible2.idl) + */ +HRESULT STDMETHODCALLTYPE AccessibleRelation::get_targets( + /* [in] */ long maxTargets, // Hmmm, ignore ??? + /* [length_is][size_is][out] */ IUnknown **targets, + /* [retval][out] */ long *nTargets) +{ + + const int numTargets = qMin((int)maxTargets, m_targets.count()); + for (int i = 0; i < numTargets; ++i) { + QAccessibleInterface *iface = m_targets.at(i); + IAccessible *iacc = QWindowsAccessibility::wrap(iface); + if (!iacc) + return E_FAIL; + *targets = iacc; + ++targets; + } + *nTargets = numTargets; + // \a targets array is allocated by client. + return numTargets > 0 ? S_OK : S_FALSE; +} + + +/**************************************************************\ + * * + * IUnknown * + * * + **************************************************************/ +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::QueryInterface(REFIID id, LPVOID *iface) +{ + *iface = 0; + + QByteArray strIID = IIDToString(id); + if (!strIID.isEmpty()) { + QString ss; QDebug dbg(&ss); dbg << accessible; + accessibleDebug("QWindowsIA2Accessible::QI() - IID:%s, iface:%s ", strIID.constData(), qPrintable(ss)); + } + if (id == IID_IUnknown) { + *iface = (IUnknown*)(IDispatch*)this; + } else if (id == IID_IDispatch) { + *iface = (IDispatch*)this; + } else if (id == IID_IAccessible) { + *iface = (IAccessible*)this; + } else if (id == IID_IOleWindow) { + *iface = (IOleWindow*)this; + } else if (id == IID_IServiceProvider) { + *iface = (IServiceProvider*)this; + } else if (id == IID_IAccessible2) { + *iface = (IAccessible2*)this; + } else if (id == IID_IAccessibleAction) { + if (accessible->actionInterface()) + *iface = (IAccessibleAction*)this; + } else if (id == IID_IAccessibleComponent) { + *iface = (IAccessibleComponent*)this; + } else if (id == IID_IAccessibleEditableText) { + //if (accessible->editableTextInterface()) { + //*iface = (IAccessibleEditableText*)this; + //} + } else if (id == IID_IAccessibleHyperlink) { + //*iface = (IAccessibleHyperlink*)this; + } else if (id == IID_IAccessibleHypertext) { + //*iface = (IAccessibleHypertext*)this; + } else if (id == IID_IAccessibleImage) { + //*iface = (IAccessibleImage*)this; + } else if (id == IID_IAccessibleRelation) { + *iface = (IAccessibleRelation*)this; + } else if (id == IID_IAccessibleTable) { + //*iface = (IAccessibleTable*)this; // not supported + } else if (id == IID_IAccessibleTable2) { + if (accessible->tableInterface()) + *iface = (IAccessibleTable2*)this; + } else if (id == IID_IAccessibleTableCell) { + if (accessible->tableCellInterface()) + *iface = (IAccessibleTableCell*)this; + } else if (id == IID_IAccessibleText) { + if (accessible->textInterface()) + *iface = (IAccessibleText*)this; + } else if (id == IID_IAccessibleValue) { + if (accessible->valueInterface()) + *iface = (IAccessibleValue*)this; + } + if (*iface) { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE QWindowsIA2Accessible::AddRef() +{ + return ++ref; +} + +ULONG STDMETHODCALLTYPE QWindowsIA2Accessible::Release() +{ + if (!--ref) { + delete this; + return 0; + } + return ref; +} + + + +/**************************************************************\ + * * + * IAccessible2 * + * * + **************************************************************/ +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nRelations(long *nRelations) +{ + accessibleDebugClientCalls(accessible); + if (!nRelations) + return E_INVALIDARG; + if (!accessible->isValid()) + return E_FAIL; + + return getRelationsHelper(0, 0, 0, nRelations); +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_relation(long relationIndex, IAccessibleRelation **relation) +{ + accessibleDebugClientCalls(accessible); + if (!relation) + return E_INVALIDARG; + if (!accessible->isValid()) + return E_FAIL; + + return getRelationsHelper(relation, relationIndex, 1); +} + +/*! + \internal + Client allocates and deallocates array + (see "Special Consideration when using Arrays", in Accessible2.idl) + */ +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_relations(long maxRelations, + IAccessibleRelation **relations, + long *nRelations) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + return getRelationsHelper(relations, 0, maxRelations, nRelations); +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::role(long *ia2role) +{ + accessibleDebugClientCalls(accessible); + //### Change QAccessibleInterface::role() to return both MSAA and IA2 roles. + // When that is completed, we must patch the MSAA bridge not not return any + // IA2-specific roles from get_accRole(). + if (!accessible->isValid()) + return E_FAIL; + + *ia2role = accessible->role(); + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::scrollTo(enum IA2ScrollType /*scrollType*/) +{ + //### Ignore for now + return E_NOTIMPL; +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::scrollToPoint(enum IA2CoordinateType /*coordinateType*/, long /*x*/, long /*y*/) +{ + //### Ignore for now + return E_NOTIMPL; +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_groupPosition(long *groupLevel, + long *similarItemsInGroup, + long *positionInGroup) +{ + // ### Ignore for now. Not sure what this is used for..... + *groupLevel = 0; // Not applicable + *similarItemsInGroup = 0; // Not applicable + *positionInGroup = 0; // Not applicable + return S_FALSE; +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_states(AccessibleStates *states) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + if (!states) + return E_POINTER; + QAccessible::State st = accessible->state(); + AccessibleStates ia2states = 0; + if (st.active) + ia2states |= IA2_STATE_ACTIVE; + if (st.invalid) + ia2states |= IA2_STATE_DEFUNCT; + if (st.editable) + ia2states |= IA2_STATE_EDITABLE; + if (st.multiLine) + ia2states |= IA2_STATE_MULTI_LINE; + if (st.selectableText) + ia2states |= IA2_STATE_SELECTABLE_TEXT; + if (st.supportsAutoCompletion) + ia2states |= IA2_STATE_SUPPORTS_AUTOCOMPLETION; + + *states = ia2states; + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_extendedRole(BSTR *extendedRole) +{ + //### + *extendedRole = 0; + return E_NOTIMPL; // mozilla does this + //return S_FALSE; +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_localizedExtendedRole(BSTR *localizedExtendedRole) +{ + //### + *localizedExtendedRole = 0; + return E_NOTIMPL; // mozilla does this + //return S_FALSE; +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nExtendedStates(long *nExtendedStates) +{ + // Who will ever intepret these values into something meaningful?? + *nExtendedStates = 0; + return E_NOTIMPL; // mozilla does this + //return S_FALSE; +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_extendedStates(long /*maxExtendedStates*/, + BSTR **extendedStates, + long *nExtendedStates) +{ + *extendedStates = 0; + *nExtendedStates = 0; + return E_NOTIMPL; // mozilla does this + //return S_FALSE; +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_localizedExtendedStates(long /*maxLocalizedExtendedStates*/, + BSTR **localizedExtendedStates, + long *nLocalizedExtendedStates) +{ + *localizedExtendedStates = 0; + *nLocalizedExtendedStates = 0; + return E_NOTIMPL; // mozilla does this + //return S_FALSE; +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_uniqueID(long *outUniqueID) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + // ### FIXME SERIOUSLY, NOT A STABLE SOLUTION IF NODES ARE DELETED ETC + // Return 0 if no object and no parent. This is really an error case. + uint uid = uniqueID(); + accessibleDebug("uniqueID: %08x", uid); + + *outUniqueID = (long)uid; + return uid ? S_OK : S_FALSE; +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_windowHandle(HWND *windowHandle) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + return GetWindow(windowHandle); +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_indexInParent(long *indexInParent) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + if (!indexInParent) + return E_INVALIDARG; + QAccessibleInterface *par = accessible->parent(); + if (!par) { + *indexInParent = -1; + return S_FALSE; + } + int indexOfChild = par->indexOfChild(accessible); + delete par; + Q_ASSERT(indexOfChild >= 0); + *indexInParent = indexOfChild; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_locale(IA2Locale *locale) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + IA2Locale res; + QLocale l; + res.country = QStringToBSTR(QLocale::countryToString(l.country())); + res.language = QStringToBSTR(QLocale::languageToString(l.language())); + *locale = res; + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_attributes(BSTR *attributes) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + *attributes = 0;//QStringToBSTR(QString()); + return S_FALSE; +} + +/**************************************************************\ + * IAccessibleAction * + **************************************************************/ +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::nActions(long *nActions) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + *nActions = 0; + + if (QAccessibleActionInterface *actionIface = actionInterface()) + *nActions = actionIface->actionNames().count(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::doAction(long actionIndex) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + if (QAccessibleActionInterface *actionIface = actionInterface()) { + const QStringList actionNames = actionIface->actionNames(); + if (actionIndex < 0 || actionIndex >= actionNames.count()) + return E_INVALIDARG; + const QString actionName = actionNames.at(actionIndex); + actionIface->doAction(actionName); + return S_OK; + } + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_description(long actionIndex, BSTR *description) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + *description = 0; + if (QAccessibleActionInterface *actionIface = actionInterface()) { + const QStringList actionNames = actionIface->actionNames(); + if (actionIndex < 0 || actionIndex >= actionNames.count()) + return E_INVALIDARG; + const QString actionName = actionNames.at(actionIndex); + *description = QStringToBSTR(actionIface->localizedActionDescription(actionName)); + } + return *description ? S_OK : S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_keyBinding(long actionIndex, long nMaxBindings, BSTR **keyBindings, long *nBindings) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + Q_UNUSED(nMaxBindings); + BSTR *arrayOfBindingsToReturn = 0; + int numBindings = 0; + if (QAccessibleActionInterface *actionIface = actionInterface()) { + const QStringList actionNames = actionIface->actionNames(); + if (actionIndex < 0 || actionIndex >= actionNames.count()) + return E_INVALIDARG; + const QString actionName = actionNames.at(actionIndex); + const QStringList keyBindings = actionIface->keyBindingsForAction(actionName); + numBindings = keyBindings.count(); + if (numBindings > 0) { + // The IDL documents that the client must free with CoTaskMemFree + arrayOfBindingsToReturn = (BSTR*)::CoTaskMemAlloc(sizeof(BSTR) * numBindings); + for (int i = 0; i < numBindings; ++i) + arrayOfBindingsToReturn[i] = QStringToBSTR(keyBindings.at(i)); + } + } + *keyBindings = arrayOfBindingsToReturn; + *nBindings = numBindings; + + return numBindings ? S_OK : S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_name(long actionIndex, BSTR *name) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + *name = 0; + if (QAccessibleActionInterface *actionIface = actionInterface()) { + const QStringList actionNames = actionIface->actionNames(); + if (actionIndex < 0 || actionIndex >= actionNames.count()) + return E_INVALIDARG; + const QString actionName = actionNames.at(actionIndex); + *name = QStringToBSTR(actionName); + } + return *name ? S_OK : S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_localizedName(long actionIndex, BSTR *localizedName) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + *localizedName = 0; + if (QAccessibleActionInterface *actionIface = actionInterface()) { + const QStringList actionNames = actionIface->actionNames(); + if (actionIndex < 0 || actionIndex >= actionNames.count()) + return E_INVALIDARG; + + const QString actionName = actionNames.at(actionIndex); + *localizedName = QStringToBSTR(actionIface->localizedActionName(actionName)); + } + return *localizedName ? S_OK : S_FALSE; +} + +/**************************************************************\ + * IAccessibleComponent * + **************************************************************/ +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_locationInParent(long *x, long *y) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + QPoint topLeft = accessible->rect().topLeft(); + + if (QAccessibleInterface *parentIface = accessible->parent()) + topLeft -= parentIface->rect().topLeft(); + + *x = topLeft.x(); + *y = topLeft.y(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_foreground(IA2Color *foreground) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + // IA2Color is a typedef for long + *foreground = (IA2Color)accessible->foregroundColor().rgb(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_background(IA2Color *background) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + // IA2Color is a typedef for long + *background = (IA2Color)accessible->backgroundColor().rgb(); + return S_OK; +} + +/**************************************************************\ + * IAccessibleTable2 * + **************************************************************/ +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_cellAt( long row, long column, IUnknown **cell) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + *cell = 0; + if (QAccessibleTableInterface *tableIface = tableInterface()) { + if (QAccessibleInterface *qtCell = tableIface->cellAt(row, column)) { + *cell = QWindowsAccessibility::wrap(qtCell); + } + } + accessibleDebug("found cell? %p", *cell); + return *cell ? S_OK : S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_caption( IUnknown **captionInterface) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + *captionInterface = 0; + if (QAccessibleTableInterface *tableIface = tableInterface()) { + if (QAccessibleInterface *iface = tableIface->caption()) + *captionInterface = QWindowsAccessibility::wrap(iface); + } + return *captionInterface ? S_OK : S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_columnDescription( long column, BSTR *description) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + *description = 0; + if (QAccessibleTableInterface *tableIface = tableInterface()) { + const QString qtDesc = tableIface->columnDescription(column); + if (!qtDesc.isEmpty()) + *description = QStringToBSTR(qtDesc); + } + return *description ? S_OK : S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nColumns( long *columnCount) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + *columnCount = tableIface->columnCount(); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nRows(long *rowCount) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + *rowCount = tableIface->rowCount(); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nSelectedCells(long *cellCount) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + *cellCount = tableIface->selectedCellCount(); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nSelectedColumns(long *columnCount) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + *columnCount = tableIface->selectedColumnCount(); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nSelectedRows(long *rowCount) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + *rowCount = tableIface->selectedRowCount(); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_rowDescription(long row, BSTR *description) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + *description = 0; + if (QAccessibleTableInterface *tableIface = tableInterface()) { + const QString qtDesc = tableIface->columnDescription(row); + if (!qtDesc.isEmpty()) + *description = QStringToBSTR(qtDesc); + } + return *description ? S_OK : S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_selectedCells(IUnknown ***cells, long *nSelectedCells) +{ + accessibleDebugClientCalls(accessible); + Q_UNUSED(cells); + Q_UNUSED(nSelectedCells); + if (!accessible->isValid()) + return E_FAIL; + + QList selectedCells = tableInterface()->selectedCells(); + return wrapListOfCells(selectedCells, cells, nSelectedCells); +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_selectedColumns(long **selectedColumns, long *nColumns) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + const QList selectedIndices = tableIface->selectedColumns(); + const int &count = selectedIndices.count(); + long *selected = (count ? (long*)::CoTaskMemAlloc(sizeof(long) * count) : (long*)0); + for (int i = 0; i < count; ++i) + selected[i] = selectedIndices.at(i); + *selectedColumns = selected; + *nColumns = count; + return count ? S_OK : S_FALSE; + } + return E_FAIL; + +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_selectedRows(long **selectedRows, long *nRows) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + const QList selectedIndices = tableIface->selectedRows(); + const int &count = selectedIndices.count(); + long *selected = (count ? (long*)::CoTaskMemAlloc(sizeof(long) * count) : (long*)0); + for (int i = 0; i < count; ++i) + selected[i] = selectedIndices.at(i); + *selectedRows = selected; + *nRows = count; + return count ? S_OK : S_FALSE; + } + return E_FAIL; + +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_summary(IUnknown **summaryInterface) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + *summaryInterface = 0; + if (QAccessibleTableInterface *tableIface = tableInterface()) { + if (QAccessibleInterface *iface = tableIface->summary()) + *summaryInterface = QWindowsAccessibility::wrap(iface); + } + return *summaryInterface ? S_OK : S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_isColumnSelected(long column, boolean *isSelected) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + *isSelected = tableIface->isColumnSelected(column); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_isRowSelected(long row, boolean *isSelected) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + *isSelected = tableIface->isRowSelected(row); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::selectRow(long row) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + bool ok = tableIface->selectRow(row); + return ok ? S_OK : E_INVALIDARG; //### Not sure of the return value if it fails??? + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::selectColumn(long column) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + bool ok = tableIface->selectColumn(column); + return ok ? S_OK : E_INVALIDARG; //### Not sure of the return value if it fails??? + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::unselectRow(long row) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + bool ok = tableIface->unselectRow(row); + return ok ? S_OK : E_INVALIDARG; //### Not sure of the return value if it fails??? + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::unselectColumn(long column) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + if (QAccessibleTableInterface *tableIface = tableInterface()) { + bool ok = tableIface->unselectColumn(column); + return ok ? S_OK : E_INVALIDARG; //### Not sure of the return value if it fails??? + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_modelChange( IA2TableModelChange * /*modelChange*/) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + return E_NOTIMPL; +} + +/**************************************************************\ + * IAccessibleTableCell * +\**************************************************************/ +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_columnExtent(long *nColumnsSpanned) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + *nColumnsSpanned = tableCellInterface()->columnExtent(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_columnHeaderCells(IUnknown ***cellAccessibles, + long *nColumnHeaderCells) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + const QList headerCells = tableCellInterface()->columnHeaderCells(); + return wrapListOfCells(headerCells, cellAccessibles, nColumnHeaderCells); +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_columnIndex(long *columnIndex) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + *columnIndex = tableCellInterface()->columnIndex(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_rowExtent(long *nRowsSpanned) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + *nRowsSpanned = tableCellInterface()->rowExtent(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_rowHeaderCells(IUnknown ***cellAccessibles, + long *nRowHeaderCells) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + const QList headerCells = tableCellInterface()->rowHeaderCells(); + return wrapListOfCells(headerCells, cellAccessibles, nRowHeaderCells); +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_rowIndex(long *rowIndex) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + *rowIndex = tableCellInterface()->rowIndex(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_isSelected( boolean *isSelected) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + *isSelected = tableCellInterface()->isSelected(); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_rowColumnExtents(long *row, long *column, + long *rowExtents, long *columnExtents, + boolean *isSelected) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + tableCellInterface()->rowColumnExtents((int*)row, (int*)column, (int*)rowExtents, (int*)columnExtents, (bool*)isSelected); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_table(IUnknown **table) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + + QAccessibleInterface *tableIface = tableCellInterface()->table(); + + *table = QWindowsAccessibility::wrap(tableIface); + return S_OK; +} + +/**************************************************************\ + * IAccessibleText * +\**************************************************************/ +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::addSelection(long startOffset, + long endOffset) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *text = textInterface()) { + text->addSelection(startOffset, endOffset); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_attributes(long offset, + long *startOffset, + long *endOffset, + BSTR *textAttributes) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *text = textInterface()) { + const QString attrs = text->attributes(offset, (int*)startOffset, (int*)endOffset); + *textAttributes = QStringToBSTR(attrs); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_caretOffset(long *offset) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *text = textInterface()) { + *offset = text->cursorPosition(); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_characterExtents(long offset, + enum IA2CoordinateType coordType, + long *x, + long *y, + long *width, + long *height) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *text = textInterface()) { + const QRect rect = text->characterRect(offset, (QAccessible2::CoordinateType)coordType); + *x = rect.x(); + *y = rect.y(); + *width = rect.width(); + *height = rect.height(); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nSelections(long *nSelections) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *text = textInterface()) { + *nSelections = text->selectionCount(); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_offsetAtPoint(long x, + long y, + enum IA2CoordinateType coordType, + long *offset) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *text = textInterface()) { + *offset = text->offsetAtPoint(QPoint(x,y), (QAccessible2::CoordinateType)coordType); + return (*offset >=0 ? S_OK : S_FALSE); + } + return E_FAIL; + +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_selection(long selectionIndex, + long *startOffset, + long *endOffset) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *text = textInterface()) { + text->selection(selectionIndex, (int*)startOffset, (int*)endOffset); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_text(long startOffset, + long endOffset, + BSTR *text) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *textif = textInterface()) { + const QString t = textif->text(startOffset, endOffset); + if (!t.isEmpty()) { + *text = QStringToBSTR(t); + return S_OK; + } + return E_INVALIDARG; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_textBeforeOffset(long offset, + enum IA2TextBoundaryType boundaryType, + long *startOffset, + long *endOffset, + BSTR *text) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *textIface = textInterface()) { + const QString txt = textIface->textBeforeOffset(offset, (QAccessible2::BoundaryType)boundaryType, (int*)startOffset, (int*)endOffset); + if (!txt.isEmpty()) { + *text = QStringToBSTR(txt); + return S_OK; + } + return S_FALSE; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_textAfterOffset( + long offset, + enum IA2TextBoundaryType boundaryType, + long *startOffset, + long *endOffset, + BSTR *text) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *textIface = textInterface()) { + const QString txt = textIface->textAfterOffset(offset, (QAccessible2::BoundaryType)boundaryType, (int*)startOffset, (int*)endOffset); + if (!txt.isEmpty()) { + *text = QStringToBSTR(txt); + return S_OK; + } + return S_FALSE; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_textAtOffset(long offset, + enum IA2TextBoundaryType boundaryType, + long *startOffset, + long *endOffset, + BSTR *text) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *textIface = textInterface()) { + const QString txt = textIface->textAtOffset(offset, (QAccessible2::BoundaryType)boundaryType, (int*)startOffset, (int*)endOffset); + if (!txt.isEmpty()) { + *text = QStringToBSTR(txt); + return S_OK; + } + return S_FALSE; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::removeSelection(long selectionIndex) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *textIface = textInterface()) { + textIface->removeSelection(selectionIndex); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::setCaretOffset(long offset) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *textIface = textInterface()) { + textIface->setCursorPosition(offset); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::setSelection(long selectionIndex, + long startOffset, + long endOffset) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *textIface = textInterface()) { + textIface->setSelection(selectionIndex, startOffset, endOffset); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nCharacters(long *nCharacters) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *textIface = textInterface()) { + *nCharacters = textIface->characterCount(); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::scrollSubstringTo(long startIndex, + long endIndex, + enum IA2ScrollType scrollType) +{ + accessibleDebugClientCalls(accessible); + if (QAccessibleTextInterface *textIface = textInterface()) { + Q_UNUSED(scrollType); //### + textIface->scrollToSubstring(startIndex, endIndex); + return S_OK; + } + return E_FAIL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::scrollSubstringToPoint(long startIndex, + long endIndex, + enum IA2CoordinateType coordinateType, + long x, + long y) +{ + Q_UNUSED(startIndex); + Q_UNUSED(endIndex); + Q_UNUSED(coordinateType); + Q_UNUSED(x); + Q_UNUSED(y); + + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_newText(IA2TextSegment *newText) +{ + Q_UNUSED(newText); + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_oldText(IA2TextSegment *oldText) +{ + Q_UNUSED(oldText); + return E_NOTIMPL; +} + +/**************************************************************\ + * IAccessibleValue * + **************************************************************/ +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_currentValue(VARIANT *currentValue) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + if (QAccessibleValueInterface *valueIface = valueInterface()) { + const QVariant var = valueIface->currentValue(); + if (QVariantToVARIANT(var, *currentValue, QByteArray(), false)) + return S_OK; + + } + currentValue->vt = VT_EMPTY; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::setCurrentValue(VARIANT value) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + HRESULT hr = S_FALSE; + if (QAccessibleValueInterface *valueIface = valueInterface()) { + hr = VariantChangeType(&value, &value, 0, VT_R8); + if (SUCCEEDED(hr)) { + // ### works only for numbers (not date, strings, etc) + valueIface->setCurrentValue(QVariant(value.dblVal)); + } + } + return hr; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_maximumValue(VARIANT *maximumValue) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + if (QAccessibleValueInterface *valueIface = valueInterface()) { + const QVariant var = valueIface->maximumValue(); + if (QVariantToVARIANT(var, *maximumValue, QByteArray(), false)) + return S_OK; + } + maximumValue->vt = VT_EMPTY; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_minimumValue(VARIANT *minimumValue) +{ + accessibleDebugClientCalls(accessible); + if (!accessible->isValid()) + return E_FAIL; + if (QAccessibleValueInterface *valueIface = valueInterface()) { + const QVariant var = valueIface->minimumValue(); + if (QVariantToVARIANT(var, *minimumValue, QByteArray(), false)) + return S_OK; + } + minimumValue->vt = VT_EMPTY; + return S_FALSE; +} + + +/**************************************************************\ + * IServiceProvider * + **************************************************************/ +/*! + \internal + Reimplemented from IServiceProvider +*/ +HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::QueryService(REFGUID guidService, REFIID riid, void **iface) +{ + if (!iface) + return E_POINTER; + Q_UNUSED(guidService); + *iface = 0; + accessibleDebug("QWindowsIA2Accessible::QS(): %s", IIDToString(riid).constData()); + if (riid == IID_IAccessible || riid == IID_IUnknown || riid == IID_IDispatch) { + *iface = static_cast(this); + } else if (/*guidService == IID_IAccessible && */riid == IID_IAccessible2) { + *iface = static_cast(this); + } else if (riid == IID_IAccessibleApplication) { + *iface = new AccessibleApplication; + return S_OK; + } else { + QueryInterface(riid, iface); + } + if (*iface) { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + + +/*! + \internal + private function.. + \a maxRelations max number of relations to return in \a relations + \a relations the array of relations matching + \a startIndex Index to start to return from, + it will return only that specific relation in \a relations + + If \a relations is null, \a startIndex and \a maxRelations are ignored, causing + it to return the number of relations in \a nRelations +*/ +HRESULT QWindowsIA2Accessible::getRelationsHelper(IAccessibleRelation **relations, int startIndex, long maxRelations, long *nRelations /* = 0*/) +{ + if (nRelations) + *nRelations = 0; + typedef QPair RelationEntry; + QVector rels = accessible->relations(); + QMap relationMap; + for (QVector::const_iterator it = rels.constBegin(); it != rels.constEnd(); ++it) + { + RelationEntry e = *it; + relationMap.insertMulti(e.second, e.first); + } + + QList keys = relationMap.keys(); + const int numRelations = keys.count(); + if (relations) { + for (int i = startIndex; i < qMin(startIndex + (int)maxRelations, numRelations); ++i) { + QAccessible::Relation relation = keys.at(i); + QList targets = relationMap.values(relation); + AccessibleRelation *rel = new AccessibleRelation(targets, relation); + *relations = rel; + ++relations; + } + } + if (nRelations) + *nRelations = numRelations; + + return numRelations > 0 ? S_OK : S_FALSE; +} + + + + +/*! + \internal + helper to wrap a QList inside an array of IAccessible* + The IAccessible* array is returned as a IUnknown* +*/ +HRESULT QWindowsIA2Accessible::wrapListOfCells(const QList &inputCells, IUnknown ***outputAccessibles, long *nCellCount) +{ + const int count = inputCells.count(); + // Server allocates array + IUnknown **outputCells = count ? (IUnknown**)::CoTaskMemAlloc(sizeof(IUnknown*) * count ) : (IUnknown**)0; + for (int i = 0; i < count; ++i) + outputCells[i] = QWindowsAccessibility::wrap(inputCells.at(i)); + + *outputAccessibles = outputCells; + *nCellCount = count; + return count > 0 ? S_OK : S_FALSE; +} + +uint QWindowsIA2Accessible::uniqueID() const +{ + uint uid = 0; + if (QObject *obj = accessible->object()) + uid = qHash(obj); + + if (!uid) { + QAccessibleInterface *acc = accessible; + QVector indexOfNodes; + while (acc && !acc->object()) { + QAccessibleInterface *par = acc->parent(); + indexOfNodes.append(par->indexOfChild(acc)); + if (acc != accessible) + delete acc; + acc = par; + } + if (acc) { + if (acc->object()) { + uid = qHash(acc->object()); + for (int i = 0; i < indexOfNodes.count(); ++i) + uid = qHash(uid + indexOfNodes.at(i)); + + } + if (acc != accessible) + delete acc; + } + } + return uid; +} + + +#define IF_EQUAL_RETURN_IIDSTRING(id, iid) if (id == iid) return QByteArray(#iid) + +QByteArray QWindowsIA2Accessible::IIDToString(REFIID id) +{ + IF_EQUAL_RETURN_IIDSTRING(id, IID_IUnknown); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IDispatch); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessible); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IOleWindow); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IServiceProvider); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessible2); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleAction); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleApplication); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleComponent); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleEditableText); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleHyperlink); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleHypertext); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleImage); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleRelation); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleTable); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleTable2); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleTableCell); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleText); + IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleValue); + + // else... + QByteArray strGuid; +#if 0 // Can be useful for debugging, but normally we'd like to reduce the noise a bit... + OLECHAR szGuid[39]={0}; + ::StringFromGUID2(id, szGuid, 39); + strGuid.reserve(40); + ::WideCharToMultiByte(CP_UTF8, 0, szGuid, 39, strGuid.data(), 39, NULL, NULL); + strGuid[38] = '\0'; +#endif + return strGuid; +} + + +QT_END_NAMESPACE + +#endif //QT_NO_ACCESSIBILITY diff --git a/src/plugins/platforms/windows/accessible/iaccessible2.h b/src/plugins/platforms/windows/accessible/iaccessible2.h new file mode 100644 index 0000000000..11889df9ec --- /dev/null +++ b/src/plugins/platforms/windows/accessible/iaccessible2.h @@ -0,0 +1,375 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#ifndef IACCESSIBLE2_H +#define IACCESSIBLE2_H + +#include +#ifndef QT_NO_ACCESSIBILITY + +#include "qwindowsmsaaaccessible.h" +#include "comutils.h" + +#include "Accessible2.h" +#include "AccessibleAction.h" +#include "AccessibleApplication.h" +#include "AccessibleComponent.h" +#include "AccessibleEditableText.h" +#include "AccessibleHyperlink.h" +#include "AccessibleHypertext.h" +#include "AccessibleImage.h" +#include "AccessibleRelation.h" +#include "AccessibleTable.h" +#include "AccessibleTable2.h" +#include "AccessibleTableCell.h" +#include "AccessibleText.h" +#include "AccessibleValue.h" + +#include "AccessibleEventID.h" +#include "AccessibleRole.h" +#include "AccessibleStates.h" + +QT_BEGIN_NAMESPACE + +class QWindowsIA2Accessible : public QWindowsMsaaAccessible, + public IAccessibleAction, + public IAccessibleComponent, + /*public IAccessibleEditableText,*/ + public IAccessibleTable2, + public IAccessibleTableCell, + public IAccessibleText, + public IAccessibleValue, + public IServiceProvider +{ +public: + QWindowsIA2Accessible(QAccessibleInterface *a) : QWindowsMsaaAccessible(a) {} + + /* IUnknown */ + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + /* IAccessible2 */ + HRESULT STDMETHODCALLTYPE get_nRelations(long *nRelations); + HRESULT STDMETHODCALLTYPE get_relation(long relationIndex, IAccessibleRelation **relation); + HRESULT STDMETHODCALLTYPE get_relations(long maxRelations, IAccessibleRelation **relations, long *nRelations); + HRESULT STDMETHODCALLTYPE role(long *role); + HRESULT STDMETHODCALLTYPE scrollTo(enum IA2ScrollType scrollType); + HRESULT STDMETHODCALLTYPE scrollToPoint(enum IA2CoordinateType coordinateType, long x, long y); + HRESULT STDMETHODCALLTYPE get_groupPosition(long *groupLevel, long *similarItemsInGroup, long *positionInGroup); + HRESULT STDMETHODCALLTYPE get_states(AccessibleStates *states); + HRESULT STDMETHODCALLTYPE get_extendedRole(BSTR *extendedRole); + HRESULT STDMETHODCALLTYPE get_localizedExtendedRole(BSTR *localizedExtendedRole); + HRESULT STDMETHODCALLTYPE get_nExtendedStates(long *nExtendedStates); + HRESULT STDMETHODCALLTYPE get_extendedStates(long maxExtendedStates, BSTR **extendedStates, long *nExtendedStates); + HRESULT STDMETHODCALLTYPE get_localizedExtendedStates(long maxLocalizedExtendedStates, BSTR **localizedExtendedStates, long *nLocalizedExtendedStates); + HRESULT STDMETHODCALLTYPE get_uniqueID(long *uniqueID); + HRESULT STDMETHODCALLTYPE get_windowHandle(HWND *windowHandle); + HRESULT STDMETHODCALLTYPE get_indexInParent(long *indexInParent); + HRESULT STDMETHODCALLTYPE get_locale(IA2Locale *locale); + HRESULT STDMETHODCALLTYPE get_attributes(BSTR *attributes); + + /* IAccessibleAction */ + HRESULT STDMETHODCALLTYPE nActions(long *nActions); + HRESULT STDMETHODCALLTYPE doAction(long actionIndex); + HRESULT STDMETHODCALLTYPE get_description(long actionIndex, BSTR *description); + HRESULT STDMETHODCALLTYPE get_keyBinding(long actionIndex, long nMaxBindings, BSTR **keyBindings, long *nBindings); + HRESULT STDMETHODCALLTYPE get_name(long actionIndex, BSTR *name); + HRESULT STDMETHODCALLTYPE get_localizedName(long actionIndex, BSTR *localizedName); + + /* IAccessibleComponent */ + HRESULT STDMETHODCALLTYPE get_locationInParent(long *x,long *y); + HRESULT STDMETHODCALLTYPE get_foreground(IA2Color *foreground); + HRESULT STDMETHODCALLTYPE get_background(IA2Color *background); + + /* IAccessibleEditableText */ + /* + HRESULT STDMETHODCALLTYPE copyText(long startOffset, long endOffset); + HRESULT STDMETHODCALLTYPE deleteText(long startOffset, long endOffset); + HRESULT STDMETHODCALLTYPE insertText(long offset, BSTR *text); + HRESULT STDMETHODCALLTYPE cutText(long startOffset, long endOffset); + HRESULT STDMETHODCALLTYPE pasteText(long offset); + HRESULT STDMETHODCALLTYPE replaceText(long startOffset, long endOffset, BSTR *text); + HRESULT STDMETHODCALLTYPE setAttributes(long startOffset, long endOffset, BSTR *attributes); + */ + + /* IAccessibleTable2 */ + HRESULT STDMETHODCALLTYPE get_cellAt( long row, long column, IUnknown **cell); + HRESULT STDMETHODCALLTYPE get_caption( IUnknown **accessible); + HRESULT STDMETHODCALLTYPE get_columnDescription( long column, BSTR *description); + HRESULT STDMETHODCALLTYPE get_nColumns( long *columnCount); + HRESULT STDMETHODCALLTYPE get_nRows( long *rowCount); + HRESULT STDMETHODCALLTYPE get_nSelectedCells( long *cellCount); + HRESULT STDMETHODCALLTYPE get_nSelectedColumns( long *columnCount); + HRESULT STDMETHODCALLTYPE get_nSelectedRows( long *rowCount); + HRESULT STDMETHODCALLTYPE get_rowDescription( long row, BSTR *description); + HRESULT STDMETHODCALLTYPE get_selectedCells( IUnknown ***cells, long *nSelectedCells); + HRESULT STDMETHODCALLTYPE get_selectedColumns( long **selectedColumns, long *nColumns); + HRESULT STDMETHODCALLTYPE get_selectedRows( long **selectedRows, long *nRows); + HRESULT STDMETHODCALLTYPE get_summary( IUnknown **accessible); + HRESULT STDMETHODCALLTYPE get_isColumnSelected( long column, boolean *isSelected); + HRESULT STDMETHODCALLTYPE get_isRowSelected( long row, boolean *isSelected); + HRESULT STDMETHODCALLTYPE selectRow( long row); + HRESULT STDMETHODCALLTYPE selectColumn( long column); + HRESULT STDMETHODCALLTYPE unselectRow( long row); + HRESULT STDMETHODCALLTYPE unselectColumn( long column); + HRESULT STDMETHODCALLTYPE get_modelChange( IA2TableModelChange *modelChange); + + /* IAccessibleTableCell */ + HRESULT STDMETHODCALLTYPE get_columnExtent(long *nColumnsSpanned); + HRESULT STDMETHODCALLTYPE get_columnHeaderCells(IUnknown ***cellAccessibles, long *nColumnHeaderCells); + HRESULT STDMETHODCALLTYPE get_columnIndex(long *columnIndex); + HRESULT STDMETHODCALLTYPE get_rowExtent(long *nRowsSpanned); + HRESULT STDMETHODCALLTYPE get_rowHeaderCells(IUnknown ***cellAccessibles, long *nRowHeaderCells); + HRESULT STDMETHODCALLTYPE get_rowIndex(long *rowIndex); + HRESULT STDMETHODCALLTYPE get_isSelected( boolean *isSelected); + HRESULT STDMETHODCALLTYPE get_rowColumnExtents(long *row, long *column, + long *rowExtents, long *columnExtents, + boolean *isSelected); + HRESULT STDMETHODCALLTYPE get_table(IUnknown **table); + + + /* IAccessibleText */ + HRESULT STDMETHODCALLTYPE addSelection(long startOffset, long endOffset); + HRESULT STDMETHODCALLTYPE get_attributes(long offset, long *startOffset, + long *endOffset, BSTR *textAttributes); + HRESULT STDMETHODCALLTYPE get_caretOffset(long *offset); + HRESULT STDMETHODCALLTYPE get_characterExtents(long offset, enum IA2CoordinateType coordType, + long *x, long *y, + long *width, long *height); + HRESULT STDMETHODCALLTYPE get_nSelections(long *nSelections); + HRESULT STDMETHODCALLTYPE get_offsetAtPoint(long x, long y, enum IA2CoordinateType coordType, long *offset); + HRESULT STDMETHODCALLTYPE get_selection(long selectionIndex, long *startOffset, long *endOffset); + HRESULT STDMETHODCALLTYPE get_text(long startOffset, long endOffset, BSTR *text); + HRESULT STDMETHODCALLTYPE get_textBeforeOffset(long offset, enum IA2TextBoundaryType boundaryType, + long *startOffset, long *endOffset, BSTR *text); + HRESULT STDMETHODCALLTYPE get_textAfterOffset(long offset, enum IA2TextBoundaryType boundaryType, + long *startOffset, long *endOffset, BSTR *text); + HRESULT STDMETHODCALLTYPE get_textAtOffset(long offset, enum IA2TextBoundaryType boundaryType, + long *startOffset, long *endOffset, BSTR *text); + HRESULT STDMETHODCALLTYPE removeSelection(long selectionIndex); + HRESULT STDMETHODCALLTYPE setCaretOffset(long offset); + HRESULT STDMETHODCALLTYPE setSelection(long selectionIndex, long startOffset, long endOffset); + HRESULT STDMETHODCALLTYPE get_nCharacters(long *nCharacters); + HRESULT STDMETHODCALLTYPE scrollSubstringTo(long startIndex, long endIndex, enum IA2ScrollType scrollType); + HRESULT STDMETHODCALLTYPE scrollSubstringToPoint(long startIndex, long endIndex, + enum IA2CoordinateType coordinateType, long x, long y); + HRESULT STDMETHODCALLTYPE get_newText(IA2TextSegment *newText); + HRESULT STDMETHODCALLTYPE get_oldText(IA2TextSegment *oldText); + + /* IAccessibleValue */ + HRESULT STDMETHODCALLTYPE get_currentValue(VARIANT *currentValue); + HRESULT STDMETHODCALLTYPE setCurrentValue(VARIANT value); + HRESULT STDMETHODCALLTYPE get_maximumValue(VARIANT *maximumValue); + HRESULT STDMETHODCALLTYPE get_minimumValue(VARIANT *minimumValue); + + /* IServiceProvider */ + HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, REFIID riid, void **ppv); + + /* private helper functions */ +private: + inline QAccessibleTextInterface *textInterface() const { + return accessible->isValid() ? accessible->textInterface() : static_cast(0); + } + + inline QAccessibleActionInterface *actionInterface() const { + return accessible->actionInterface(); + } + + inline QAccessibleValueInterface *valueInterface() const { + return accessible->valueInterface(); + } + + inline QAccessibleTableInterface *tableInterface() const { + return accessible->tableInterface(); + } + + inline QAccessibleTableCellInterface *tableCellInterface() const { + return accessible->tableCellInterface(); + } + + HRESULT getRelationsHelper(IAccessibleRelation **relations, int startIndex, long maxRelations, long *nRelations = 0); + HRESULT wrapListOfCells(const QList &inputCells, IUnknown ***outputAccessibles, long *nCellCount); + uint uniqueID() const; + QByteArray IIDToString(REFIID id); + +private: + ULONG ref; + +}; + +/**************************************************************\ + * AccessibleApplication * + **************************************************************/ +class AccessibleApplication : public IAccessibleApplication +{ +public: + AccessibleApplication() : m_ref(1) + { + + } + + virtual ~AccessibleApplication() {} + + /* IUnknown */ + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + /* IAccessibleApplication */ + HRESULT STDMETHODCALLTYPE get_appName(/* [retval][out] */ BSTR *name); + HRESULT STDMETHODCALLTYPE get_appVersion(/* [retval][out] */ BSTR *version); + HRESULT STDMETHODCALLTYPE get_toolkitName(/* [retval][out] */ BSTR *name); + HRESULT STDMETHODCALLTYPE get_toolkitVersion(/* [retval][out] */ BSTR *version); +private: + ULONG m_ref; +}; + + + +/**************************************************************\ + * IAccessibleRelation * + **************************************************************/ +struct AccessibleRelation : public IAccessibleRelation +{ + AccessibleRelation(const QList &targets, + QAccessible::Relation relation) + : m_targets(targets), m_relation(relation), m_ref(1) + { + Q_ASSERT(m_targets.count()); + } + + + /* IUnknown */ + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface) + { + *iface = 0; + if (id == IID_IUnknown) + *iface = (IUnknown*)this; + + if (*iface) { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + ULONG STDMETHODCALLTYPE AddRef() + { + return ++m_ref; + } + + ULONG STDMETHODCALLTYPE Release() + { + if (!--m_ref) { + delete this; + return 0; + } + return m_ref; + } + + /* IAccessibleRelation */ + HRESULT STDMETHODCALLTYPE get_relationType( + /* [retval][out] */ BSTR *relationType) + { + *relationType = relationToBSTR(m_relation); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE get_localizedRelationType( + /* [retval][out] */ BSTR *localizedRelationType) + { + // Who ever needs this??? + *localizedRelationType = relationToBSTR(m_relation); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE get_nTargets( + /* [retval][out] */ long *nTargets) + { + // ### always one target + *nTargets = m_targets.count(); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE get_target(long targetIndex, IUnknown **target); + + + /*! + \internal + Client allocates and deallocates \a targets array + (see "Special Consideration when using Arrays", in Accessible2.idl) + */ + HRESULT STDMETHODCALLTYPE get_targets( + /* [in] */ long maxTargets, // Hmmm, ignore ??? + /* [length_is][size_is][out] */ IUnknown **targets, + /* [retval][out] */ long *nTargets); + +private: + static BSTR relationToBSTR(QAccessible::Relation relation) + { + wchar_t *constRelationString = 0; + switch (relation) { + case QAccessible::Controlled: + constRelationString = IA2_RELATION_CONTROLLED_BY; + break; + } + + if (constRelationString) { + BSTR bstrVal; + const UINT wlen = (UINT)wcslen(constRelationString); + bstrVal = ::SysAllocStringLen(constRelationString, wlen); + return bstrVal; + } + return 0; + } + + + QList m_targets; + QAccessible::Relation m_relation; + ULONG m_ref; +}; + +QT_END_NAMESPACE + +#endif //QT_NO_ACCESSIBILITY + +#endif // IACCESSIBLE2_H diff --git a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp new file mode 100644 index 0000000000..f409730600 --- /dev/null +++ b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp @@ -0,0 +1,301 @@ +/**************************************************************************** +** +** 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 +#ifndef QT_NO_ACCESSIBILITY + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qwindowsaccessibility.h" +#include "iaccessible2.h" +#include "comutils.h" + +#include + +//#include +#ifndef UiaRootObjectId +#define UiaRootObjectId -25 +#endif + +#include +#if !defined(WINABLEAPI) +# if defined(Q_OS_WINCE) +# include +# endif +# include +#endif + +#include +#include +#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU) +#include +#endif + +#ifdef Q_OS_WINCE +#include "../qguifunctions_wince.h" +#endif + +#include "../qtwindows_additional.h" + + +// This stuff is used for widgets/items with no window handle: +typedef QMap > NotifyMap; +Q_GLOBAL_STATIC(NotifyMap, qAccessibleRecentSentEvents) + + +QT_BEGIN_NAMESPACE + + +/*! + \!internal + \class QWindowsAccessibility + + Implements QPlatformAccessibility + +*/ +QWindowsAccessibility::QWindowsAccessibility() +{ +} + +void QWindowsAccessibility::notifyAccessibilityUpdate(const QAccessibleEvent &event) +{ + QString soundName; + switch (event.type()) { + case QAccessible::PopupMenuStart: + soundName = QLatin1String("MenuPopup"); + break; + + case QAccessible::MenuCommand: + soundName = QLatin1String("MenuCommand"); + break; + + case QAccessible::Alert: + { + /* ### FIXME +#ifndef QT_NO_MESSAGEBOX + QMessageBox *mb = qobject_cast(o); + if (mb) { + switch (mb->icon()) { + case QMessageBox::Warning: + soundName = QLatin1String("SystemExclamation"); + break; + case QMessageBox::Critical: + soundName = QLatin1String("SystemHand"); + break; + case QMessageBox::Information: + soundName = QLatin1String("SystemAsterisk"); + break; + default: + break; + } + } else +#endif // QT_NO_MESSAGEBOX +*/ + { + soundName = QLatin1String("SystemAsterisk"); + } + + } + break; + default: + break; + } + + if (!soundName.isEmpty()) { +#ifndef QT_NO_SETTINGS + QSettings settings(QLatin1String("HKEY_CURRENT_USER\\AppEvents\\Schemes\\Apps\\.Default\\") + soundName, + QSettings::NativeFormat); + QString file = settings.value(QLatin1String(".Current/.")).toString(); +#else + QString file; +#endif + if (!file.isEmpty()) { + PlaySound(reinterpret_cast(soundName.utf16()), 0, SND_ALIAS | SND_ASYNC | SND_NODEFAULT | SND_NOWAIT); + } + } + + typedef void (WINAPI *PtrNotifyWinEvent)(DWORD, HWND, LONG, LONG); + +#if defined(Q_OS_WINCE) // ### TODO: check for NotifyWinEvent in CE 6.0 + // There is no user32.lib nor NotifyWinEvent for CE + return; +#else + static PtrNotifyWinEvent ptrNotifyWinEvent = 0; + static bool resolvedNWE = false; + if (!resolvedNWE) { + resolvedNWE = true; + ptrNotifyWinEvent = (PtrNotifyWinEvent)QSystemLibrary::resolve(QLatin1String("user32"), "NotifyWinEvent"); + } + if (!ptrNotifyWinEvent) + return; + + // An event has to be associated with a window, + // so find the first parent that is a widget and that has a WId + QAccessibleInterface *iface = event.accessibleInterface(); + QWindow *window = iface ? window_helper(iface) : 0; + delete iface; + + if (!window) { + window = QGuiApplication::activeWindow(); + if (!window) + return; + } + + QPlatformNativeInterface *platform = QGuiApplication::platformNativeInterface(); + HWND hWnd = (HWND)platform->nativeResourceForWindow("handle", window); + + static int eventNum = 0; + if (event.type() != QAccessible::MenuCommand) { // MenuCommand is faked + // See comment "SENDING EVENTS TO OBJECTS WITH NO WINDOW HANDLE" + eventNum %= 50; //[0..49] + int eventId = - (eventNum - 1); + + qAccessibleRecentSentEvents()->insert(eventId, qMakePair(event.object(), event.child())); + ptrNotifyWinEvent(event.type(), hWnd, OBJID_CLIENT, eventId ); + + ++eventNum; + } +#endif // Q_OS_WINCE +} + + +/*! + \internal + helper to wrap a QAccessibleInterface inside a IAccessible* +*/ +IAccessible *QWindowsAccessibility::wrap(QAccessibleInterface *acc) +{ + if (!acc) + return 0; + QWindowsIA2Accessible *wacc = new QWindowsIA2Accessible(acc); + IAccessible *iacc = 0; + wacc->QueryInterface(IID_IAccessible, (void**)&iacc); + return iacc; +} + +/*! + \internal +*/ +QPair QWindowsAccessibility::getCachedObject(int entryId) +{ + return qAccessibleRecentSentEvents()->value(entryId); +} + +/* +void QWindowsAccessibility::setRootObject(QObject *o) +{ + +} + +void QWindowsAccessibility::initialize() +{ + +} + +void QWindowsAccessibility::cleanup() +{ + +} + +*/ + +bool QWindowsAccessibility::handleAccessibleObjectFromWindowRequest(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult) +{ + if (static_cast(lParam) == static_cast(UiaRootObjectId)) { + /* For UI Automation */ + } else if ((DWORD)lParam == OBJID_CLIENT) { +#if 1 + // Ignoring all requests while starting up + // ### Maybe QPA takes care of this??? + if (QApplication::startingUp() || QApplication::closingDown()) + return false; +#endif + + typedef LRESULT (WINAPI *PtrLresultFromObject)(REFIID, WPARAM, LPUNKNOWN); + static PtrLresultFromObject ptrLresultFromObject = 0; + static bool oleaccChecked = false; + + if (!oleaccChecked) { + oleaccChecked = true; +#if !defined(Q_OS_WINCE) + ptrLresultFromObject = (PtrLresultFromObject)QSystemLibrary::resolve(QLatin1String("oleacc"), "LresultFromObject"); +#endif + } + + if (ptrLresultFromObject) { + QWindow *window = QWindowsContext::instance()->findWindow(hwnd); + if (window) { + QAccessibleInterface *acc = window->accessibleRoot(); + if (acc) { + if (IAccessible *iface = wrap(acc)) { + *lResult = ptrLresultFromObject(IID_IAccessible, wParam, iface); // ref == 2 + if (*lResult) { + iface->Release(); // the client will release the object again, and then it will destroy itself + } + return true; + } else { + delete acc; + } + } + } + } + } + return false; +} + +QT_END_NAMESPACE + +#endif //QT_NO_ACCESSIBILITY diff --git a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h new file mode 100644 index 0000000000..3b7069570b --- /dev/null +++ b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWINDOWSACCESSIBILITY_H +#define QWINDOWSACCESSIBILITY_H + +#include "../qtwindowsglobal.h" +#include "../qwindowscontext.h" +#include + +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +class QWindowsAccessibility : public QPlatformAccessibility +{ +public: + QWindowsAccessibility(); + static bool handleAccessibleObjectFromWindowRequest(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult); + virtual void notifyAccessibilityUpdate(const QAccessibleEvent &event); + /* + virtual void setRootObject(QObject *o); + virtual void initialize(); + virtual void cleanup(); + */ + static IAccessible *wrap(QAccessibleInterface *acc); + + static QPair getCachedObject(int entryId); +}; + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QWINDOWSACCESSIBILITY_H diff --git a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp new file mode 100644 index 0000000000..ca3ce0a614 --- /dev/null +++ b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp @@ -0,0 +1,1228 @@ +/**************************************************************************** +** +** 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 +#ifndef QT_NO_ACCESSIBILITY + +#include "qwindowsmsaaaccessible.h" +#include "qwindowsaccessibility.h" +#include +#include +#include +#include "comutils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include +#ifndef UiaRootObjectId +#define UiaRootObjectId -25 +#endif + +#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU) +#include +#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 &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 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 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 RelationPair; + QVector 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 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; +} + +QWindow *window_helper(const QAccessibleInterface *iface) +{ + QWindow *window = iface->window(); + if (!window) { + QAccessibleInterface *acc = iface->parent(); + while (acc && !window) { + window = acc->window(); + QAccessibleInterface *par = acc->parent(); + delete acc; + acc = par; + } + } + return window; +} + +/**************************************************************\ + * 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 = window_helper(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 diff --git a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.h b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.h new file mode 100644 index 0000000000..609f57b398 --- /dev/null +++ b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#ifndef QWINDOWSMSAAACCESSIBLE_H +#define QWINDOWSMSAAACCESSIBLE_H + +#include +#ifndef QT_NO_ACCESSIBILITY + +#include +#include "Accessible2.h" +#include +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_DEBUG +bool debug_accessibility(); +# define accessibleDebug !debug_accessibility() ? (void)0 : qDebug +#else +# define accessibleDebug() +#endif + +#define DEBUG_SHOW_ATCLIENT_COMMANDS +#if defined(DEBUG_SHOW_ATCLIENT_COMMANDS) +void accessibleDebugClientCalls_helper(const char* funcName, const QAccessibleInterface *iface); +# define accessibleDebugClientCalls(iface) accessibleDebugClientCalls_helper(Q_FUNC_INFO, iface) +#else +# define accessibleDebugClientCalls(iface) +#endif + +typedef QSharedPointer QAIPointer; + +QWindow *window_helper(const QAccessibleInterface *iface); + +/**************************************************************\ + * QWindowsAccessible * + **************************************************************/ +class QWindowsMsaaAccessible : public IAccessible2, public IOleWindow +{ +public: + QWindowsMsaaAccessible(QAccessibleInterface *a) + : accessible(a) + { + } + + virtual ~QWindowsMsaaAccessible() + { + delete accessible; + } + + + /* IDispatch */ + HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int *); + HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int, unsigned long, ITypeInfo **); + HRESULT STDMETHODCALLTYPE GetIDsOfNames(const _GUID &, wchar_t **, unsigned int, unsigned long, long *); + HRESULT STDMETHODCALLTYPE Invoke(long, const _GUID &, unsigned long, unsigned short, tagDISPPARAMS *, tagVARIANT *, tagEXCEPINFO *, unsigned int *); + + /* IAccessible */ + HRESULT STDMETHODCALLTYPE accHitTest(long xLeft, long yTop, VARIANT *pvarID); + HRESULT STDMETHODCALLTYPE accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID); + HRESULT STDMETHODCALLTYPE accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd); + HRESULT STDMETHODCALLTYPE get_accChild(VARIANT varChildID, IDispatch** ppdispChild); + HRESULT STDMETHODCALLTYPE get_accChildCount(long* pcountChildren); + HRESULT STDMETHODCALLTYPE get_accParent(IDispatch** ppdispParent); + + HRESULT STDMETHODCALLTYPE accDoDefaultAction(VARIANT varID); + HRESULT STDMETHODCALLTYPE get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction); + HRESULT STDMETHODCALLTYPE get_accDescription(VARIANT varID, BSTR* pszDescription); + HRESULT STDMETHODCALLTYPE get_accHelp(VARIANT varID, BSTR *pszHelp); + HRESULT STDMETHODCALLTYPE get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild, long *pidTopic); + HRESULT STDMETHODCALLTYPE get_accKeyboardShortcut(VARIANT varID, BSTR *pszKeyboardShortcut); + HRESULT STDMETHODCALLTYPE get_accName(VARIANT varID, BSTR* pszName); + HRESULT STDMETHODCALLTYPE put_accName(VARIANT varChild, BSTR szName); + HRESULT STDMETHODCALLTYPE get_accRole(VARIANT varID, VARIANT *pvarRole); + HRESULT STDMETHODCALLTYPE get_accState(VARIANT varID, VARIANT *pvarState); + HRESULT STDMETHODCALLTYPE get_accValue(VARIANT varID, BSTR* pszValue); + HRESULT STDMETHODCALLTYPE put_accValue(VARIANT varChild, BSTR szValue); + + HRESULT STDMETHODCALLTYPE accSelect(long flagsSelect, VARIANT varID); + HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT *pvarID); + HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT *pvarChildren); + + /* IOleWindow */ + HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd); + HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode); + +protected: + QAccessibleInterface *accessible; + + QAIPointer childPointer(VARIANT varID) + { + return QAIPointer(accessible->child(varID.lVal - 1)); + } +}; + +QT_END_NAMESPACE + +#endif //QT_NO_ACCESSIBILITY + +#endif // QWINDOWSMSAAACCESSIBLE_H diff --git a/src/plugins/platforms/windows/qwindowsaccessibility.cpp b/src/plugins/platforms/windows/qwindowsaccessibility.cpp deleted file mode 100644 index 134b1c81ce..0000000000 --- a/src/plugins/platforms/windows/qwindowsaccessibility.cpp +++ /dev/null @@ -1,1525 +0,0 @@ -/**************************************************************************** -** -** 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 -#ifndef QT_NO_ACCESSIBILITY - - -#include "qwindowsaccessibility.h" -#include "qwindowscontext.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#include -#ifndef UiaRootObjectId -#define UiaRootObjectId -25 -#endif - -#include -#if !defined(WINABLEAPI) -# if defined(Q_OS_WINCE) -# include -# endif -# include -#endif - -#include -#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU) -#include -#endif - -#ifdef Q_OS_WINCE -#include "qguifunctions_wince.h" -#endif - -#include "qtwindows_additional.h" - -QT_BEGIN_NAMESPACE - -#ifndef QT_NO_DEBUG -QT_BEGIN_INCLUDE_NAMESPACE -# include -QT_END_INCLUDE_NAMESPACE -static inline bool debug_accessibility() -{ - static signed int debugging = -1; - if (debugging == -1) - debugging = qgetenv("QT_DEBUG_ACCESSIBILITY").toInt(); - return !!debugging; -} -# define accessibleDebug !debug_accessibility() ? (void)0 : qDebug -#else -# define accessibleDebug() -#endif - -//#define DEBUG_SHOW_ATCLIENT_COMMANDS -#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)); -} -# define accessibleDebugClientCalls(iface) accessibleDebugClientCalls_helper(Q_FUNC_INFO, iface) -#else -# define accessibleDebugClientCalls(iface) -#endif - - -typedef QSharedPointer QAIPointer; - -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; -} - -// This stuff is used for widgets/items with no window handle: -typedef QMap > NotifyMap; -Q_GLOBAL_STATIC(NotifyMap, qAccessibleRecentSentEvents) -static int eventNum = 0; - -class QWindowsEnumerate : public IEnumVARIANT -{ -public: - QWindowsEnumerate(const QVector &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 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; -} - -/* -*/ -class QWindowsAccessible : public IAccessible, IOleWindow -{ -public: - QWindowsAccessible(QAccessibleInterface *a) - : ref(0), accessible(a) - { - } - - virtual ~QWindowsAccessible() - { - delete accessible; - } - - /* IUnknown */ - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *); - ULONG STDMETHODCALLTYPE AddRef(); - ULONG STDMETHODCALLTYPE Release(); - - /* IDispatch */ - HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int *); - HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int, unsigned long, ITypeInfo **); - HRESULT STDMETHODCALLTYPE GetIDsOfNames(const _GUID &, wchar_t **, unsigned int, unsigned long, long *); - HRESULT STDMETHODCALLTYPE Invoke(long, const _GUID &, unsigned long, unsigned short, tagDISPPARAMS *, tagVARIANT *, tagEXCEPINFO *, unsigned int *); - - /* IAccessible */ - HRESULT STDMETHODCALLTYPE accHitTest(long xLeft, long yTop, VARIANT *pvarID); - HRESULT STDMETHODCALLTYPE accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID); - HRESULT STDMETHODCALLTYPE accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd); - HRESULT STDMETHODCALLTYPE get_accChild(VARIANT varChildID, IDispatch** ppdispChild); - HRESULT STDMETHODCALLTYPE get_accChildCount(long* pcountChildren); - HRESULT STDMETHODCALLTYPE get_accParent(IDispatch** ppdispParent); - - HRESULT STDMETHODCALLTYPE accDoDefaultAction(VARIANT varID); - HRESULT STDMETHODCALLTYPE get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction); - HRESULT STDMETHODCALLTYPE get_accDescription(VARIANT varID, BSTR* pszDescription); - HRESULT STDMETHODCALLTYPE get_accHelp(VARIANT varID, BSTR *pszHelp); - HRESULT STDMETHODCALLTYPE get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild, long *pidTopic); - HRESULT STDMETHODCALLTYPE get_accKeyboardShortcut(VARIANT varID, BSTR *pszKeyboardShortcut); - HRESULT STDMETHODCALLTYPE get_accName(VARIANT varID, BSTR* pszName); - HRESULT STDMETHODCALLTYPE put_accName(VARIANT varChild, BSTR szName); - HRESULT STDMETHODCALLTYPE get_accRole(VARIANT varID, VARIANT *pvarRole); - HRESULT STDMETHODCALLTYPE get_accState(VARIANT varID, VARIANT *pvarState); - HRESULT STDMETHODCALLTYPE get_accValue(VARIANT varID, BSTR* pszValue); - HRESULT STDMETHODCALLTYPE put_accValue(VARIANT varChild, BSTR szValue); - - HRESULT STDMETHODCALLTYPE accSelect(long flagsSelect, VARIANT varID); - HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT *pvarID); - HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT *pvarChildren); - - /* IOleWindow */ - HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd); - HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode); - -private: - ULONG ref; - QAccessibleInterface *accessible; - - QAIPointer childPointer(VARIANT varID) - { - return QAIPointer(accessible->child(varID.lVal - 1)); - } -}; - -static inline BSTR QStringToBSTR(const QString &str) -{ - BSTR bstrVal; - - int wlen = str.length()+1; - bstrVal = SysAllocStringByteLen(0, wlen*2); - memcpy(bstrVal, str.unicode(), sizeof(QChar)*(wlen)); - bstrVal[wlen] = 0; - - return bstrVal; -} - -/* -*/ - -/* - IUnknown -*/ -HRESULT STDMETHODCALLTYPE QWindowsAccessible::QueryInterface(REFIID id, LPVOID *iface) -{ - *iface = 0; - if (id == IID_IUnknown) - *iface = (IUnknown*)(IDispatch*)this; - else if (id == IID_IDispatch) - *iface = (IDispatch*)this; - else if (id == IID_IAccessible) - *iface = (IAccessible*)this; - else if (id == IID_IOleWindow) - *iface = (IOleWindow*)this; - else - return E_NOINTERFACE; - - AddRef(); - return S_OK; -} - -ULONG STDMETHODCALLTYPE QWindowsAccessible::AddRef() -{ - return ++ref; -} - -ULONG STDMETHODCALLTYPE QWindowsAccessible::Release() -{ - if (!--ref) { - delete this; - return 0; - } - return ref; -} - -/* - IDispatch -*/ - -HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetTypeInfoCount(unsigned int * pctinfo) -{ - // We don't use a type library - *pctinfo = 0; - return S_OK; -} - -HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetTypeInfo(unsigned int, unsigned long, ITypeInfo **pptinfo) -{ - // We don't use a type library - *pptinfo = 0; - return S_OK; -} - -HRESULT STDMETHODCALLTYPE QWindowsAccessible::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 QWindowsAccessible::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 QWindowsAccessible::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 { - QWindowsAccessible* wacc = new QWindowsAccessible(child); - IDispatch *iface = 0; - wacc->QueryInterface(IID_IDispatch, (void**)&iface); - 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 QWindowsAccessible::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 QWindowsAccessible::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; - } - QWindowsAccessible* wacc = new QWindowsAccessible(acc); - - IDispatch *iface = 0; - wacc->QueryInterface(IID_IDispatch, (void**)&iface); - if (iface) { - (*pvarEnd).vt = VT_DISPATCH; - (*pvarEnd).pdispVal = iface; - return S_OK; - } else { - delete wacc; - } - - (*pvarEnd).vt = VT_EMPTY; - return S_FALSE; -} - -// moz: [important] -HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild(VARIANT varChildID, IDispatch** ppdispChild) -{ - accessibleDebugClientCalls(accessible); - if (!accessible->isValid()) - return E_FAIL; - - if (varChildID.vt == VT_EMPTY) - return E_INVALIDARG; - - - int childIndex = varChildID.lVal; - QAccessibleInterface *acc = 0; - - if (childIndex < 0) { - const int entry = childIndex; - QPair ref = qAccessibleRecentSentEvents()->value(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 { - 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) { - QWindowsAccessible* wacc = new QWindowsAccessible(acc); - wacc->QueryInterface(IID_IDispatch, (void**)ppdispChild); - return S_OK; - } - - *ppdispChild = 0; - return S_FALSE; -} - -// moz: [important] -HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChildCount(long* pcountChildren) -{ - accessibleDebugClientCalls(accessible); - if (!accessible->isValid()) - return E_FAIL; - - *pcountChildren = accessible->childCount(); - return S_OK; -} - -// moz: [important] -HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accParent(IDispatch** ppdispParent) -{ - accessibleDebugClientCalls(accessible); - if (!accessible->isValid()) - return E_FAIL; - - QAccessibleInterface *acc = accessible->parent(); - if (acc) { - QWindowsAccessible* wacc = new QWindowsAccessible(acc); - wacc->QueryInterface(IID_IDispatch, (void**)ppdispParent); - - if (*ppdispParent) - return S_OK; - } - - *ppdispParent = 0; - return S_FALSE; -} - -/* - Properties and methods -*/ -HRESULT STDMETHODCALLTYPE QWindowsAccessible::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 QWindowsAccessible::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 QWindowsAccessible::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 QWindowsAccessible::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 QWindowsAccessible::get_accHelpTopic(BSTR *, VARIANT, long *) -{ - return DISP_E_MEMBERNOTFOUND; -} - -HRESULT STDMETHODCALLTYPE QWindowsAccessible::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 RelationPair; - QVector 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 QWindowsAccessible::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 QWindowsAccessible::put_accName(VARIANT, BSTR) -{ - accessibleDebugClientCalls(accessible); - return DISP_E_MEMBERNOTFOUND; -} - -// moz: [important] -HRESULT STDMETHODCALLTYPE QWindowsAccessible::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 QWindowsAccessible::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 QWindowsAccessible::get_accValue(VARIANT varID, BSTR* pszValue) -{ - accessibleDebugClientCalls(accessible); - 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; - return S_FALSE; -} - -HRESULT STDMETHODCALLTYPE QWindowsAccessible::put_accValue(VARIANT, BSTR) -{ - accessibleDebugClientCalls(accessible); - return DISP_E_MEMBERNOTFOUND; -} - -// moz: [important] -HRESULT STDMETHODCALLTYPE QWindowsAccessible::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 QWindowsAccessible::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 { - QWindowsAccessible* wacc = new QWindowsAccessible(acc); - IDispatch *iface = 0; - wacc->QueryInterface(IID_IDispatch, (void**)&iface); - (*pvarID).vt = VT_DISPATCH; - (*pvarID).pdispVal = iface; - return S_OK; - } - delete acc; - } - (*pvarID).vt = VT_EMPTY; - return S_FALSE; -} - -HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accSelection(VARIANT *pvarChildren) -{ - accessibleDebugClientCalls(accessible); - if (!accessible->isValid()) - return E_FAIL; - - int cc = accessible->childCount(); - QVector 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; -} - -static QWindow *window_helper(const QAccessibleInterface *iface) -{ - QWindow *window = iface->window(); - if (!window) { - QAccessibleInterface *acc = iface->parent(); - while (acc && !window) { - window = acc->window(); - QAccessibleInterface *par = acc->parent(); - delete acc; - acc = par; - } - } - return window; -} - -HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetWindow(HWND *phwnd) -{ - *phwnd = 0; - if (!accessible->isValid()) - return E_UNEXPECTED; - - QWindow *window = window_helper(accessible); - if (!window) - return E_FAIL; - - QPlatformNativeInterface *platform = QGuiApplication::platformNativeInterface(); - Q_ASSERT(platform); - *phwnd = (HWND)platform->nativeResourceForWindow("handle", window); - return S_OK; -} - -HRESULT STDMETHODCALLTYPE QWindowsAccessible::ContextSensitiveHelp(BOOL) -{ - return S_OK; -} - - -QWindowsAccessibility::QWindowsAccessibility() -{ -} - - -void QWindowsAccessibility::notifyAccessibilityUpdate(const QAccessibleEvent &event) -{ - QString soundName; - switch (event.type()) { - case QAccessible::PopupMenuStart: - soundName = QLatin1String("MenuPopup"); - break; - - case QAccessible::MenuCommand: - soundName = QLatin1String("MenuCommand"); - break; - - case QAccessible::Alert: - { - /* ### FIXME -#ifndef QT_NO_MESSAGEBOX - QMessageBox *mb = qobject_cast(o); - if (mb) { - switch (mb->icon()) { - case QMessageBox::Warning: - soundName = QLatin1String("SystemExclamation"); - break; - case QMessageBox::Critical: - soundName = QLatin1String("SystemHand"); - break; - case QMessageBox::Information: - soundName = QLatin1String("SystemAsterisk"); - break; - default: - break; - } - } else -#endif // QT_NO_MESSAGEBOX -*/ - { - soundName = QLatin1String("SystemAsterisk"); - } - - } - break; - default: - break; - } - - if (!soundName.isEmpty()) { -#ifndef QT_NO_SETTINGS - QSettings settings(QLatin1String("HKEY_CURRENT_USER\\AppEvents\\Schemes\\Apps\\.Default\\") + soundName, - QSettings::NativeFormat); - QString file = settings.value(QLatin1String(".Current/.")).toString(); -#else - QString file; -#endif - if (!file.isEmpty()) { - PlaySound(reinterpret_cast(soundName.utf16()), 0, SND_ALIAS | SND_ASYNC | SND_NODEFAULT | SND_NOWAIT); - } - } - - typedef void (WINAPI *PtrNotifyWinEvent)(DWORD, HWND, LONG, LONG); - -#if defined(Q_OS_WINCE) // ### TODO: check for NotifyWinEvent in CE 6.0 - // There is no user32.lib nor NotifyWinEvent for CE - return; -#else - static PtrNotifyWinEvent ptrNotifyWinEvent = 0; - static bool resolvedNWE = false; - if (!resolvedNWE) { - resolvedNWE = true; - ptrNotifyWinEvent = (PtrNotifyWinEvent)QSystemLibrary::resolve(QLatin1String("user32"), "NotifyWinEvent"); - } - if (!ptrNotifyWinEvent) - return; - - // An event has to be associated with a window, - // so find the first parent that is a widget and that has a WId - QAccessibleInterface *iface = event.accessibleInterface(); - QWindow *window = iface ? window_helper(iface) : 0; - delete iface; - - if (!window) { - window = QGuiApplication::activeWindow(); - if (!window) - return; - } - - QPlatformNativeInterface *platform = QGuiApplication::platformNativeInterface(); - HWND hWnd = (HWND)platform->nativeResourceForWindow("handle", window); - - if (event.type() != QAccessible::MenuCommand) { // MenuCommand is faked - // See comment "SENDING EVENTS TO OBJECTS WITH NO WINDOW HANDLE" - eventNum %= 50; //[0..49] - int eventId = - eventNum - 1; - - qAccessibleRecentSentEvents()->insert(eventId, qMakePair(event.object(), event.child())); - ptrNotifyWinEvent(event.type(), hWnd, OBJID_CLIENT, eventId ); - - ++eventNum; - } -#endif // Q_OS_WINCE -} - -/* -void QWindowsAccessibility::setRootObject(QObject *o) -{ - -} - -void QWindowsAccessibility::initialize() -{ - -} - -void QWindowsAccessibility::cleanup() -{ - -} - -*/ - -bool QWindowsAccessibility::handleAccessibleObjectFromWindowRequest(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult) -{ - if (static_cast(lParam) == static_cast(UiaRootObjectId)) { - /* For UI Automation - */ - } else if ((DWORD)lParam == OBJID_CLIENT) { -#if 1 - // Ignoring all requests while starting up - // ### Maybe QPA takes care of this??? - if (QApplication::startingUp() || QApplication::closingDown()) - return false; -#endif - - typedef LRESULT (WINAPI *PtrLresultFromObject)(REFIID, WPARAM, LPUNKNOWN); - static PtrLresultFromObject ptrLresultFromObject = 0; - static bool oleaccChecked = false; - - if (!oleaccChecked) { - oleaccChecked = true; -#if !defined(Q_OS_WINCE) - ptrLresultFromObject = (PtrLresultFromObject)QSystemLibrary::resolve(QLatin1String("oleacc"), "LresultFromObject"); -#endif - } - - if (ptrLresultFromObject) { - QWindow *window = QWindowsContext::instance()->findWindow(hwnd); - if (window) { - QAccessibleInterface *acc = window->accessibleRoot(); - if (acc) { - QWindowsAccessible *winacc = new QWindowsAccessible(acc); - IAccessible *iface; - HRESULT hr = winacc->QueryInterface(IID_IAccessible, (void**)&iface); - if (SUCCEEDED(hr)) { - *lResult = ptrLresultFromObject(IID_IAccessible, wParam, iface); // ref == 2 - if (*lResult) { - iface->Release(); // the client will release the object again, and then it will destroy itself - } - return true; - } - } - } - } - } - return false; -} - -QT_END_NAMESPACE - -#endif //QT_NO_ACCESSIBILITY diff --git a/src/plugins/platforms/windows/qwindowsaccessibility.h b/src/plugins/platforms/windows/qwindowsaccessibility.h deleted file mode 100644 index 9294f23565..0000000000 --- a/src/plugins/platforms/windows/qwindowsaccessibility.h +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QWINDOWSACCESSIBILITY_H -#define QWINDOWSACCESSIBILITY_H - -#include "qtwindowsglobal.h" -#include - -QT_BEGIN_HEADER -QT_BEGIN_NAMESPACE - -class QWindowsAccessibility : public QPlatformAccessibility -{ -public: - QWindowsAccessibility(); - static bool handleAccessibleObjectFromWindowRequest(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult); - virtual void notifyAccessibilityUpdate(const QAccessibleEvent &event); - /* - virtual void setRootObject(QObject *o); - virtual void initialize(); - virtual void cleanup(); - */ -}; - -QT_END_NAMESPACE -QT_END_HEADER - -#endif // QWINDOWSACCESSIBILITY_H diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 2706e02cf8..e8050d5027 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -47,7 +47,7 @@ #include "qtwindowsglobal.h" #include "qwindowsmime.h" #include "qwindowsinputcontext.h" -#include "qwindowsaccessibility.h" +#include "accessible/qwindowsaccessibility.h" #include "qwindowsscreen.h" #include "qwindowstheme.h" diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 3c2ece58d0..7510cded50 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -55,8 +55,8 @@ #include "qwindowsclipboard.h" #include "qwindowsdrag.h" #include "qwindowsinputcontext.h" -#include "qwindowsaccessibility.h" #include "qwindowskeymapper.h" +#include "accessible/qwindowsaccessibility.h" #include #include diff --git a/src/plugins/platforms/windows/windows.pro b/src/plugins/platforms/windows/windows.pro index bb41fe87fc..87bc7245c4 100644 --- a/src/plugins/platforms/windows/windows.pro +++ b/src/plugins/platforms/windows/windows.pro @@ -45,7 +45,6 @@ SOURCES += \ qwindowsdrag.cpp \ qwindowscursor.cpp \ qwindowsinputcontext.cpp \ - qwindowsaccessibility.cpp \ qwindowstheme.cpp \ qwindowsdialoghelpers.cpp \ qwindowsservices.cpp @@ -73,7 +72,6 @@ HEADERS += \ qwindowscursor.h \ array.h \ qwindowsinputcontext.h \ - qwindowsaccessibility.h \ qwindowstheme.h \ qwindowsdialoghelpers.h \ qwindowsservices.h @@ -151,5 +149,6 @@ contains(QT_CONFIG, freetype) { OTHER_FILES += windows.json +include(accessible/accessible.pri) target.path += $$[QT_INSTALL_PLUGINS]/platforms INSTALLS += target -- cgit v1.2.3