Add Linux Accessibility Bridge
authorFrederik Gladhorn <frederik.gladhorn@nokia.com>
Thu, 10 May 2012 21:52:05 +0000 (23:52 +0200)
committerQt by Nokia <qt-info@nokia.com>
Mon, 13 Aug 2012 23:40:06 +0000 (01:40 +0200)
This is a plugin that bridges the QAccessible world
to AT-SPI 2 on Linux.

Change-Id: I7af22621ee6a3cefc723b137b7f227a611cf6641
Reviewed-by: Jan-Arve Sæther <jan-arve.saether@nokia.com>
26 files changed:
configure
src/3rdparty/atspi2/atspi2.pri [new file with mode: 0644]
src/3rdparty/atspi2/xml/Cache.xml [new file with mode: 0644]
src/3rdparty/atspi2/xml/DeviceEventController.xml [new file with mode: 0644]
src/3rdparty/atspi2/xml/Socket.xml [new file with mode: 0644]
src/platformsupport/linuxaccessibility/application.cpp [new file with mode: 0644]
src/platformsupport/linuxaccessibility/application_p.h [new file with mode: 0644]
src/platformsupport/linuxaccessibility/atspiadaptor.cpp [new file with mode: 0644]
src/platformsupport/linuxaccessibility/atspiadaptor_p.h [new file with mode: 0644]
src/platformsupport/linuxaccessibility/bridge.cpp [new file with mode: 0644]
src/platformsupport/linuxaccessibility/bridge_p.h [new file with mode: 0644]
src/platformsupport/linuxaccessibility/cache.cpp [new file with mode: 0644]
src/platformsupport/linuxaccessibility/cache_p.h [new file with mode: 0644]
src/platformsupport/linuxaccessibility/constant_mappings.cpp [new file with mode: 0644]
src/platformsupport/linuxaccessibility/constant_mappings_p.h [new file with mode: 0644]
src/platformsupport/linuxaccessibility/dbusconnection.cpp [new file with mode: 0644]
src/platformsupport/linuxaccessibility/dbusconnection_p.h [new file with mode: 0644]
src/platformsupport/linuxaccessibility/linuxaccessibility.pri [new file with mode: 0644]
src/platformsupport/linuxaccessibility/main.cpp [new file with mode: 0644]
src/platformsupport/linuxaccessibility/struct_marshallers.cpp [new file with mode: 0644]
src/platformsupport/linuxaccessibility/struct_marshallers_p.h [new file with mode: 0644]
src/platformsupport/platformsupport.pro
src/plugins/platforms/xcb/qxcbintegration.cpp
tests/auto/other/other.pro
tests/auto/other/qaccessibilitylinux/qaccessibilitylinux.pro [new file with mode: 0644]
tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp [new file with mode: 0644]

index 74d7367..10cc344 100755 (executable)
--- a/configure
+++ b/configure
@@ -3012,7 +3012,7 @@ Configure options:
  +  -largefile ......... Enables Qt to access files larger than 4 GB.
 
     -no-accessibility .. Do not compile Accessibility support.
*  -accessibility ..... Compile Accessibility support.
+  -accessibility ..... Compile Accessibility support.
 
     -no-sql-<driver> ... Disable SQL <driver> entirely.
     -qt-sql-<driver> ... Enable a SQL <driver> in the QtSql library, by default
@@ -3989,11 +3989,6 @@ if [ "$CFG_LIBPNG" = "auto" ]; then
     fi
 fi
 
-# detect accessibility
-if [ "$CFG_ACCESSIBILITY" = "auto" ]; then
-    CFG_ACCESSIBILITY=yes
-fi
-
 if [ "$CFG_EGLFS" = "yes" ]; then
     if [ "$CFG_EGL" = "no" ]; then
         echo "The EGLFS plugin requires EGL support and cannot be built"
@@ -4842,6 +4837,25 @@ if [ "$CFG_KMS" = "yes" ]; then
     fi
 fi
 
+# Detect accessibility support
+if [ "$CFG_ACCESSIBILITY" != "no" ]; then
+    if [ "$CFG_XCB" = "no" ]; then
+        CFG_ACCESSIBILITY=yes
+    else
+        # linux/xcb accessibility needs dbus and atspi-2
+        if [ "$CFG_DBUS" != "no" ] && [ -n "$PKG_CONFIG" ] && $PKG_CONFIG --exists "atspi-2" 2>/dev/null; then
+            CFG_ACCESSIBILITY=yes
+        else
+            if [ "$CFG_ACCESSIBILITY" = "auto" ]; then
+                CFG_ACCESSIBILITY=no
+            else
+                echo "Accessibility support needs pkg-config and libatspi2."
+                exit 101
+            fi
+        fi
+    fi
+fi
+
 # Determine the default QPA platform
 if [ -z "$QT_QPA_DEFAULT_PLATFORM" ]; then
     # check the mkspec
diff --git a/src/3rdparty/atspi2/atspi2.pri b/src/3rdparty/atspi2/atspi2.pri
new file mode 100644 (file)
index 0000000..080a805
--- /dev/null
@@ -0,0 +1,6 @@
+
+DBUS_ADAPTORS = $$PWD/xml/Cache.xml $$PWD/xml/DeviceEventController.xml
+QDBUSXML2CPP_ADAPTOR_HEADER_FLAGS = -i struct_marshallers_p.h
+
+DBUS_INTERFACES = $$PWD/xml/Socket.xml
+QDBUSXML2CPP_INTERFACE_HEADER_FLAGS = -i struct_marshallers_p.h
diff --git a/src/3rdparty/atspi2/xml/Cache.xml b/src/3rdparty/atspi2/xml/Cache.xml
new file mode 100644 (file)
index 0000000..9d0c580
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/node">
+<interface name="org.a11y.atspi.Cache">
+
+  <method name="GetItems">
+    <arg name="nodes" type="a((so)(so)a(so)assusau)" direction="out"/>
+    <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QSpiAccessibleCacheArray"/>
+  </method>
+
+  <signal name="AddAccessible">
+    <arg name="nodeAdded" type="((so)(so)a(so)assusau)"/>
+    <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QSpiAccessibleCacheItem"/>
+  </signal>
+
+  <signal name="RemoveAccessible">
+    <arg name="nodeRemoved" type="(so)"/>
+    <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QSpiObjectReference"/>
+  </signal>
+
+</interface>
+</node>
diff --git a/src/3rdparty/atspi2/xml/DeviceEventController.xml b/src/3rdparty/atspi2/xml/DeviceEventController.xml
new file mode 100644 (file)
index 0000000..d4c26ef
--- /dev/null
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/node">
+<interface name="org.a11y.atspi.DeviceEventController">
+
+<!--
+  <method name="RegisterKeystrokeListener">
+    <arg direction="in" name="listener" type="o"/>
+    <arg direction="in" name="keys" type="a(iisi)">
+      <annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="QSpiKeyTypeArray"/>
+    </arg>
+    <arg direction="in" name="mask" type="u"/>
+    <arg direction="in" name="type" type="au">
+      <annotation name="com.trolltech.QtDBus.QtTypeName.In3" value="QSpiEventTypeArray"/>
+    </arg>
+    <arg direction="in" name="mode" type="(bbb)">
+      <annotation name="com.trolltech.QtDBus.QtTypeName.In4" value="QSpiEventMode"/>
+    </arg>
+    <arg direction="out" type="b"/>
+  </method>
+
+  <method name="DeregisterKeystrokeListener">
+    <arg direction="in" name="listener" type="o"/>
+    <arg direction="in" name="keys" type="a(iisi)">
+      <annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="QSpiKeyTypeArray"/>
+    </arg>
+    <arg direction="in" name="mask" type="u"/>
+    <arg direction="in" name="type" type="u"/>
+  </method>
+
+  <method name="RegisterDeviceEventListener">
+    <arg direction="in" name="listener" type="o"/>
+    <arg direction="in" name="types" type="u"/>
+    <arg direction="out" type="b"/>
+  </method>
+
+  <method name="DeregisterDeviceEventListener">
+    <arg direction="in" name="listener" type="o"/>
+    <arg direction="in" name="types" type="u"/>
+  </method>
+
+  <method name="GenerateKeyboardEvent">
+    <arg direction="in" name="keycode" type="i"/>
+    <arg direction="in" name="keystring" type="s"/>
+    <arg direction="in" name="type" type="u"/>
+  </method>
+
+  <method name="GenerateMouseEvent">
+    <arg direction="in" name="x" type="i"/>
+    <arg direction="in" name="y" type="i"/>
+    <arg direction="in" name="eventName" type="s"/>
+  </method>
+-->
+
+  <method name="NotifyListenersSync">
+    <arg direction="in" name="event" type="(uinnisb)"/>
+    <arg direction="out" type="b"/>
+    <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QSpiDeviceEvent"/>
+  </method>
+
+  <method name="NotifyListenersAsync">
+    <arg direction="in" name="event" type="(uinnisb)"/>
+    <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QSpiDeviceEvent"/>
+  </method>
+
+</interface>
+</node>
diff --git a/src/3rdparty/atspi2/xml/Socket.xml b/src/3rdparty/atspi2/xml/Socket.xml
new file mode 100644 (file)
index 0000000..75ec99f
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/node">
+<interface name="org.a11y.atspi.Socket">
+
+  <method name="Embed">
+    <arg direction="in" name="plug" type="(so)"/>
+    <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QSpiObjectReference"/>
+    <arg direction="out" name="socket" type="(so)"/>
+    <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QSpiObjectReference"/>
+  </method>
+
+  <method name="Unembed">
+    <arg direction="in" name="plug" type="(so)"/>
+    <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QSpiObjectReference"/>
+  </method>
+
+  <signal name="Available">
+    <arg direction="in" name="socket" type="(so)"/>
+    <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QSpiObjectReference"/>
+  </method>
+
+</interface>
+</node>
diff --git a/src/platformsupport/linuxaccessibility/application.cpp b/src/platformsupport/linuxaccessibility/application.cpp
new file mode 100644 (file)
index 0000000..84f5cef
--- /dev/null
@@ -0,0 +1,219 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 "application_p.h"
+
+#include <QtWidgets/qapplication.h>
+#include <QtDBus/qdbuspendingreply.h>
+#include <qdebug.h>
+
+#include "deviceeventcontroller_adaptor.h"
+
+//#define KEYBOARD_DEBUG
+
+QT_BEGIN_NAMESPACE
+
+/*!
+    \class QSpiApplicationAdaptor
+
+    \brief QSpiApplicationAdaptor
+
+    QSpiApplicationAdaptor
+*/
+
+QSpiApplicationAdaptor::QSpiApplicationAdaptor(const QDBusConnection &connection, QObject *parent)
+    : QObject(parent), dbusConnection(connection)
+{
+}
+
+enum QSpiKeyEventType {
+      QSPI_KEY_EVENT_PRESS,
+      QSPI_KEY_EVENT_RELEASE,
+      QSPI_KEY_EVENT_LAST_DEFINED
+};
+
+void QSpiApplicationAdaptor::sendEvents(bool active)
+{
+    if (active) {
+        qApp->installEventFilter(this);
+    } else {
+        qApp->removeEventFilter(this);
+    }
+}
+
+
+bool QSpiApplicationAdaptor::eventFilter(QObject *target, QEvent *event)
+{
+    if (!event->spontaneous())
+        return false;
+
+    switch (event->type()) {
+    case QEvent::WindowActivate:
+        emit windowActivated(target, true);
+        break;
+    case QEvent::WindowDeactivate:
+        emit windowActivated(target, false);
+        break;
+    case QEvent::KeyPress:
+    case QEvent::KeyRelease: {
+        QKeyEvent *keyEvent = static_cast <QKeyEvent *>(event);
+        QSpiDeviceEvent de;
+
+        if (event->type() == QEvent::KeyPress)
+            de.type = QSPI_KEY_EVENT_PRESS;
+        else
+            de.type = QSPI_KEY_EVENT_RELEASE;
+
+        de.id = keyEvent->nativeVirtualKey();
+        de.hardwareCode = keyEvent->nativeScanCode();
+
+        de.modifiers = keyEvent->nativeModifiers();
+        de.timestamp = QDateTime::currentMSecsSinceEpoch();
+
+        if (keyEvent->key() == Qt::Key_Tab)
+            de.text = QStringLiteral("Tab");
+        else if (keyEvent->key() == Qt::Key_Backtab)
+            de.text = QStringLiteral("Backtab");
+        else if (keyEvent->key() == Qt::Key_Left)
+            de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Left") : QStringLiteral("Left");
+        else if (keyEvent->key() == Qt::Key_Right)
+            de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Right") : QStringLiteral("Right");
+        else if (keyEvent->key() == Qt::Key_Up)
+            de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Up") : QStringLiteral("Up");
+        else if (keyEvent->key() == Qt::Key_Down)
+            de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Down") : QStringLiteral("Down");
+        else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)
+            de.text = QStringLiteral("Return");
+        else if (keyEvent->key() == Qt::Key_Backspace)
+            de.text = QStringLiteral("BackSpace");
+        else if (keyEvent->key() == Qt::Key_Delete)
+            de.text = QStringLiteral("Delete");
+        else if (keyEvent->key() == Qt::Key_PageUp)
+            de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Page_Up") : QStringLiteral("Page_Up");
+        else if (keyEvent->key() == Qt::Key_PageDown)
+            de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Page_Up") : QStringLiteral("Page_Down");
+        else if (keyEvent->key() == Qt::Key_Home)
+            de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_Home") : QStringLiteral("Home");
+        else if (keyEvent->key() == Qt::Key_End)
+            de.text = (keyEvent->modifiers() & Qt::KeypadModifier) ? QStringLiteral("KP_End") : QStringLiteral("End");
+        else if (keyEvent->key() == Qt::Key_Clear && (keyEvent->modifiers() & Qt::KeypadModifier))
+            de.text = QStringLiteral("KP_Begin"); // Key pad 5
+        else if (keyEvent->key() == Qt::Key_Escape)
+            de.text = QStringLiteral("Escape");
+        else if (keyEvent->key() == Qt::Key_Space)
+            de.text = QStringLiteral("space");
+        else if (keyEvent->key() == Qt::Key_CapsLock)
+            de.text = QStringLiteral("Caps_Lock");
+        else if (keyEvent->key() == Qt::Key_NumLock)
+            de.text = QStringLiteral("Num_Lock");
+        else if (keyEvent->key() == Qt::Key_Insert)
+            de.text = QStringLiteral("Insert");
+        else
+            de.text = keyEvent->text();
+
+        // This is a bit dubious, Gnome uses some gtk function here.
+        // Long term the spec will hopefully change to just use keycodes.
+        de.isText = !de.text.isEmpty();
+
+#ifdef KEYBOARD_DEBUG
+        qDebug() << QStringLiteral("Key event text: ") << event->type() << de.isText << QStringLiteral(" ") << de.text
+                 << QStringLiteral(" hardware code: ") << de.hardwareCode
+                 << QStringLiteral(" native sc: ") << keyEvent->nativeScanCode()
+                 << QStringLiteral(" native mod: ") << keyEvent->nativeModifiers()
+                 << QStringLiteral("native virt: ") << keyEvent->nativeVirtualKey();
+#endif
+
+        QDBusMessage m = QDBusMessage::createMethodCall(QStringLiteral("org.a11y.atspi.Registry"),
+                                                        QStringLiteral("/org/a11y/atspi/registry/deviceeventcontroller"),
+                                                        QStringLiteral("org.a11y.atspi.DeviceEventController"), QStringLiteral("NotifyListenersSync"));
+        m.setArguments(QVariantList() <<QVariant::fromValue(de));
+
+        // FIXME: this is critical, the timeout should probably be pretty low to allow normal processing
+        int timeout = 100;
+        bool sent = dbusConnection.callWithCallback(m, this, SLOT(notifyKeyboardListenerCallback(QDBusMessage)),
+                        SLOT(notifyKeyboardListenerError(QDBusError, QDBusMessage)), timeout);
+        if (sent) {
+            //queue the event and send it after callback
+            keyEvents.enqueue(QPair<QObject*, QKeyEvent*> (target, copyKeyEvent(keyEvent)));
+#ifdef KEYBOARD_DEBUG
+            qDebug() << QStringLiteral("Sent key: ") << de.text;
+#endif
+            return true;
+        }
+    }
+    default:
+        break;
+    }
+    return false;
+}
+
+QKeyEvent* QSpiApplicationAdaptor::copyKeyEvent(QKeyEvent* old)
+{
+    return new QKeyEvent(old->type(), old->key(), old->modifiers(), old->text(), old->isAutoRepeat(), old->count());
+}
+
+void QSpiApplicationAdaptor::notifyKeyboardListenerCallback(const QDBusMessage& message)
+{
+    if (!keyEvents.length()) {
+        qWarning() << QStringLiteral("QSpiApplication::notifyKeyboardListenerCallback with no queued key called");
+        return;
+    }
+    Q_ASSERT(message.arguments().length() == 1);
+    if (message.arguments().at(0).toBool() == true) {
+        QPair<QObject*, QKeyEvent*> event = keyEvents.dequeue();
+        delete event.second;
+    } else {
+        QPair<QObject*, QKeyEvent*> event = keyEvents.dequeue();
+        QApplication::postEvent(event.first, event.second);
+    }
+}
+
+void QSpiApplicationAdaptor::notifyKeyboardListenerError(const QDBusError& error, const QDBusMessage& /*message*/)
+{
+    qWarning() << QStringLiteral("QSpiApplication::keyEventError ") << error.name() << error.message();
+    while (!keyEvents.isEmpty()) {
+        QPair<QObject*, QKeyEvent*> event = keyEvents.dequeue();
+        QApplication::postEvent(event.first, event.second);
+    }
+}
+
+QT_END_NAMESPACE
diff --git a/src/platformsupport/linuxaccessibility/application_p.h b/src/platformsupport/linuxaccessibility/application_p.h
new file mode 100644 (file)
index 0000000..01ebc16
--- /dev/null
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 Q_SPI_APPLICATION_H
+#define Q_SPI_APPLICATION_H
+
+#include <QtCore/QQueue>
+#include <QtDBus/QDBusConnection>
+#include <QtGui/QAccessibleInterface>
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+/*
+ * Used for the root object.
+ *
+ * Uses the root object reference and reports its parent as the desktop object.
+ */
+class QSpiApplicationAdaptor :public QObject
+{
+    Q_OBJECT
+
+public:
+    QSpiApplicationAdaptor(const QDBusConnection &connection, QObject *parent);
+    virtual ~QSpiApplicationAdaptor() {}
+    void sendEvents(bool active);
+
+Q_SIGNALS:
+    void windowActivated(QObject* window, bool active);
+
+protected:
+    bool eventFilter(QObject *obj, QEvent *event);
+
+private Q_SLOTS:
+    void notifyKeyboardListenerCallback(const QDBusMessage& message);
+    void notifyKeyboardListenerError(const QDBusError& error, const QDBusMessage& message);
+
+private:
+    static QKeyEvent* copyKeyEvent(QKeyEvent*);
+
+    QQueue<QPair<QObject*, QKeyEvent*> > keyEvents;
+    QDBusConnection dbusConnection;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#endif
diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp
new file mode 100644 (file)
index 0000000..f9aedb4
--- /dev/null
@@ -0,0 +1,2316 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 "atspiadaptor_p.h"
+
+#include <QtGui/qwindow.h>
+#include <QtWidgets/qapplication.h>
+#include <qdbusmessage.h>
+#include <qdbusreply.h>
+#include <QtWidgets/qwidget.h>
+#include <qclipboard.h>
+
+#include <qdebug.h>
+
+#include "socket_interface.h"
+#include "constant_mappings_p.h"
+
+#include "application_p.h"
+/*!
+    \class AtSpiAdaptor
+
+    \brief AtSpiAdaptor is the main class to forward between QAccessibleInterface and AT-SPI DBus
+
+    AtSpiAdaptor implements the functions specified in all at-spi interfaces.
+    It sends notifications comming from Qt via dbus and listens to incoming dbus requests.
+*/
+
+QT_BEGIN_NAMESPACE
+
+AtSpiAdaptor::AtSpiAdaptor(DBusConnection *connection, QObject *parent)
+    : QDBusVirtualObject(parent), m_dbus(connection), initialized(false)
+    , sendFocus(0)
+    , sendObject(0)
+    , sendObject_active_descendant_changed(0)
+    , sendObject_attributes_changed(0)
+    , sendObject_bounds_changed(0)
+    , sendObject_children_changed(0)
+//    , sendObject_children_changed_add(0)
+//    , sendObject_children_changed_remove(0)
+    , sendObject_column_deleted(0)
+    , sendObject_column_inserted(0)
+    , sendObject_column_reordered(0)
+    , sendObject_link_selected(0)
+    , sendObject_model_changed(0)
+    , sendObject_property_change(0)
+    , sendObject_property_change_accessible_description(0)
+    , sendObject_property_change_accessible_name(0)
+    , sendObject_property_change_accessible_parent(0)
+    , sendObject_property_change_accessible_role(0)
+    , sendObject_property_change_accessible_table_caption(0)
+    , sendObject_property_change_accessible_table_column_description(0)
+    , sendObject_property_change_accessible_table_column_header(0)
+    , sendObject_property_change_accessible_table_row_description(0)
+    , sendObject_property_change_accessible_table_row_header(0)
+    , sendObject_property_change_accessible_table_summary(0)
+    , sendObject_property_change_accessible_value(0)
+    , sendObject_row_deleted(0)
+    , sendObject_row_inserted(0)
+    , sendObject_row_reordered(0)
+    , sendObject_selection_changed(0)
+    , sendObject_text_attributes_changed(0)
+    , sendObject_text_bounds_changed(0)
+    , sendObject_text_caret_moved(0)
+    , sendObject_text_changed(0)
+//    , sendObject_text_changed_delete(0)
+//    , sendObject_text_changed_insert(0)
+    , sendObject_text_selection_changed(0)
+    , sendObject_value_changed(0)
+    , sendObject_visible_data_changed(0)
+    , sendWindow(0)
+    , sendWindow_activate(0)
+    , sendWindow_close(0)
+    , sendWindow_create(0)
+    , sendWindow_deactivate(0)
+//    , sendWindow_desktop_create(0)
+//    , sendWindow_desktop_destroy(0)
+    , sendWindow_lower(0)
+    , sendWindow_maximize(0)
+    , sendWindow_minimize(0)
+    , sendWindow_move(0)
+    , sendWindow_raise(0)
+    , sendWindow_reparent(0)
+    , sendWindow_resize(0)
+    , sendWindow_restore(0)
+    , sendWindow_restyle(0)
+    , sendWindow_shade(0)
+    , sendWindow_unshade(0)
+{
+    m_applicationAdaptor = new QSpiApplicationAdaptor(m_dbus->connection(), this);
+    connect(m_applicationAdaptor, SIGNAL(windowActivated(QObject*,bool)), this, SLOT(windowActivated(QObject*,bool)));
+}
+
+AtSpiAdaptor::~AtSpiAdaptor()
+{
+}
+
+/*!
+  Provide DBus introspection.
+  */
+QString AtSpiAdaptor::introspect(const QString &path) const
+{
+    static const QLatin1String accessibleIntrospection(
+                "  <interface name=\"org.a11y.atspi.Accessible\">\n"
+                "    <property access=\"read\" type=\"s\" name=\"Name\"/>\n"
+                "    <property access=\"read\" type=\"s\" name=\"Description\"/>\n"
+                "    <property access=\"read\" type=\"(so)\" name=\"Parent\">\n"
+                "      <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName\"/>\n"
+                "    </property>\n"
+                "    <property access=\"read\" type=\"i\" name=\"ChildCount\"/>\n"
+                "    <method name=\"GetChildAtIndex\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
+                "      <arg direction=\"out\" type=\"(so)\"/>\n"
+                "      <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetChildren\">\n"
+                "      <arg direction=\"out\" type=\"a(so)\"/>\n"
+                "      <annotation value=\"QSpiObjectReferenceArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetIndexInParent\">\n"
+                "      <arg direction=\"out\" type=\"i\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetRelationSet\">\n"
+                "      <arg direction=\"out\" type=\"a(ua(so))\"/>\n"
+                "      <annotation value=\"QSpiRelationArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetRole\">\n"
+                "      <arg direction=\"out\" type=\"u\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetRoleName\">\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetLocalizedRoleName\">\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetState\">\n"
+                "      <arg direction=\"out\" type=\"au\"/>\n"
+                "      <annotation value=\"QSpiUIntList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetAttributes\">\n"
+                "      <arg direction=\"out\" type=\"a{ss}\"/>\n"
+                "      <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetApplication\">\n"
+                "      <arg direction=\"out\" type=\"(so)\"/>\n"
+                "      <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "  </interface>\n"
+                );
+
+    static const QLatin1String actionIntrospection(
+                "  <interface name=\"org.a11y.atspi.Action\">\n"
+                "    <property access=\"read\" type=\"i\" name=\"NActions\"/>\n"
+                "    <method name=\"GetDescription\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetName\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetKeyBinding\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetActions\">\n"
+                "      <arg direction=\"out\" type=\"a(sss)\" name=\"index\"/>\n"
+                "      <annotation value=\"QSpiActionArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"DoAction\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "  </interface>\n"
+                );
+
+    static const QLatin1String applicationIntrospection(
+                "  <interface name=\"org.a11y.atspi.Application\">\n"
+                "    <property access=\"read\" type=\"s\" name=\"ToolkitName\"/>\n"
+                "    <property access=\"read\" type=\"s\" name=\"Version\"/>\n"
+                "    <property access=\"readwrite\" type=\"i\" name=\"Id\"/>\n"
+                "    <method name=\"GetLocale\">\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"lctype\"/>\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetApplicationBusAddress\">\n"
+                "      <arg direction=\"out\" type=\"s\" name=\"address\"/>\n"
+                "    </method>\n"
+                "  </interface>\n"
+                );
+
+    static const QLatin1String componentIntrospection(
+                "  <interface name=\"org.a11y.atspi.Component\">\n"
+                "    <method name=\"Contains\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetAccessibleAtPoint\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
+                "      <arg direction=\"out\" type=\"(so)\"/>\n"
+                "      <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetExtents\">\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
+                "      <arg direction=\"out\" type=\"(iiii)\"/>\n"
+                "      <annotation value=\"QSpiRect\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetPosition\">\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"x\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"y\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetSize\">\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"width\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"height\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetLayer\">\n"
+                "      <arg direction=\"out\" type=\"u\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetMDIZOrder\">\n"
+                "      <arg direction=\"out\" type=\"n\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GrabFocus\">\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetAlpha\">\n"
+                "      <arg direction=\"out\" type=\"d\"/>\n"
+                "    </method>\n"
+                "    <method name=\"SetExtents\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"width\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"height\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"SetPosition\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"SetSize\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"width\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"height\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "  </interface>\n"
+                );
+
+    static const QLatin1String editableTextIntrospection(
+                "  <interface name=\"org.a11y.atspi.EditableText\">\n"
+                "    <method name=\"SetTextContents\">\n"
+                "      <arg direction=\"in\" type=\"s\" name=\"newContents\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"InsertText\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"position\"/>\n"
+                "      <arg direction=\"in\" type=\"s\" name=\"text\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"length\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"CopyText\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"startPos\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"endPos\"/>\n"
+                "    </method>\n"
+                "    <method name=\"CutText\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"startPos\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"endPos\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"DeleteText\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"startPos\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"endPos\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"PasteText\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"position\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "  </interface>\n"
+                );
+
+    static const QLatin1String tableIntrospection(
+                "  <interface name=\"org.a11y.atspi.Table\">\n"
+                "    <property access=\"read\" type=\"i\" name=\"NRows\"/>\n"
+                "    <property access=\"read\" type=\"i\" name=\"NColumns\"/>\n"
+                "    <property access=\"read\" type=\"(so)\" name=\"Caption\">\n"
+                "      <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName\"/>\n"
+                "    </property>\n"
+                "    <property access=\"read\" type=\"(so)\" name=\"Summary\">\n"
+                "      <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName\"/>\n"
+                "    </property>\n"
+                "    <property access=\"read\" type=\"i\" name=\"NSelectedRows\"/>\n"
+                "    <property access=\"read\" type=\"i\" name=\"NSelectedColumns\"/>\n"
+                "    <method name=\"GetAccessibleAt\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
+                "      <arg direction=\"out\" type=\"(so)\"/>\n"
+                "      <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetIndexAt\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
+                "      <arg direction=\"out\" type=\"i\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetRowAtIndex\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
+                "      <arg direction=\"out\" type=\"i\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetColumnAtIndex\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
+                "      <arg direction=\"out\" type=\"i\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetRowDescription\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetColumnDescription\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetRowExtentAt\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
+                "      <arg direction=\"out\" type=\"i\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetColumnExtentAt\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
+                "      <arg direction=\"out\" type=\"i\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetRowHeader\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
+                "      <arg direction=\"out\" type=\"(so)\"/>\n"
+                "      <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetColumnHeader\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
+                "      <arg direction=\"out\" type=\"(so)\"/>\n"
+                "      <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetSelectedRows\">\n"
+                "      <arg direction=\"out\" type=\"ai\"/>\n"
+                "      <annotation value=\"QSpiIntList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetSelectedColumns\">\n"
+                "      <arg direction=\"out\" type=\"ai\"/>\n"
+                "      <annotation value=\"QSpiIntList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"IsRowSelected\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"IsColumnSelected\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"IsSelected\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"AddRowSelection\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"AddColumnSelection\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"RemoveRowSelection\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"RemoveColumnSelection\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetRowColumnExtentsAtIndex\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"row\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"col\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"row_extents\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"col_extents\"/>\n"
+                "      <arg direction=\"out\" type=\"b\" name=\"is_selected\"/>\n"
+                "    </method>\n"
+                "  </interface>\n"
+                );
+
+    static const QLatin1String textIntrospection(
+                "  <interface name=\"org.a11y.atspi.Text\">\n"
+                "    <property access=\"read\" type=\"i\" name=\"CharacterCount\"/>\n"
+                "    <property access=\"read\" type=\"i\" name=\"CaretOffset\"/>\n"
+                "    <method name=\"GetText\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "    </method>\n"
+                "    <method name=\"SetCaretOffset\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetTextBeforeOffset\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"type\"/>\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetTextAtOffset\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"type\"/>\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetTextAfterOffset\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"type\"/>\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetCharacterAtOffset\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
+                "      <arg direction=\"out\" type=\"i\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetAttributeValue\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
+                "      <arg direction=\"in\" type=\"s\" name=\"attributeName\"/>\n"
+                "      <arg direction=\"out\" type=\"s\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
+                "      <arg direction=\"out\" type=\"b\" name=\"defined\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetAttributes\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
+                "      <arg direction=\"out\" type=\"a{ss}\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
+                "      <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetDefaultAttributes\">\n"
+                "      <arg direction=\"out\" type=\"a{ss}\"/>\n"
+                "      <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetCharacterExtents\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"x\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"y\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"width\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"height\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"coordType\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetOffsetAtPoint\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"coordType\"/>\n"
+                "      <arg direction=\"out\" type=\"i\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetNSelections\">\n"
+                "      <arg direction=\"out\" type=\"i\"/>\n"
+                "    <method name=\"GetSelection\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"selectionNum\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
+                "    </method>\n"
+                "    <method name=\"AddSelection\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"RemoveSelection\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"selectionNum\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"SetSelection\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"selectionNum\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
+                "      <arg direction=\"out\" type=\"b\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetRangeExtents\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"x\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"y\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"width\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"height\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"coordType\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetBoundedRanges\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"width\"/>\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"height\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"coordType\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"xClipType\"/>\n"
+                "      <arg direction=\"in\" type=\"u\" name=\"yClipType\"/>\n"
+                "      <arg direction=\"out\" type=\"a(iisv)\"/>\n"
+                "      <annotation value=\"QSpiRangeList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetAttributeRun\">\n"
+                "      <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
+                "      <arg direction=\"in\" type=\"b\" name=\"includeDefaults\"/>\n"
+                "      <arg direction=\"out\" type=\"a{ss}\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
+                "      <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
+                "      <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "    <method name=\"GetDefaultAttributeSet\">\n"
+                "      <arg direction=\"out\" type=\"a{ss}\"/>\n"
+                "      <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
+                "    </method>\n"
+                "  </interface>\n"
+                );
+
+    static const QLatin1String valueIntrospection(
+                "  <interface name=\"org.a11y.atspi.Value\">\n"
+                "    <property access=\"read\" type=\"d\" name=\"MinimumValue\"/>\n"
+                "    <property access=\"read\" type=\"d\" name=\"MaximumValue\"/>\n"
+                "    <property access=\"read\" type=\"d\" name=\"MinimumIncrement\"/>\n"
+                "    <property access=\"readwrite\" type=\"d\" name=\"CurrentValue\"/>\n"
+                "    <method name=\"SetCurrentValue\">\n"
+                "      <arg direction=\"in\" type=\"d\" name=\"value\"/>\n"
+                "    </method>\n"
+                "  </interface>\n"
+                );
+
+    QAIPointer interface = interfaceFromPath(path);
+    if (!interface) {
+        qWarning() << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << path;
+        return QString();
+    }
+
+    QStringList interfaces = accessibleInterfaces(interface);
+
+    QString xml;
+    xml.append(accessibleIntrospection);
+
+    if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT)))
+        xml.append(componentIntrospection);
+    if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_TEXT)))
+        xml.append(textIntrospection);
+    if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT)))
+        xml.append(editableTextIntrospection);
+    if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_ACTION)))
+        xml.append(actionIntrospection);
+    if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_TABLE)))
+        xml.append(tableIntrospection);
+    if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_VALUE)))
+        xml.append(valueIntrospection);
+    if (path == QLatin1String(QSPI_OBJECT_PATH_ROOT))
+        xml.append(applicationIntrospection);
+
+    return xml;
+}
+
+/*!
+  When initialized we will send updates, not before this.
+
+  This function also checks which event listeners are registered in the at-spi registry.
+  */
+void AtSpiAdaptor::setInitialized(bool init)
+{
+    initialized = init;
+
+    if (!initialized)
+        return;
+
+    updateEventListeners();
+    bool success = m_dbus->connection().connect(QLatin1String("org.a11y.atspi.Registry"), QLatin1String("/org/a11y/atspi/registry"),
+                                               QLatin1String("org.a11y.atspi.Registry"), QLatin1String("EventListenerRegistered"), this,
+                                               SLOT(eventListenerRegistered(QString,QString)));
+    success = success && m_dbus->connection().connect(QLatin1String("org.a11y.atspi.Registry"), QLatin1String("/org/a11y/atspi/registry"),
+                                               QLatin1String("org.a11y.atspi.Registry"), QLatin1String("EventListenerDeregistered"), this,
+                                               SLOT(eventListenerDeregistered(QString,QString)));
+#ifdef QT_ATSPI_DEBUG
+    qDebug() << "Registered event listener change listener: " << success;
+#endif
+}
+
+void AtSpiAdaptor::setBitFlag(const QString &flag)
+{
+    Q_ASSERT(flag.size());
+
+    // assume we don't get nonsense - look at first letter only
+    switch (flag.at(0).toLower().toLatin1()) {
+    case 'o': {
+        if (flag.size() <= 8) { // Object::
+            sendObject = 1;
+        } else { // Object:Foo:Bar
+            QString right = flag.mid(7);
+            if (false) {
+            } else if (right.startsWith(QLatin1String("ActiveDescendantChanged"))) {
+                sendObject_active_descendant_changed = 1;
+            } else if (right.startsWith(QLatin1String("AttributesChanged"))) {
+                sendObject_attributes_changed = 1;
+            } else if (right.startsWith(QLatin1String("BoundsChanged"))) {
+                sendObject_bounds_changed = 1;
+            } else if (right.startsWith(QLatin1String("ChildrenChanged"))) {
+                sendObject_children_changed = 1;
+            } else if (right.startsWith(QLatin1String("ColumnDeleted"))) {
+                sendObject_column_deleted = 1;
+            } else if (right.startsWith(QLatin1String("ColumnInserted"))) {
+                sendObject_column_inserted = 1;
+            } else if (right.startsWith(QLatin1String("ColumnReordered"))) {
+                sendObject_column_reordered = 1;
+            } else if (right.startsWith(QLatin1String("LinkSelected"))) {
+                sendObject_link_selected = 1;
+            } else if (right.startsWith(QLatin1String("ModelChanged"))) {
+                sendObject_model_changed = 1;
+            } else if (right.startsWith(QLatin1String("PropertyChange"))) {
+                if (right == QLatin1String("PropertyChange:AccessibleDescription")) {
+                    sendObject_property_change_accessible_description = 1;
+                } else if (right == QLatin1String("PropertyChange:AccessibleName")) {
+                    sendObject_property_change_accessible_name = 1;
+                } else if (right == QLatin1String("PropertyChange:AccessibleParent")) {
+                    sendObject_property_change_accessible_parent = 1;
+                } else if (right == QLatin1String("PropertyChange:AccessibleRole")) {
+                    sendObject_property_change_accessible_role = 1;
+                } else if (right == QLatin1String("PropertyChange:TableCaption")) {
+                    sendObject_property_change_accessible_table_caption = 1;
+                } else if (right == QLatin1String("PropertyChange:TableColumnDescription")) {
+                    sendObject_property_change_accessible_table_column_description = 1;
+                } else if (right == QLatin1String("PropertyChange:TableColumnHeader")) {
+                    sendObject_property_change_accessible_table_column_header = 1;
+                } else if (right == QLatin1String("PropertyChange:TableRowDescription")) {
+                    sendObject_property_change_accessible_table_row_description = 1;
+                } else if (right == QLatin1String("PropertyChange:TableRowHeader")) {
+                    sendObject_property_change_accessible_table_row_header = 1;
+                } else if (right == QLatin1String("PropertyChange:TableSummary")) {
+                    sendObject_property_change_accessible_table_summary = 1;
+                } else if (right == QLatin1String("PropertyChange:AccessibleValue")) {
+                    sendObject_property_change_accessible_value = 1;
+                } else {
+                    sendObject_property_change = 1;
+                }
+            } else if (right.startsWith(QLatin1String("RowDeleted"))) {
+                sendObject_row_deleted = 1;
+            } else if (right.startsWith(QLatin1String("RowInserted"))) {
+                sendObject_row_inserted = 1;
+            } else if (right.startsWith(QLatin1String("RowReordered"))) {
+                sendObject_row_reordered = 1;
+            } else if (right.startsWith(QLatin1String("SelectionChanged"))) {
+                sendObject_selection_changed = 1;
+            } else if (right.startsWith(QLatin1String("StateChanged"))) {
+                sendObject_state_changed = 1;
+            } else if (right.startsWith(QLatin1String("TextAttributesChanged"))) {
+                sendObject_text_attributes_changed = 1;
+            } else if (right.startsWith(QLatin1String("TextBoundsChanged"))) {
+                sendObject_text_bounds_changed = 1;
+            } else if (right.startsWith(QLatin1String("TextCaretMoved"))) {
+                sendObject_text_caret_moved = 1;
+            } else if (right.startsWith(QLatin1String("TextChanged"))) {
+                sendObject_text_changed = 1;
+            } else if (right.startsWith(QLatin1String("TextSelectionChanged"))) {
+                sendObject_text_selection_changed = 1;
+            } else if (right.startsWith(QLatin1String("ValueChanged"))) {
+                sendObject_value_changed = 1;
+            } else if (right.startsWith(QLatin1String("VisibleDataChanged"))) {
+                sendObject_visible_data_changed = 1;
+            } else {
+                qWarning() << "WARNING: subscription string not handled:" << flag;
+            }
+        }
+        break;
+    }
+    case 'w': { // window
+        if (flag.size() <= 8) {
+            sendWindow = 1;
+        } else { // object:Foo:Bar
+            QString right = flag.mid(7);
+            if (false) {
+            } else if (right.startsWith(QLatin1String("Activate"))) {
+                sendWindow_activate = 1;
+            } else if (right.startsWith(QLatin1String("Close"))) {
+                sendWindow_close= 1;
+            } else if (right.startsWith(QLatin1String("Create"))) {
+                sendWindow_create = 1;
+            } else if (right.startsWith(QLatin1String("Deactivate"))) {
+                sendWindow_deactivate = 1;
+            } else if (right.startsWith(QLatin1String("Lower"))) {
+                sendWindow_lower = 1;
+            } else if (right.startsWith(QLatin1String("Maximize"))) {
+                sendWindow_maximize = 1;
+            } else if (right.startsWith(QLatin1String("Minimize"))) {
+                sendWindow_minimize = 1;
+            } else if (right.startsWith(QLatin1String("Move"))) {
+                sendWindow_move = 1;
+            } else if (right.startsWith(QLatin1String("Raise"))) {
+                sendWindow_raise = 1;
+            } else if (right.startsWith(QLatin1String("Reparent"))) {
+                sendWindow_reparent = 1;
+            } else if (right.startsWith(QLatin1String("Resize"))) {
+                sendWindow_resize = 1;
+            } else if (right.startsWith(QLatin1String("Restore"))) {
+                sendWindow_restore = 1;
+            } else if (right.startsWith(QLatin1String("Restyle"))) {
+                sendWindow_restyle = 1;
+            } else if (right.startsWith(QLatin1String("Shade"))) {
+                sendWindow_shade = 1;
+            } else if (right.startsWith(QLatin1String("Unshade"))) {
+                sendWindow_unshade = 1;
+            } else if (right.startsWith(QLatin1String("DesktopCreate"))) {
+                // ignore this one
+            } else if (right.startsWith(QLatin1String("DesktopDestroy"))) {
+                // ignore this one
+            } else {
+                qWarning() << "WARNING: subscription string not handled:" << flag;
+            }
+        }
+        break;
+    }
+    case 'f': {
+        sendFocus = 1;
+        break;
+    }
+    case 'd': { // document is not implemented
+        break;
+    }
+    case 't': { // terminal is not implemented
+        break;
+    }
+    case 'm': { // mouse* is handled in a different way by the gnome atspi stack
+        break;
+    }
+    default:
+        qWarning() << "WARNING: subscription string not handled:" << flag;
+    }
+}
+
+/*!
+  Checks via dbus which events should be sent.
+  */
+void AtSpiAdaptor::updateEventListeners()
+{
+    QDBusMessage m = QDBusMessage::createMethodCall(QLatin1String("org.a11y.atspi.Registry"),
+                                                    QLatin1String("/org/a11y/atspi/registry"),
+                                                    QLatin1String("org.a11y.atspi.Registry"), QLatin1String("GetRegisteredEvents"));
+    QDBusReply<QSpiEventListenerArray> listenersReply = m_dbus->connection().call(m);
+    if (listenersReply.isValid()) {
+        const QSpiEventListenerArray evList = listenersReply.value();
+        Q_FOREACH (const QSpiEventListener &ev, evList) {
+            setBitFlag(ev.eventName);
+        }
+        m_applicationAdaptor->sendEvents(!evList.isEmpty());
+    } else {
+        qWarning() << "Could not query active accessibility event listeners.";
+    }
+}
+
+void AtSpiAdaptor::eventListenerDeregistered(const QString &/*bus*/, const QString &/*path*/)
+{
+//    qDebug() << "AtSpiAdaptor::eventListenerDeregistered: " << bus << path;
+    updateEventListeners();
+}
+
+void AtSpiAdaptor::eventListenerRegistered(const QString &/*bus*/, const QString &/*path*/)
+{
+//    qDebug() << "AtSpiAdaptor::eventListenerRegistered: " << bus << path;
+    updateEventListeners();
+}
+
+/*!
+  This slot needs to get called when a \a window has be activated or deactivated (become focused).
+  When \a active is true, the window just received focus, otherwise it lost the focus.
+  */
+void AtSpiAdaptor::windowActivated(QObject* window, bool active)
+{
+    if (!(sendWindow || sendWindow_activate))
+        return;
+
+    QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window);
+    Q_ASSERT(iface && iface->isValid());
+
+    QString windowTitle = iface->text(QAccessible::Name);
+    delete iface;
+
+    QDBusVariant data;
+    data.setVariant(windowTitle);
+
+    QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(data));
+
+    QString status = active ? QLatin1String("Activate") : QLatin1String("Deactivate");
+    QString path = pathForObject(window);
+    sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_WINDOW), status, args);
+
+    QVariantList stateArgs = packDBusSignalArguments(QLatin1String("active"), active ? 1 : 0, 0, variantForPath(path));
+    sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                   QLatin1String("StateChanged"), stateArgs);
+}
+
+QVariantList AtSpiAdaptor::packDBusSignalArguments(const QString &type, int data1, int data2, const QVariant &variantData) const
+{
+    QVariantList arguments;
+    arguments << type << data1 << data2 << variantData
+              << QVariant::fromValue(QSpiObjectReference(m_dbus->connection(), QDBusObjectPath(QSPI_OBJECT_PATH_ROOT)));
+    return arguments;
+}
+
+QVariant AtSpiAdaptor::variantForPath(const QString &path) const
+{
+    QDBusVariant data;
+    data.setVariant(QVariant::fromValue(QSpiObjectReference(m_dbus->connection(), QDBusObjectPath(path))));
+    return QVariant::fromValue(data);
+}
+
+bool AtSpiAdaptor::sendDBusSignal(const QString &path, const QString &interface, const QString &signalName, const QVariantList &arguments) const
+{
+    QDBusMessage message = QDBusMessage::createSignal(path, interface, signalName);
+    message.setArguments(arguments);
+    return m_dbus->connection().send(message);
+}
+
+QAIPointer AtSpiAdaptor::interfaceFromPath(const QString& dbusPath) const
+{
+    if (dbusPath == QLatin1String(QSPI_OBJECT_PATH_ROOT))
+        return QAIPointer(QAccessible::queryAccessibleInterface(qApp));
+
+    QStringList parts = dbusPath.split(QLatin1Char('/'));
+    if (parts.size() <= 5) {
+        qWarning() << "invalid path: " << dbusPath;
+        return QAIPointer();
+    }
+
+    QString objectString = parts.at(5);
+    quintptr uintptr = objectString.toULongLong();
+
+    if (uintptr && m_handledObjects.contains(uintptr)) {
+        // We found the pointer, check if it's still valid:
+        if (m_handledObjects[uintptr]) {
+            QObject* object = reinterpret_cast<QObject*>(uintptr);
+
+            QAIPointer interface = QAIPointer(QAccessible::queryAccessibleInterface(object));
+            if (!interface)
+                return QAIPointer();
+
+            for (int i = 6; i < parts.size(); ++i) {
+                int childIndex = parts.at(i).toInt();
+                if (childIndex < 0) {
+                    qWarning() << "Invalid child index";
+                    return QAIPointer();
+                }
+                QAIPointer childInterface(interface->child(childIndex));
+                if (childInterface)
+                    interface = childInterface;
+            }
+            return interface;
+        } else {
+            m_handledObjects.remove(uintptr);
+        }
+    }
+    return QAIPointer();
+}
+
+
+/*!
+    This function gets called when Qt notifies about accessibility updates.
+*/
+void AtSpiAdaptor::notify(QAccessibleEvent *event)
+{
+    if (!initialized)
+        return;
+
+    switch (event->type()) {
+    case QAccessible::ObjectCreated:
+        if (sendObject || sendObject_children_changed)
+            notifyAboutCreation(QAIPointer(event->accessibleInterface()));
+        break;
+    case QAccessible::ObjectShow: {
+        if (sendObject || sendObject_state_changed) {
+            QString path = pathForInterface(QAIPointer(event->accessibleInterface()));
+            QVariantList stateArgs = packDBusSignalArguments(QLatin1String("showing"), 1, 0, variantForPath(path));
+            sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                           QLatin1String("StateChanged"), stateArgs);
+        }
+        break;
+    }
+    case QAccessible::ObjectHide: {
+        if (sendObject || sendObject_state_changed) {
+            QString path = pathForInterface(QAIPointer(event->accessibleInterface()));
+            QVariantList stateArgs = packDBusSignalArguments(QLatin1String("showing"), 0, 0, variantForPath(path));
+            sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                           QLatin1String("StateChanged"), stateArgs);
+        }
+        break;
+    }
+    case QAccessible::ObjectDestroyed: {
+        if (sendObject || sendObject_state_changed)
+            notifyAboutDestruction(QAIPointer(event->accessibleInterface()));
+        break;
+    }
+    case QAccessible::NameChanged: {
+        if (sendObject || sendObject_property_change || sendObject_property_change_accessible_name) {
+            QString path = pathForInterface(QAIPointer(event->accessibleInterface()));
+            QVariantList args = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path));
+            sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                           QLatin1String("PropertyChange"), args);
+        }
+        break;
+    }
+    case QAccessible::DescriptionChanged: {
+        if (sendObject || sendObject_property_change || sendObject_property_change_accessible_description) {
+            QString path = pathForInterface(QAIPointer(event->accessibleInterface()));
+            QVariantList args = packDBusSignalArguments(QLatin1String("accessible-description"), 0, 0, variantForPath(path));
+            sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                           QLatin1String("PropertyChange"), args);
+        }
+        break;
+    }
+    case QAccessible::Focus: {
+        if (sendFocus || sendObject || sendObject_state_changed)
+            sendFocusChanged(QAIPointer(event->accessibleInterface()));
+        break;
+    }
+    case QAccessible::TextInserted:
+    case QAccessible::TextRemoved:
+    case QAccessible::TextUpdated: {
+        if (sendObject || sendObject_text_changed) {
+            QAIPointer iface = QAIPointer(event->accessibleInterface());
+            Q_ASSERT(iface->textInterface());
+            QString path = pathForInterface(iface);
+
+            int changePosition = 0;
+            int cursorPosition = 0;
+            QString textRemoved;
+            QString textInserted;
+
+            if (event->type() == QAccessible::TextInserted) {
+                QAccessibleTextInsertEvent *textEvent = static_cast<QAccessibleTextInsertEvent*>(event);
+                textInserted = textEvent->textInserted();
+                changePosition = textEvent->changePosition();
+                cursorPosition = textEvent->cursorPosition();
+            } else if (event->type() == QAccessible::TextRemoved) {
+                QAccessibleTextRemoveEvent *textEvent = static_cast<QAccessibleTextRemoveEvent*>(event);
+                textRemoved = textEvent->textRemoved();
+                changePosition = textEvent->changePosition();
+                cursorPosition = textEvent->cursorPosition();
+            } else if (event->type() == QAccessible::TextInserted) {
+                QAccessibleTextUpdateEvent *textEvent = static_cast<QAccessibleTextUpdateEvent*>(event);
+                textInserted = textEvent->textInserted();
+                textRemoved = textEvent->textRemoved();
+                changePosition = textEvent->changePosition();
+                cursorPosition = textEvent->cursorPosition();
+            }
+
+            QDBusVariant data;
+
+            if (!textRemoved.isEmpty()) {
+                data.setVariant(QVariant::fromValue(textRemoved));
+                QVariantList args = packDBusSignalArguments(QLatin1String("delete"), changePosition, textRemoved.length(), QVariant::fromValue(data));
+                sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                               QLatin1String("TextChanged"), args);
+            }
+
+            if (!textInserted.isEmpty()) {
+                data.setVariant(QVariant::fromValue(textInserted));
+                QVariantList args = packDBusSignalArguments(QLatin1String("insert"), changePosition, textInserted.length(), QVariant::fromValue(data));
+                sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                               QLatin1String("TextChanged"), args);
+            }
+
+            // send a cursor update
+            Q_UNUSED(cursorPosition)
+//            QDBusVariant cursorData;
+//            cursorData.setVariant(QVariant::fromValue(cursorPosition));
+//            QVariantList args = packDBusSignalArguments(QString(), cursorPosition, 0, QVariant::fromValue(cursorData));
+//            sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+//                           QLatin1String("TextCaretMoved"), args);
+        }
+        break;
+    }
+    case QAccessible::TextCaretMoved: {
+        if (sendObject || sendObject_text_caret_moved) {
+            QAIPointer iface = QAIPointer(event->accessibleInterface());
+            Q_ASSERT(iface->textInterface());
+            QString path = pathForInterface(iface);
+            QDBusVariant cursorData;
+            int pos = iface->textInterface()->cursorPosition();
+            cursorData.setVariant(QVariant::fromValue(pos));
+            QVariantList args = packDBusSignalArguments(QString(), pos, 0, QVariant::fromValue(cursorData));
+            sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                           QLatin1String("TextCaretMoved"), args);
+        }
+        break;
+    }
+    case QAccessible::TextSelectionChanged: {
+        if (sendObject || sendObject_text_selection_changed) {
+            QAIPointer iface = QAIPointer(event->accessibleInterface());
+            QString path = pathForInterface(iface);
+            QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
+            sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                           QLatin1String("TextSelectionChanged"), args);
+        }
+        break;
+    }
+    case QAccessible::ValueChanged: {
+        if (sendObject || sendObject_value_changed) {
+            QAIPointer iface = QAIPointer(event->accessibleInterface());
+            Q_ASSERT(iface->valueInterface());
+            QString path = pathForInterface(iface);
+            QVariantList args = packDBusSignalArguments(QLatin1String("accessible-value"), 0, 0, variantForPath(path));
+            sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                           QLatin1String("PropertyChange"), args);
+        }
+        break;
+    }
+    case QAccessible::Selection: {
+        QAIPointer iface = QAIPointer(event->accessibleInterface());
+        QString path = pathForInterface(iface);
+        int selected = iface->state().selected ? 1 : 0;
+        QVariantList stateArgs = packDBusSignalArguments(QLatin1String("selected"), selected, 0, variantForPath(path));
+        sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                       QLatin1String("StateChanged"), stateArgs);
+        break;
+    }
+
+    case QAccessible::StateChanged: {
+        if (sendObject || sendObject_state_changed || sendWindow || sendWindow_activate) {
+            QAccessible::State stateChange = static_cast<QAccessibleStateChangeEvent*>(event)->changedStates();
+            if (stateChange.checked) {
+                QAIPointer iface = QAIPointer(event->accessibleInterface());
+                int checked = iface->state().checked;
+                QString path = pathForInterface(iface);
+                QVariantList args = packDBusSignalArguments(QLatin1String("checked"), checked, 0, variantForPath(path));
+                sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                               QLatin1String("StateChanged"), args);
+            } else if (stateChange.active) {
+                QAIPointer iface = QAIPointer(event->accessibleInterface());
+                if (!(iface->role() == QAccessible::Window && (sendWindow || sendWindow_activate)))
+                    return;
+                QString windowTitle = iface->text(QAccessible::Name);
+                QDBusVariant data;
+                data.setVariant(windowTitle);
+                QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(data));
+
+                QString status = iface->state().active ? QLatin1String("Activate") : QLatin1String("Deactivate");
+                QString path = pathForInterface(iface);
+                sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_WINDOW), status, args);
+
+                QVariantList stateArgs = packDBusSignalArguments(QLatin1String("active"), iface->state().active ? 1 : 0, 0, variantForPath(path));
+                sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                               QLatin1String("StateChanged"), stateArgs);
+            }
+        }
+        break;
+    }
+//    case QAccessible::TableModelChanged: {
+//        // This is rather evil. We don't send data and hope that at-spi fetches the right child.
+//        // This hack fails when a row gets removed and a different one added in its place.
+//        QDBusVariant data;
+//        emit ChildrenChanged("add", 0, 0, data, spiBridge->getRootReference());
+//        break;
+//    }
+        //    case QAccessible::TableModelChanged:
+        //        QAccessible2::TableModelChange change = interface->tableInterface()->modelChange();
+        //        // assume we should reset if everything is 0
+        //        if (change.firstColumn == 0 && change.firstRow == 0 && change.lastColumn == 0 && change.lastRow == 0) {
+        //            notifyAboutDestruction(accessible);
+        //            notifyAboutCreation(accessible);
+        //        }
+        //        break;
+
+    case QAccessible::ParentChanged:
+        break;
+    case QAccessible::DialogStart:
+        break;
+    case QAccessible::DialogEnd:
+        break;
+    case QAccessible::SelectionRemove:
+        break;
+    default:
+        QAIPointer iface = QAIPointer(event->accessibleInterface());
+        qWarning() << "QSpiAccessible::accessibleEvent not handled: " << QString::number(event->type(), 16)
+                   << " obj: " << iface->object()
+                   << ((iface->isValid() && iface->object()) ? iface->object()->objectName() : QLatin1String(" invalid interface!"));
+        break;
+    }
+}
+
+void AtSpiAdaptor::sendFocusChanged(const QAIPointer &interface) const
+{
+    static QString lastFocusPath;
+    // "remove" old focus
+    if (!lastFocusPath.isEmpty()) {
+        QVariantList stateArgs = packDBusSignalArguments(QLatin1String("focused"), 0, 0, variantForPath(lastFocusPath));
+        sendDBusSignal(lastFocusPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                       QLatin1String("StateChanged"), stateArgs);
+    }
+    // send new focus
+    {
+        QString path = pathForInterface(interface);
+
+        QVariantList stateArgs = packDBusSignalArguments(QLatin1String("focused"), 1, 0, variantForPath(path));
+        sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
+                       QLatin1String("StateChanged"), stateArgs);
+
+        QVariantList focusArgs = packDBusSignalArguments(QString(), 0, 0, variantForPath(path));
+        sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_FOCUS),
+                       QLatin1String("Focus"), focusArgs);
+        lastFocusPath = path;
+    }
+}
+
+void AtSpiAdaptor::notifyAboutCreation(const QAIPointer &interface) const
+{
+//    // say hello to d-bus
+//    cache->emitAddAccessible(accessible->getCacheItem());
+
+    // notify about the new child of our parent
+    QAIPointer parent(interface->parent());
+    if (!parent) {
+        qWarning() << "AtSpiAdaptor::notifyAboutCreation: Could not find parent for " << interface->object();
+        return;
+    }
+    QString path = pathForInterface(interface);
+    int childCount = parent->childCount();
+    QString parentPath = pathForInterface(parent);
+    QVariantList args = packDBusSignalArguments(QLatin1String("add"), childCount, 0, variantForPath(path));
+    sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("ChildrenChanged"), args);
+}
+
+void AtSpiAdaptor::notifyAboutDestruction(const QAIPointer &interface) const
+{
+    if (!interface->isValid())
+        return;
+
+    QAIPointer parent(interface->parent());
+    if (!parent) {
+        qWarning() << "AtSpiAdaptor::notifyAboutDestruction: Could not find parent for " << interface->object();
+        return;
+    }
+    QString path = pathForInterface(interface);
+
+    // this is in the destructor. we have no clue which child we used to be.
+    // FIXME
+    int childIndex = -1;
+    //    if (child) {
+    //        childIndex = child;
+    //    } else {
+    //        childIndex = parent->indexOfChild(interface);
+    //    }
+
+    QString parentPath = pathForInterface(parent, true);
+    QVariantList args = packDBusSignalArguments(QLatin1String("remove"), childIndex, 0, variantForPath(path));
+    sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("ChildrenChanged"), args);
+}
+
+/*!
+  Handle incoming DBus message.
+  This function dispatches the dbus message to the right interface handler.
+  */
+bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnection &connection)
+{
+    // get accessible interface
+    QAIPointer accessible = interfaceFromPath(message.path());
+    if (!accessible) {
+        qWarning() << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << message.path();
+        return false;
+    }
+
+    QString interface = message.interface();
+    QString function = message.member();
+
+    // qDebug() << "AtSpiAdaptor::handleMessage: " << interface << function;
+
+    if (function == QLatin1String("Introspect")) {
+        //introspect(message.path());
+        return false;
+    }
+
+    // handle properties like regular functions
+    if (interface == QLatin1String("org.freedesktop.DBus.Properties")) {
+        interface = message.arguments().at(0).toString();
+        // Get/Set + Name
+        function = message.member() + message.arguments().at(1).toString();
+    }
+
+    // switch interface to call
+    if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_ACCESSIBLE))
+        return accessibleInterface(accessible, function, message, connection);
+    if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_APPLICATION))
+        return applicationInterface(accessible, function, message, connection);
+    if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT))
+        return componentInterface(accessible, function, message, connection);
+    if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_ACTION))
+        return actionInterface(accessible, function, message, connection);
+    if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_TEXT))
+        return textInterface(accessible, function, message, connection);
+    if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT))
+        return editableTextInterface(accessible, function, message, connection);
+    if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_VALUE))
+        return valueInterface(accessible, function, message, connection);
+    if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_TABLE))
+        return tableInterface(accessible, function, message, connection);
+
+    qWarning() << "AtSpiAdaptor::handleMessage with unknown interface: " << message.path() << interface << function;
+    return false;
+}
+
+// Application
+bool AtSpiAdaptor::applicationInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
+{
+    if (message.path() != QLatin1String(ATSPI_DBUS_PATH_ROOT)) {
+        qWarning() << "WARNING Qt AtSpiAdaptor: Could not find application interface for: " << message.path() << interface;
+        return false;
+    }
+
+    if (function == QLatin1String("SetId")) {
+        Q_ASSERT(message.signature() == QLatin1String("ssv"));
+        QVariant value = qvariant_cast<QDBusVariant>(message.arguments().at(2)).variant();
+
+        m_applicationId = value.toInt();
+        return true;
+    }
+    if (function == QLatin1String("GetId")) {
+        Q_ASSERT(message.signature() == QLatin1String("ss"));
+        QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(m_applicationId)));
+        return connection.send(reply);
+    }
+    if (function == QLatin1String("GetToolkitName")) {
+        Q_ASSERT(message.signature() == QLatin1String("ss"));
+        QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(QLatin1String("Qt"))));
+        return connection.send(reply);
+    }
+
+    qDebug() << "AtSpiAdaptor::applicationInterface " << message.path() << interface << function;
+    return false;
+}
+
+/*!
+  Register this application as accessible on the accessibility DBus.
+  */
+void AtSpiAdaptor::registerApplication()
+{
+    OrgA11yAtspiSocketInterface *registry;
+    registry = new OrgA11yAtspiSocketInterface(QLatin1String(QSPI_REGISTRY_NAME),
+                               QLatin1String(QSPI_OBJECT_PATH_ROOT), m_dbus->connection());
+
+    QDBusPendingReply<QSpiObjectReference> reply;
+    QSpiObjectReference ref = QSpiObjectReference(m_dbus->connection(), QDBusObjectPath(QSPI_OBJECT_PATH_ROOT));
+    reply = registry->Embed(ref);
+    reply.waitForFinished(); // TODO: make this async
+    if (reply.isValid ()) {
+        const QSpiObjectReference &socket = reply.value();
+        accessibilityRegistry = QSpiObjectReference(socket);
+    } else {
+        qWarning() << "Error in contacting registry";
+        qWarning() << reply.error().name();
+        qWarning() << reply.error().message();
+    }
+    delete registry;
+}
+
+// Accessible
+bool AtSpiAdaptor::accessibleInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
+{
+    if (function == QLatin1String("GetRole")) {
+        sendReply(connection, message, (uint) getRole(interface));
+    } else if (function == QLatin1String("GetName")) {
+        sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->text(QAccessible::Name))));
+    } else if (function == QLatin1String("GetRoleName")) {
+        sendReply(connection, message, qSpiRoleMapping[interface->role()].name());
+    } else if (function == QLatin1String("GetLocalizedRoleName")) {
+        sendReply(connection, message, QVariant::fromValue(qSpiRoleMapping[interface->role()].localizedName()));
+    } else if (function == QLatin1String("GetChildCount")) {
+        sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->childCount())));
+    } else if (function == QLatin1String("GetIndexInParent")) {
+        int childIndex = -1;
+        QAIPointer parent(interface->parent());
+        if (parent) {
+            childIndex = parent->indexOfChild(interface.data());
+            if (childIndex < 0)
+                qWarning() <<  "GetIndexInParent get invalid index: " << childIndex << interface;
+        }
+        sendReply(connection, message, childIndex);
+    } else if (function == QLatin1String("GetParent")) {
+        QString path;
+        QAIPointer parent(interface->parent());
+        if (!parent) {
+            path = QLatin1String(ATSPI_DBUS_PATH_NULL);
+        } else if (parent->role() == QAccessible::Application) {
+            path = QLatin1String(ATSPI_DBUS_PATH_ROOT);
+        } else {
+            path = pathForInterface(parent);
+        }
+        // Parent is a property, so it needs to be wrapped inside an extra variant.
+        sendReply(connection, message, QVariant::fromValue(
+                      QDBusVariant(QVariant::fromValue(QSpiObjectReference(connection, QDBusObjectPath(path))))));
+    } else if (function == QLatin1String("GetChildAtIndex")) {
+        int index = message.arguments().first().toInt();
+        if (index < 0) {
+            sendReply(connection, message, QVariant::fromValue(
+                          QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))));
+        } else {
+            QAIPointer childInterface = QAIPointer(interface->child(index));
+            sendReply(connection, message, QVariant::fromValue(
+                          QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(childInterface)))));
+        }
+    } else if (function == QLatin1String("GetInterfaces")) {
+        sendReply(connection, message, accessibleInterfaces(interface));
+    } else if (function == QLatin1String("GetDescription")) {
+        sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->text(QAccessible::Description))));
+    } else if (function == QLatin1String("GetState")) {
+        quint64 spiState = spiStatesFromQState(interface->state());
+        if (interface->tableInterface()) {
+            setSpiStateBit(&spiState, ATSPI_STATE_MANAGES_DESCENDANTS);
+        }
+// FIXME: figure out if this is a top level window and set its active state accordingly
+//        if (interface->object() && interface->object()->isWidgetType()) {
+//            QWidget *w = qobject_cast<QWidget*>(interface->object());
+//            if (w->topLevelWidget() && w->isActiveWindow()) {
+//                setSpiStateBit(&spiState, ATSPI_STATE_ACTIVE);
+//            }
+//        }
+        QAccessible::Role role = interface->role();
+        if (role == QAccessible::TreeItem ||
+            role == QAccessible::ListItem) {
+            /* Transient means libatspi2 will not cache items.
+               This is important because when adding/removing an item
+               the cache becomes outdated and we don't change the paths of
+               items in lists/trees/tables. */
+            setSpiStateBit(&spiState, ATSPI_STATE_TRANSIENT);
+        }
+        sendReply(connection, message,
+                  QVariant::fromValue(spiStateSetFromSpiStates(spiState)));
+    } else if (function == QLatin1String("GetAttributes")) {
+        sendReply(connection, message, QVariant::fromValue(QSpiAttributeSet()));
+    } else if (function == QLatin1String("GetRelationSet")) {
+        sendReply(connection, message, QVariant::fromValue(relationSet(interface, connection)));
+    } else if (function == QLatin1String("GetApplication")) {
+        sendReply(connection, message, QVariant::fromValue(
+                      QSpiObjectReference(connection, QDBusObjectPath(QSPI_OBJECT_PATH_ROOT))));
+    } else if (function == QLatin1String("GetChildren")) {
+        QSpiObjectReferenceArray children;
+        for (int i = 0; i < interface->childCount(); ++i) {
+            QString childPath = pathForInterface(QAIPointer(interface->child(i)));
+            QSpiObjectReference ref(connection, QDBusObjectPath(childPath));
+            children << ref;
+        }
+        connection.send(message.createReply(QVariant::fromValue(children)));
+    } else {
+        qWarning() << "WARNING: AtSpiAdaptor::handleMessage does not implement " << function << message.path();
+        return false;
+    }
+    return true;
+}
+
+AtspiRole AtSpiAdaptor::getRole(const QAIPointer &interface) const
+{
+    if ((interface->role() == QAccessible::EditableText) && interface->state().passwordEdit)
+        return ATSPI_ROLE_PASSWORD_TEXT;
+    return qSpiRoleMapping[interface->role()].spiRole();
+}
+
+//#define ACCESSIBLE_CREATION_DEBUG
+
+QStringList AtSpiAdaptor::accessibleInterfaces(const QAIPointer &interface) const
+{
+    QStringList ifaces;
+#ifdef ACCESSIBLE_CREATION_DEBUG
+    qDebug() << "AtSpiAdaptor::accessibleInterfaces create: " << interface->object();
+#endif
+    ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_ACCESSIBLE);
+
+    if (    (!interface->rect().isEmpty()) ||
+            (interface->object() && interface->object()->isWidgetType()) ||
+            (interface->role() == QAccessible::ListItem) ||
+            (interface->role() == QAccessible::Cell) ||
+            (interface->role() == QAccessible::TreeItem) ||
+            (interface->role() == QAccessible::Row) ||
+            (interface->object() && interface->object()->inherits("QSGItem"))
+            ) {
+        ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT);
+        }
+#ifdef ACCESSIBLE_CREATION_DEBUG
+    else {
+        qDebug() << " IS NOT a component";
+    }
+#endif
+
+    if (interface->actionInterface())
+        ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_ACTION);
+
+    if (interface->textInterface())
+        ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_TEXT);
+
+    if (interface->editableTextInterface())
+        ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT);
+
+    if (interface->valueInterface())
+        ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_VALUE);
+
+    if (interface->tableInterface())
+        ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_TABLE);
+
+    return ifaces;
+}
+
+QSpiRelationArray AtSpiAdaptor::relationSet(const QAIPointer &interface, const QDBusConnection &connection) const
+{
+    typedef QPair<QAccessibleInterface*, QAccessible::Relation> RelationPair;
+    QVector<RelationPair> relationInterfaces;
+    relationInterfaces = interface->relations();
+
+    QSpiRelationArray relations;
+    Q_FOREACH (const RelationPair &pair, relationInterfaces) {
+// FIXME: this loop seems a bit strange... "related" always have one item when we check.
+//And why is it a list, when it always have one item? And it seems to assume that the QAccessible::Relation enum maps directly to AtSpi
+        QList<QSpiObjectReference> related;
+
+        QDBusObjectPath path = QDBusObjectPath(pathForInterface(QAIPointer(pair.first)));
+        related.append(QSpiObjectReference(connection, path));
+
+        if (!related.isEmpty())
+            relations.append(QSpiRelationArrayEntry(qAccessibleRelationToAtSpiRelation(pair.second), related));
+    }
+    return relations;
+}
+
+void AtSpiAdaptor::sendReply(const QDBusConnection &connection, const QDBusMessage &message, const QVariant &argument) const
+{
+    QDBusMessage reply = message.createReply(argument);
+    connection.send(reply);
+}
+
+
+QString AtSpiAdaptor::pathForObject(QObject *object) const
+{
+    Q_ASSERT(object);
+
+    if (object == qApp) {
+        return QLatin1String(QSPI_OBJECT_PATH_ROOT);
+    }
+
+    if (qstrcmp(object->metaObject()->className(), "QAction") == 0) {
+        qDebug() << "AtSpiAdaptor::pathForObject: warning: creating path with QAction as object.";
+    }
+    quintptr uintptr = reinterpret_cast<quintptr>(object);
+    if (!m_handledObjects.contains(uintptr))
+        m_handledObjects[uintptr] = QPointer<QObject>(object);
+    return QLatin1String(QSPI_OBJECT_PATH_PREFIX) + QString::number(uintptr);
+}
+
+QString AtSpiAdaptor::pathForInterface(const QAIPointer &interface, bool inDestructor) const
+{
+    if (!interface || !interface->isValid())
+        return QLatin1String(ATSPI_DBUS_PATH_NULL);
+    if (interface->role() == QAccessible::Application)
+        return QLatin1String(QSPI_OBJECT_PATH_ROOT);
+
+    QAIPointer interfaceWithObject = interface;
+    QString path;
+
+    if (interface->role() == QAccessible::MenuItem && interface->object() &&
+            inheritsQAction(interface->object())) {
+        interfaceWithObject = QAIPointer(interface->parent());
+        int childIndex = interfaceWithObject->indexOfChild(interface.data());
+        path.append(QString::fromLatin1("/%1").arg(childIndex));
+    }
+
+    while (!interfaceWithObject->object()) {
+        QAIPointer parentInterface(interfaceWithObject->parent());
+
+        Q_ASSERT(parentInterface->isValid());
+        int index = parentInterface->indexOfChild(interfaceWithObject.data());
+        if (index < 0) {
+            qWarning() << "Object claims to have child that we cannot navigate to. FIX IT!" << parentInterface->object();
+            return QLatin1String(ATSPI_DBUS_PATH_NULL);
+        }
+        path.prepend(QLatin1Char('/') + QString::number(index));
+        interfaceWithObject = parentInterface;
+    }
+    quintptr uintptr = reinterpret_cast<quintptr>(interfaceWithObject->object());
+    path.prepend(QLatin1String(QSPI_OBJECT_PATH_PREFIX) + QString::number(uintptr));
+
+    if (!inDestructor && !m_handledObjects.contains(uintptr))
+        m_handledObjects[uintptr] = QPointer<QObject>(interfaceWithObject->object());
+
+    return path;
+}
+
+bool AtSpiAdaptor::inheritsQAction(QObject *object)
+{
+    const QMetaObject *mo = object->metaObject();
+    while (mo) {
+        const QLatin1String cn(mo->className());
+        if (cn == QLatin1String("QAction"))
+            return true;
+        mo = mo->superClass();
+    }
+    return false;
+}
+
+// Component
+static QAIPointer getWindow(QAIPointer interface)
+{
+    if (interface->role() == QAccessible::Window)
+        return interface;
+
+    QAIPointer parent(interface->parent());
+    while (parent && parent->role() != QAccessible::Window)
+        parent = QAIPointer(parent->parent());
+
+    return parent;
+}
+
+static QRect getRelativeRect(const QAIPointer &interface)
+{
+    QAIPointer window;
+    QRect wr, cr;
+
+    cr = interface->rect();
+
+    window = getWindow(interface);
+    if (window) {
+        wr = window->rect();
+
+        cr.setX(cr.x() - wr.x());
+        cr.setY(cr.x() - wr.y());
+    }
+    return cr;
+}
+
+bool AtSpiAdaptor::componentInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
+{
+    if (function == QLatin1String("Contains")) {
+        bool ret = false;
+        int x = message.arguments().at(0).toInt();
+        int y = message.arguments().at(1).toInt();
+        uint coordType = message.arguments().at(2).toUInt();
+        if (coordType == ATSPI_COORD_TYPE_SCREEN)
+            ret = interface->rect().contains(x, y);
+        else
+            ret = getRelativeRect(interface).contains(x, y);
+        sendReply(connection, message, ret);
+    } else if (function == QLatin1String("GetAccessibleAtPoint")) {
+        int x = message.arguments().at(0).toInt();
+        int y = message.arguments().at(1).toInt();
+        uint coordType = message.arguments().at(2).toUInt();
+        Q_UNUSED (coordType) // FIXME
+
+        QAIPointer childInterface(interface->childAt(x, y));
+        QAIPointer iface;
+        while (childInterface) {
+            iface = childInterface;
+            childInterface = QAIPointer(iface->childAt(x, y));
+        }
+        if (iface) {
+            QString path = pathForInterface(iface);
+            sendReply(connection, message, QVariant::fromValue(
+                          QSpiObjectReference(connection, QDBusObjectPath(path))));
+        } else {
+            sendReply(connection, message, QVariant::fromValue(
+                          QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))));
+        }
+    } else if (function == QLatin1String("GetAlpha")) {
+        sendReply(connection, message, (double) 1.0);
+    } else if (function == QLatin1String("GetExtents")) {
+        uint coordType = message.arguments().at(0).toUInt();
+        sendReply(connection, message, QVariant::fromValue(getExtents(interface, coordType)));
+    } else if (function == QLatin1String("GetLayer")) {
+        sendReply(connection, message, QVariant::fromValue((uint)1));
+    } else if (function == QLatin1String("GetMDIZOrder")) {
+        sendReply(connection, message, QVariant::fromValue((short)0));
+    } else if (function == QLatin1String("GetPosition")) {
+        uint coordType = message.arguments().at(0).toUInt();
+        QRect rect;
+        if (coordType == ATSPI_COORD_TYPE_SCREEN)
+            rect = interface->rect();
+        else
+            rect = getRelativeRect(interface);
+        QVariantList pos;
+        pos << rect.x() << rect.y();
+        connection.send(message.createReply(pos));
+    } else if (function == QLatin1String("GetSize")) {
+        QRect rect = interface->rect();
+        QVariantList size;
+        size << rect.width() << rect.height();
+        connection.send(message.createReply(size));
+    } else if (function == QLatin1String("GrabFocus")) {
+// FIXME: implement focus grabbing
+//        if (interface->object() && interface->object()->isWidgetType()) {
+//            QWidget* w = static_cast<QWidget*>(interface->object());
+//            w->setFocus(Qt::OtherFocusReason);
+//            sendReply(connection, message, true);
+//        }
+        sendReply(connection, message, false);
+    } else if (function == QLatin1String("SetExtents")) {
+//        int x = message.arguments().at(0).toInt();
+//        int y = message.arguments().at(1).toInt();
+//        int width = message.arguments().at(2).toInt();
+//        int height = message.arguments().at(3).toInt();
+//        uint coordinateType = message.arguments().at(4).toUInt();
+        qWarning() << "SetExtents is not implemented.";
+        sendReply(connection, message, false);
+    } else if (function == QLatin1String("SetPosition")) {
+//        int x = message.arguments().at(0).toInt();
+//        int y = message.arguments().at(1).toInt();
+//        uint coordinateType = message.arguments().at(2).toUInt();
+        qWarning() << "SetPosition is not implemented.";
+        sendReply(connection, message, false);
+    } else if (function == QLatin1String("SetSize")) {
+//        int width = message.arguments().at(0).toInt();
+//        int height = message.arguments().at(1).toInt();
+        qWarning() << "SetSize is not implemented.";
+        sendReply(connection, message, false);
+    } else {
+        qWarning() << "WARNING: AtSpiAdaptor::handleMessage does not implement " << function << message.path();
+        return false;
+    }
+    return true;
+}
+
+QRect AtSpiAdaptor::getExtents(const QAIPointer &interface, uint coordType)
+{
+    return (coordType == ATSPI_COORD_TYPE_SCREEN) ? interface->rect() : getRelativeRect(interface);
+}
+
+// Action interface
+bool AtSpiAdaptor::actionInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
+{
+    QAccessibleActionInterface *actionIface = interface->actionInterface();
+    if (!actionIface)
+        return false;
+
+    if (function == QLatin1String("GetNActions")) {
+        sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(actionIface->actionNames().count()))));
+    } else if (function == QLatin1String("DoAction")) {
+        int index = message.arguments().at(0).toInt();
+        if (index < 0 || index >= actionIface->actionNames().count())
+            return false;
+        interface->actionInterface()->doAction(actionIface->actionNames().at(index));
+        sendReply(connection, message, true);
+    } else if (function == QLatin1String("GetActions")) {
+        sendReply(connection, message, QVariant::fromValue(getActions(actionIface)));
+    } else if (function == QLatin1String("GetName")) {
+        int index = message.arguments().at(0).toInt();
+        if (index < 0 || index >= actionIface->actionNames().count())
+            return false;
+        sendReply(connection, message, actionIface->actionNames().at(index));
+    } else if (function == QLatin1String("GetDescription")) {
+        int index = message.arguments().at(0).toInt();
+        if (index < 0 || index >= actionIface->actionNames().count())
+            return false;
+        sendReply(connection, message, actionIface->localizedActionDescription(actionIface->actionNames().at(index)));
+    } else if (function == QLatin1String("GetKeyBinding")) {
+        int index = message.arguments().at(0).toInt();
+        if (index < 0 || index >= actionIface->actionNames().count())
+            return false;
+        QStringList keyBindings;
+        keyBindings = actionIface->keyBindingsForAction(actionIface->actionNames().value(index));
+        if (keyBindings.isEmpty()) {
+            QString acc = interface->text(QAccessible::Accelerator);
+            if (!acc.isEmpty())
+                keyBindings.append(acc);
+        }
+        if (keyBindings.length() > 0)
+            sendReply(connection, message, keyBindings.join(QLatin1String(";")));
+        else
+            sendReply(connection, message, QString());
+    } else {
+        qWarning() << "WARNING: AtSpiAdaptor::handleMessage does not implement " << function << message.path();
+        return false;
+    }
+    return true;
+}
+
+QSpiActionArray AtSpiAdaptor::getActions(QAccessibleActionInterface *actionInterface) const
+{
+    QSpiActionArray actions;
+    Q_FOREACH (const QString &actionName, actionInterface->actionNames()) {
+        QSpiAction action;
+        QStringList keyBindings;
+
+        action.description = actionInterface->localizedActionDescription(actionName);
+
+        keyBindings = actionInterface->keyBindingsForAction(actionName);
+
+        if (keyBindings.length() > 0)
+                action.keyBinding = keyBindings[0];
+        else
+            action.keyBinding = QString();
+
+        actions << action;
+    }
+    return actions;
+}
+
+// Text interface
+bool AtSpiAdaptor::textInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
+{
+    if (!interface->textInterface())
+        return false;
+
+    // properties
+    if (function == QLatin1String("GetCaretOffset")) {
+        sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(interface->textInterface()->cursorPosition()))));
+    } else if (function == QLatin1String("GetCharacterCount")) {
+        sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(interface->textInterface()->characterCount()))));
+
+    // functions
+    } else if (function == QLatin1String("AddSelection")) {
+        int startOffset = message.arguments().at(0).toInt();
+        int endOffset = message.arguments().at(1).toInt();
+        int lastSelection = interface->textInterface()->selectionCount();
+        interface->textInterface()->setSelection(lastSelection, startOffset, endOffset);
+        sendReply(connection, message, (interface->textInterface()->selectionCount() > lastSelection));
+    } else if (function == QLatin1String("GetAttributeRun")) {
+        int offset = message.arguments().at(0).toInt();
+        bool includeDefaults = message.arguments().at(1).toBool();
+        Q_UNUSED(includeDefaults)
+        connection.send(message.createReply(getAttributes(interface, offset, includeDefaults)));
+    } else if (function == QLatin1String("GetAttributeValue")) {
+        int offset = message.arguments().at(0).toInt();
+        QString attributeName = message.arguments().at(1).toString();
+        connection.send(message.createReply(getAttributeValue(interface, offset, attributeName)));
+    } else if (function == QLatin1String("GetAttributes")) {
+        int offset = message.arguments().at(0).toInt();
+        connection.send(message.createReply(getAttributes(interface, offset, true)));
+    } else if (function == QLatin1String("GetBoundedRanges")) {
+        int x = message.arguments().at(0).toInt();
+        int y = message.arguments().at(1).toInt();
+        int width = message.arguments().at(2).toInt();
+        int height = message.arguments().at(3).toInt();
+        uint coordType = message.arguments().at(4).toUInt();
+        uint xClipType = message.arguments().at(5).toUInt();
+        uint yClipType = message.arguments().at(6).toUInt();
+        Q_UNUSED(x) Q_UNUSED (y) Q_UNUSED(width)
+        Q_UNUSED(height) Q_UNUSED(coordType)
+        Q_UNUSED(xClipType) Q_UNUSED(yClipType)
+        qWarning("Not implemented: QSpiAdaptor::GetBoundedRanges");
+        sendReply(connection, message, QVariant::fromValue(QSpiTextRangeList()));
+    } else if (function == QLatin1String("GetCharacterAtOffset")) {
+        int offset = message.arguments().at(0).toInt();
+        int start;
+        int end;
+        QString result = interface->textInterface()->textAtOffset(offset, QAccessible2::CharBoundary, &start, &end);
+        sendReply(connection, message, (int) *(qPrintable (result)));
+    } else if (function == QLatin1String("GetCharacterExtents")) {
+        int offset = message.arguments().at(0).toInt();
+        int coordType = message.arguments().at(1).toUInt();
+        connection.send(message.createReply(getCharacterExtents(interface, offset, coordType)));
+    } else if (function == QLatin1String("GetDefaultAttributeSet") || function == QLatin1String("GetDefaultAttributes")) {
+        // GetDefaultAttributes is deprecated in favour of GetDefaultAttributeSet.
+        // Empty set seems reasonable. There is no default attribute set.
+        sendReply(connection, message, QVariant::fromValue(QSpiAttributeSet()));
+    } else if (function == QLatin1String("GetNSelections")) {
+        sendReply(connection, message, interface->textInterface()->selectionCount());
+    } else if (function == QLatin1String("GetOffsetAtPoint")) {
+        qDebug() << message.signature();
+        Q_ASSERT(!message.signature().isEmpty());
+        QPoint point(message.arguments().at(0).toInt(), message.arguments().at(1).toInt());
+        uint coordType = message.arguments().at(2).toUInt();
+        if (coordType == ATSPI_COORD_TYPE_WINDOW) {
+            QWindow *win = interface->window();
+            point -= QPoint(win->x(), win->y());
+        }
+        int offset = interface->textInterface()->offsetAtPoint(point);
+        sendReply(connection, message, offset);
+    } else if (function == QLatin1String("GetRangeExtents")) {
+        int startOffset = message.arguments().at(0).toInt();
+        int endOffset = message.arguments().at(1).toInt();
+        uint coordType = message.arguments().at(2).toUInt();
+        connection.send(message.createReply(getRangeExtents(interface, startOffset, endOffset, coordType)));
+    } else if (function == QLatin1String("GetSelection")) {
+        int selectionNum = message.arguments().at(0).toInt();
+        int start, end;
+        interface->textInterface()->selection(selectionNum, &start, &end);
+        if (start < 0)
+            start = end = interface->textInterface()->cursorPosition();
+        QVariantList sel;
+        sel << start << end;
+        connection.send(message.createReply(sel));
+    } else if (function == QLatin1String("GetText")) {
+        int startOffset = message.arguments().at(0).toInt();
+        int endOffset = message.arguments().at(1).toInt();
+        if (endOffset == -1) // AT-SPI uses -1 to signal all characters
+            endOffset = interface->textInterface()->characterCount();
+        sendReply(connection, message, interface->textInterface()->text(startOffset, endOffset));
+    } else if (function == QLatin1String("GetTextAfterOffset")) {
+        int offset = message.arguments().at(0).toInt();
+        int type = message.arguments().at(1).toUInt();
+        int startOffset, endOffset;
+        QString text = interface->textInterface()->textAfterOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset);
+        QVariantList ret;
+        ret << text << startOffset << endOffset;
+        connection.send(message.createReply(ret));
+    } else if (function == QLatin1String("GetTextAtOffset")) {
+        int offset = message.arguments().at(0).toInt();
+        int type = message.arguments().at(1).toUInt();
+        int startOffset, endOffset;
+        QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset);
+        QVariantList ret;
+        ret << text << startOffset << endOffset;
+        connection.send(message.createReply(ret));
+    } else if (function == QLatin1String("GetTextBeforeOffset")) {
+        int offset = message.arguments().at(0).toInt();
+        int type = message.arguments().at(1).toUInt();
+        int startOffset, endOffset;
+        QString text = interface->textInterface()->textBeforeOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset);
+        QVariantList ret;
+        ret << text << startOffset << endOffset;
+        connection.send(message.createReply(ret));
+    } else if (function == QLatin1String("RemoveSelection")) {
+        int selectionNum = message.arguments().at(0).toInt();
+        interface->textInterface()->removeSelection(selectionNum);
+        sendReply(connection, message, true);
+    } else if (function == QLatin1String("SetCaretOffset")) {
+        int offset = message.arguments().at(0).toInt();
+        interface->textInterface()->setCursorPosition(offset);
+        sendReply(connection, message, true);
+    } else if (function == QLatin1String("SetSelection")) {
+        int selectionNum = message.arguments().at(0).toInt();
+        int startOffset = message.arguments().at(1).toInt();
+        int endOffset = message.arguments().at(2).toInt();
+        interface->textInterface()->setSelection(selectionNum, startOffset, endOffset);
+        sendReply(connection, message, true);
+    } else {
+        qWarning() << "WARNING: AtSpiAdaptor::handleMessage does not implement " << function << message.path();
+        return false;
+    }
+    return true;
+}
+
+QAccessible2::BoundaryType AtSpiAdaptor::qAccessibleBoundaryType(int atspiTextBoundaryType) const
+{
+    switch (atspiTextBoundaryType) {
+    case ATSPI_TEXT_BOUNDARY_CHAR:
+        return QAccessible2::CharBoundary;
+    case ATSPI_TEXT_BOUNDARY_WORD_START:
+    case ATSPI_TEXT_BOUNDARY_WORD_END:
+        return QAccessible2::WordBoundary;
+    case ATSPI_TEXT_BOUNDARY_SENTENCE_START:
+    case ATSPI_TEXT_BOUNDARY_SENTENCE_END:
+        return QAccessible2::SentenceBoundary;
+    case ATSPI_TEXT_BOUNDARY_LINE_START:
+    case ATSPI_TEXT_BOUNDARY_LINE_END:
+        return QAccessible2::LineBoundary;
+    }
+    Q_ASSERT_X(0, "", "Requested invalid boundary type.");
+    return QAccessible2::CharBoundary;
+}
+
+// FIXME all attribute methods below should share code
+QVariantList AtSpiAdaptor::getAttributes(const QAIPointer &interface, int offset, bool includeDefaults) const
+{
+    Q_UNUSED(includeDefaults);
+
+    QSpiAttributeSet set;
+    int startOffset;
+    int endOffset;
+
+    QString joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset);
+    QStringList attributes = joined.split (QLatin1Char(';'), QString::SkipEmptyParts, Qt::CaseSensitive);
+    foreach (const QString &attr, attributes) {
+        QStringList items;
+        items = attr.split(QLatin1Char(':'), QString::SkipEmptyParts, Qt::CaseSensitive);
+        set[items[0]] = items[1];
+    }
+
+    QVariantList list;
+    list << QVariant::fromValue(set) << startOffset << endOffset;
+
+    return list;
+}
+
+QVariantList AtSpiAdaptor::getAttributeValue(const QAIPointer &interface, int offset, const QString &attributeName) const
+{
+    QString mapped;
+    QString joined;
+    QStringList attributes;
+    QSpiAttributeSet map;
+    int startOffset;
+    int endOffset;
+    bool defined;
+
+    joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset);
+    attributes = joined.split (QLatin1Char(';'), QString::SkipEmptyParts, Qt::CaseSensitive);
+    foreach (const QString& attr, attributes) {
+        QStringList items;
+        items = attr.split(QLatin1Char(':'), QString::SkipEmptyParts, Qt::CaseSensitive);
+        map[items[0]] = items[1];
+    }
+    mapped = map[attributeName];
+    defined = mapped.isEmpty();
+    QVariantList list;
+    list << mapped << startOffset << endOffset << defined;
+    return list;
+}
+
+QRect AtSpiAdaptor::getCharacterExtents(const QAIPointer &interface, int offset, uint coordType) const
+{
+    QRect rect = interface->textInterface()->characterRect(offset);
+
+    if (coordType == ATSPI_COORD_TYPE_WINDOW)
+        rect = translateRectToWindowCoordinates(interface, rect);
+
+    return rect;
+}
+
+QRect AtSpiAdaptor::getRangeExtents(const QAIPointer &interface,
+                                            int startOffset, int endOffset, uint coordType) const
+{
+    if (endOffset == -1)
+        endOffset = interface->textInterface()->characterCount();
+
+    QAccessibleTextInterface *textInterface = interface->textInterface();
+    if (endOffset <= startOffset || !textInterface)
+        return QRect();
+
+    QRect rect = textInterface->characterRect(startOffset);
+    for (int i=startOffset + 1; i <= endOffset; i++)
+        rect = rect | textInterface->characterRect(i);
+
+    // relative to window
+    if (coordType == ATSPI_COORD_TYPE_WINDOW)
+        rect = translateRectToWindowCoordinates(interface, rect);
+
+    return rect;
+}
+
+QRect AtSpiAdaptor::translateRectToWindowCoordinates(const QAIPointer &interface, const QRect &rect)
+{
+    QAIPointer window = getWindow(interface);
+    if (window)
+        return rect.translated(-window->rect().x(), -window->rect().y());
+
+    return rect;
+}
+
+
+// Editable Text interface
+static QString textForRange(QAccessibleInterface *accessible, int startOffset, int endOffset)
+{
+    if (QAccessibleTextInterface *textIface = accessible->textInterface()) {
+        if (endOffset == -1)
+            endOffset = textIface->characterCount();
+        return textIface->text(startOffset, endOffset);
+    }
+    QString txt = accessible->text(QAccessible::Value);
+    if (endOffset == -1)
+        endOffset = txt.length();
+    return txt.mid(startOffset, endOffset - startOffset);
+}
+
+static void replaceTextFallback(QAccessibleInterface *accessible, long startOffset, long endOffset, const QString &txt)
+{
+    QString t = textForRange(accessible, 0, -1);
+    if (endOffset == -1)
+        endOffset = t.length();
+    if (endOffset - startOffset == 0)
+        t.insert(startOffset, txt);
+    else
+        t.replace(startOffset, endOffset - startOffset, txt);
+    accessible->setText(QAccessible::Value, t);
+}
+
+bool AtSpiAdaptor::editableTextInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
+{
+    if (function == QLatin1String("CopyText")) {
+#ifndef QT_NO_CLIPBOARD
+        int startOffset = message.arguments().at(0).toInt();
+        int endOffset = message.arguments().at(1).toInt();
+        const QString t = textForRange(interface.data(), startOffset, endOffset);
+        QGuiApplication::clipboard()->setText(t);
+#endif
+        connection.send(message.createReply(true));
+    } else if (function == QLatin1String("CutText")) {
+#ifndef QT_NO_CLIPBOARD
+        int startOffset = message.arguments().at(0).toInt();
+        int endOffset = message.arguments().at(1).toInt();
+        const QString t = textForRange(interface.data(), startOffset, endOffset);
+        if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
+            editableTextIface->deleteText(startOffset, endOffset);
+        else
+            replaceTextFallback(interface.data(), startOffset, endOffset, QString());
+        QGuiApplication::clipboard()->setText(t);
+#endif
+        connection.send(message.createReply(true));
+    } else if (function == QLatin1String("DeleteText")) {
+        int startOffset = message.arguments().at(0).toInt();
+        int endOffset = message.arguments().at(1).toInt();
+        if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
+            editableTextIface->deleteText(startOffset, endOffset);
+        else
+            replaceTextFallback(interface.data(), startOffset, endOffset, QString());
+        connection.send(message.createReply(true));
+    } else if (function == QLatin1String("InsertText")) {
+        int position = message.arguments().at(0).toInt();
+        QString text = message.arguments().at(1).toString();
+        int length = message.arguments().at(2).toInt();
+        text.resize(length);
+        if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
+            editableTextIface->insertText(position, text);
+        else
+            replaceTextFallback(interface.data(), position, position, text);
+        connection.send(message.createReply(true));
+    } else if (function == QLatin1String("PasteText")) {
+#ifndef QT_NO_CLIPBOARD
+        int position = message.arguments().at(0).toInt();
+        const QString txt = QGuiApplication::clipboard()->text();
+        if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
+            editableTextIface->insertText(position, txt);
+        else
+            replaceTextFallback(interface.data(), position, position, txt);
+#endif
+        connection.send(message.createReply(true));
+    } else if (function == QLatin1String("SetTextContents")) {
+        QString newContents = message.arguments().at(0).toString();
+        interface->editableTextInterface()->replaceText(0, interface->textInterface()->characterCount(), newContents);
+        connection.send(message.createReply(true));
+    } else if (function == QLatin1String("")) {
+        connection.send(message.createReply());
+    } else {
+        qWarning() << "WARNING: AtSpiAdaptor::handleMessage does not implement " << function << message.path();
+        return false;
+    }
+    return true;
+}
+
+// Value interface
+bool AtSpiAdaptor::valueInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
+{
+    if (0) {
+    } else if (function == QLatin1String("SetCurrentValue")) {
+        QDBusVariant v = message.arguments().at(2).value<QDBusVariant>();
+        double value = v.variant().toDouble();
+        //Temporary fix
+        //See https://bugzilla.gnome.org/show_bug.cgi?id=652596
+        interface->valueInterface()->setCurrentValue(value);
+        connection.send(message.createReply()); // FIXME is the reply needed?
+    } else if (function == QLatin1String("GetCurrentValue")) {
+        bool success;
+        double val = interface->valueInterface()->currentValue().toDouble(&success);
+        if (!success)
+            qWarning ("AtSpiAdaptor::valueInterface: Could not convert current value to double.");
+        connection.send(message.createReply(
+                            QVariant::fromValue(QDBusVariant(QVariant::fromValue(val)))));
+    } else if (function == QLatin1String("GetMaximumValue")) {
+        bool success;
+        double val = interface->valueInterface()->maximumValue().toDouble(&success);
+        if (!success)
+            qWarning ("AtSpiAdaptor::valueInterface: Could not convert current value to double.");
+        connection.send(message.createReply(
+                            QVariant::fromValue(QDBusVariant(QVariant::fromValue(val)))));
+    } else if (function == QLatin1String("GetMinimumIncrement")) {
+        connection.send(message.createReply(
+                            QVariant::fromValue(QDBusVariant(QVariant::fromValue(0.0)))));
+    } else if (function == QLatin1String("GetMinimumValue")) {
+        bool success;
+        double val = interface->valueInterface()->minimumValue().toDouble(&success);
+        if (!success)
+            qWarning ("AtSpiAdaptor::valueInterface: Could not convert current value to double.");
+        connection.send(message.createReply(
+                            QVariant::fromValue(QDBusVariant(QVariant::fromValue(val)))));
+    } else {
+        qWarning() << "WARNING: AtSpiAdaptor::handleMessage does not implement " << function << message.path();
+        return false;
+    }
+    return true;
+}
+
+// Table interface
+bool AtSpiAdaptor::tableInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
+{
+    if (!(interface->tableInterface() || interface->tableCellInterface())) {
+        qWarning() << "WARNING Qt AtSpiAdaptor: Could not find table interface for: " << message.path() << interface;
+        return false;
+    }
+
+    if (0) {
+    // properties
+    } else if (function == QLatin1String("GetCaption")) {
+        QAIPointer captionInterface= QAIPointer(interface->tableInterface()->caption());
+        if (captionInterface) {
+            QSpiObjectReference ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(captionInterface)));
+            sendReply(connection, message, QVariant::fromValue(ref));
+        } else {
+            sendReply(connection, message, QVariant::fromValue(
+                          QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))));
+        }
+    } else if (function == QLatin1String("GetNColumns")) {
+        connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
+            QVariant::fromValue(interface->tableInterface()->columnCount())))));
+    } else if (function == QLatin1String("GetNRows")) {
+        connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
+            QVariant::fromValue(interface->tableInterface()->rowCount())))));
+    } else if (function == QLatin1String("GetNSelectedColumns")) {
+        connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
+            QVariant::fromValue(interface->tableInterface()->selectedColumnCount())))));
+    } else if (function == QLatin1String("GetNSelectedRows")) {
+        connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
+            QVariant::fromValue(interface->tableInterface()->selectedRowCount())))));
+    } else if (function == QLatin1String("GetSummary")) {
+        QAIPointer summary = interface->tableInterface() ? QAIPointer(interface->tableInterface()->summary()) : QAIPointer(0);
+        QSpiObjectReference ref(connection, QDBusObjectPath(pathForInterface(summary)));
+        connection.send(message.createReply(QVariant::fromValue(QDBusVariant(QVariant::fromValue(ref)))));
+    } else if (function == QLatin1String("GetAccessibleAt")) {
+        int row = message.arguments().at(0).toInt();
+        int column = message.arguments().at(1).toInt();
+        Q_ASSERT(interface->tableInterface());
+        Q_ASSERT(row >= 0);
+        Q_ASSERT(column >= 0);
+        Q_ASSERT(row < interface->tableInterface()->rowCount());
+        Q_ASSERT(column < interface->tableInterface()->columnCount());
+
+        QSpiObjectReference ref;
+        QAIPointer cell(interface->tableInterface()->cellAt(row, column));
+        if (cell) {
+            ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(cell)));
+        } else {
+            qWarning() << "WARNING: no cell interface returned for " << interface->object() << row << column;
+            ref = QSpiObjectReference();
+        }
+        connection.send(message.createReply(QVariant::fromValue(ref)));
+
+    } else if (function == QLatin1String("GetIndexAt")) {
+        int row = message.arguments().at(0).toInt();
+        int column = message.arguments().at(1).toInt();
+        QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column);
+        if (!cell) {
+            qWarning() << "WARNING: AtSpiAdaptor::GetIndexAt(" << row << "," << column << ") did not find a cell. " << interface;
+            return false;
+        }
+        int index = interface->indexOfChild(cell);
+        qDebug() << "QSpiAdaptor::GetIndexAt row:" << row << " col:" << column << " logical index:" << index;
+        Q_ASSERT(index > 0);
+        delete cell;
+        connection.send(message.createReply(index));
+    } else if ((function == QLatin1String("GetColumnAtIndex")) || (function == QLatin1String("GetRowAtIndex"))) {
+        int index = message.arguments().at(0).toInt();
+        int ret = -1;
+        if (index >= 0) {
+            QAIPointer cell = QAIPointer(interface->child(index));
+            if (cell) {
+                if (function == QLatin1String("GetColumnAtIndex")) {
+                    if (cell->role() == QAccessible::ColumnHeader) {
+                        ret = index;
+                    } else if (cell->role() == QAccessible::RowHeader) {
+                        ret = -1;
+                    } else {
+                        if (!cell->tableCellInterface()) {
+                            qWarning() << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell;
+                            return false;
+                        }
+                        ret = cell->tableCellInterface()->columnIndex();
+                    }
+                } else {
+                    if (cell->role() == QAccessible::ColumnHeader) {
+                        ret = -1;
+                    } else if (cell->role() == QAccessible::RowHeader) {
+                        ret = index % interface->tableInterface()->columnCount();
+                    } else {
+                        if (!cell->tableCellInterface()) {
+                            qWarning() << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell;
+                            return false;
+                        }
+                        ret = cell->tableCellInterface()->rowIndex();
+                    }
+                }
+            } else {
+                qWarning() << "WARNING: AtSpiAdaptor::" << function << " No cell at index: " << index << interface;
+                return false;
+            }
+        }
+        connection.send(message.createReply(ret));
+
+    } else if (function == QLatin1String("GetColumnDescription")) {
+        int column = message.arguments().at(0).toInt();
+        connection.send(message.createReply(interface->tableInterface()->columnDescription(column)));
+    } else if (function == QLatin1String("GetRowDescription")) {
+        int row = message.arguments().at(0).toInt();
+        connection.send(message.createReply(interface->tableInterface()->rowDescription(row)));
+
+
+
+    } else if (function == QLatin1String("GetRowColumnExtentsAtIndex")) {
+        int index = message.arguments().at(0).toInt();
+        bool success = false;
+
+        int row, col, rowExtents, colExtents;
+        bool isSelected;
+
+        int cols = interface->tableInterface()->columnCount();
+        row = index/cols;
+        col = index%cols;
+        QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, col)->tableCellInterface();
+        if (cell) {
+            cell->rowColumnExtents(&row, &col, &rowExtents, &colExtents, &isSelected);
+            success = true;
+            delete cell;
+        }
+
+        QVariantList list;
+        list << success << row << col << rowExtents << colExtents << isSelected;
+        connection.send(message.createReply(list));
+
+    } else if (function == QLatin1String("GetColumnExtentAt")) {
+        int row = message.arguments().at(0).toInt();
+        int column = message.arguments().at(1).toInt();
+        connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->columnExtent()));
+
+    } else if (function == QLatin1String("GetRowExtentAt")) {
+        int row = message.arguments().at(0).toInt();
+        int column = message.arguments().at(1).toInt();
+        connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->rowExtent()));
+
+    } else if (function == QLatin1String("GetColumnHeader")) {
+        int column = message.arguments().at(0).toInt();
+        QSpiObjectReference ref;
+
+        QAIPointer cell(interface->tableInterface()->cellAt(0, column));
+        if (cell && cell->tableCellInterface()) {
+            QList<QAccessibleInterface*> header = cell->tableCellInterface()->columnHeaderCells();
+            if (header.size() > 0) {
+                ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(QAIPointer(header.takeAt(0)))));
+                qDeleteAll(header);
+            }
+        }
+        connection.send(message.createReply(QVariant::fromValue(ref)));
+
+    } else if (function == QLatin1String("GetRowHeader")) {
+        int row = message.arguments().at(0).toInt();
+        QSpiObjectReference ref;
+        QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, 0)->tableCellInterface();
+        if (cell) {
+            QList<QAccessibleInterface*> header = cell->rowHeaderCells();
+            delete cell;
+            if (header.size() > 0) {
+                ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(QAIPointer(header.takeAt(0)))));
+                qDeleteAll(header);
+            }
+        }
+        connection.send(message.createReply(QVariant::fromValue(ref)));
+
+    } else if (function == QLatin1String("GetSelectedColumns")) {
+        connection.send(message.createReply(QVariant::fromValue(interface->tableInterface()->selectedColumns())));
+    } else if (function == QLatin1String("GetSelectedRows")) {
+        connection.send(message.createReply(QVariant::fromValue(interface->tableInterface()->selectedRows())));
+    } else if (function == QLatin1String("IsColumnSelected")) {
+        int column = message.arguments().at(0).toInt();
+        connection.send(message.createReply(interface->tableInterface()->isColumnSelected(column)));
+    } else if (function == QLatin1String("IsRowSelected")) {
+        int row = message.arguments().at(0).toInt();
+        connection.send(message.createReply(interface->tableInterface()->isRowSelected(row)));
+    } else if (function == QLatin1String("IsSelected")) {
+        int row = message.arguments().at(0).toInt();
+        int column = message.arguments().at(1).toInt();
+        QAccessibleTableCellInterface* cell = interface->tableInterface()->cellAt(row, column)->tableCellInterface();
+        connection.send(message.createReply(cell->isSelected()));
+        delete cell;
+    } else if (function == QLatin1String("AddColumnSelection")) {
+        int column = message.arguments().at(0).toInt();
+        connection.send(message.createReply(interface->tableInterface()->selectColumn(column)));
+    } else if (function == QLatin1String("AddRowSelection")) {
+        int row = message.arguments().at(0).toInt();
+        connection.send(message.createReply(interface->tableInterface()->selectRow(row)));
+    } else if (function == QLatin1String("RemoveColumnSelection")) {
+        int column = message.arguments().at(0).toInt();
+        connection.send(message.createReply(interface->tableInterface()->unselectColumn(column)));
+    } else if (function ==  QLatin1String("RemoveRowSelection")) {
+        int row = message.arguments().at(0).toInt();
+        connection.send(message.createReply(interface->tableInterface()->unselectRow(row)));
+    } else {
+        qWarning() << "WARNING: AtSpiAdaptor::handleMessage does not implement " << function << message.path();
+        return false;
+    }
+    return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor_p.h b/src/platformsupport/linuxaccessibility/atspiadaptor_p.h
new file mode 100644 (file)
index 0000000..a649784
--- /dev/null
@@ -0,0 +1,220 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 ATSPIADAPTOR_H
+#define ATSPIADAPTOR_H
+
+#include <atspi/atspi-constants.h>
+
+#include <QtCore/qsharedpointer.h>
+#include <QtDBus/qdbusvirtualobject.h>
+#include <QtGui/qaccessible.h>
+#include <QtGui/qaccessible2.h>
+
+#include "dbusconnection_p.h"
+#include "struct_marshallers_p.h"
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+class QAccessibleInterface;
+class QSpiAccessibleInterface;
+class QSpiApplicationAdaptor;
+
+typedef QSharedPointer<QAccessibleInterface> QAIPointer;
+
+class AtSpiAdaptor :public QDBusVirtualObject
+{
+    Q_OBJECT
+
+public:
+    explicit AtSpiAdaptor(DBusConnection *connection, QObject *parent = 0);
+    ~AtSpiAdaptor();
+
+    void registerApplication();
+    QString introspect(const QString &path) const;
+    bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection);
+    void notify(QAccessibleEvent *event);
+
+    void setInitialized(bool init);
+
+public Q_SLOTS:
+    void eventListenerRegistered(const QString &bus, const QString &path);
+    void eventListenerDeregistered(const QString &bus, const QString &path);
+    void windowActivated(QObject* window, bool active);
+
+private:
+    void updateEventListeners();
+    void setBitFlag(const QString &flag);
+
+    // sending messages
+    QVariantList packDBusSignalArguments(const QString &type, int data1, int data2, const QVariant &variantData) const;
+    bool sendDBusSignal(const QString &path, const QString &interface, const QString &name, const QVariantList &arguments) const;
+    QVariant variantForPath(const QString &path) const;
+
+    void sendFocusChanged(const QAIPointer &interface) const;
+    void notifyAboutCreation(const QAIPointer &interface) const;
+    void notifyAboutDestruction(const QAIPointer &interface) const;
+
+    // handlers for the different accessible interfaces
+    bool applicationInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
+    bool accessibleInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
+    bool componentInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
+    bool actionInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
+    bool textInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
+    bool editableTextInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
+    bool valueInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
+    bool tableInterface(const QAIPointer &interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
+
+    void sendReply(const QDBusConnection &connection, const QDBusMessage &message, const QVariant &argument) const;
+
+    QAIPointer interfaceFromPath(const QString& dbusPath) const;
+    QString pathForInterface(const QAIPointer &interface, bool inDestructor = false) const;
+    QString pathForObject(QObject *object) const;
+
+    // accessible helper functions
+    AtspiRole getRole(const QAIPointer &interface) const;
+    QSpiRelationArray relationSet(const QAIPointer &interface, const QDBusConnection &connection) const;
+    QStringList accessibleInterfaces(const QAIPointer &interface) const;
+
+    // component helper functions
+    static QRect getExtents(const QAIPointer &interface, uint coordType);
+    static QRect translateRectToWindowCoordinates(const QAIPointer &interface, const QRect &rect);
+
+    // action helper functions
+    QSpiActionArray getActions(QAccessibleActionInterface* interface) const;
+
+    // text helper functions
+    QVariantList getAttributes(const QAIPointer &, int offset, bool includeDefaults) const;
+    QVariantList getAttributeValue(const QAIPointer &, int offset, const QString &attributeName) const;
+    QRect getCharacterExtents(const QAIPointer &, int offset, uint coordType) const;
+    QRect getRangeExtents(const QAIPointer &, int startOffset, int endOffset, uint coordType) const;
+    QAccessible2::BoundaryType qAccessibleBoundaryType(int atspiTextBoundaryType) const;
+    static bool inheritsQAction(QObject *object);
+
+    // private vars
+    QSpiObjectReference accessibilityRegistry;
+    DBusConnection *m_dbus;
+    QSpiApplicationAdaptor *m_applicationAdaptor;
+
+    /// Assigned from the accessibility registry.
+    int m_applicationId;
+    bool initialized;
+
+    mutable QHash<quintptr, QPointer<QObject> > m_handledObjects;
+
+    // Bit fields - which updates to send
+
+    // AT-SPI has some events that we do not care about:
+    // document
+    // document-load-complete
+    // document-load-stopped
+    // document-reload
+    uint sendFocus : 1;
+    // mouse abs/rel/button
+
+    // all of object
+    uint sendObject : 1;
+    uint sendObject_active_descendant_changed : 1;
+    uint sendObject_attributes_changed : 1;
+    uint sendObject_bounds_changed : 1;
+    uint sendObject_children_changed : 1;
+//    uint sendObject_children_changed_add : 1;
+//    uint sendObject_children_changed_remove : 1;
+    uint sendObject_column_deleted : 1;
+    uint sendObject_column_inserted : 1;
+    uint sendObject_column_reordered : 1;
+    uint sendObject_link_selected : 1;
+    uint sendObject_model_changed : 1;
+    uint sendObject_property_change : 1;
+    uint sendObject_property_change_accessible_description : 1;
+    uint sendObject_property_change_accessible_name : 1;
+    uint sendObject_property_change_accessible_parent : 1;
+    uint sendObject_property_change_accessible_role : 1;
+    uint sendObject_property_change_accessible_table_caption : 1;
+    uint sendObject_property_change_accessible_table_column_description : 1;
+    uint sendObject_property_change_accessible_table_column_header : 1;
+    uint sendObject_property_change_accessible_table_row_description : 1;
+    uint sendObject_property_change_accessible_table_row_header : 1;
+    uint sendObject_property_change_accessible_table_summary : 1;
+    uint sendObject_property_change_accessible_value : 1;
+    uint sendObject_row_deleted : 1;
+    uint sendObject_row_inserted : 1;
+    uint sendObject_row_reordered : 1;
+    uint sendObject_selection_changed : 1;
+    uint sendObject_state_changed : 1;
+    uint sendObject_text_attributes_changed : 1;
+    uint sendObject_text_bounds_changed : 1;
+    uint sendObject_text_caret_moved : 1;
+    uint sendObject_text_changed : 1;
+//    uint sendObject_text_changed_delete : 1;
+//    uint sendObject_text_changed_insert : 1;
+    uint sendObject_text_selection_changed : 1;
+    uint sendObject_value_changed : 1;
+    uint sendObject_visible_data_changed : 1;
+
+    // we don't implement terminal
+    // terminal-application_changed/charwidth_changed/columncount_changed/line_changed/linecount_changed
+    uint sendWindow : 1;
+    uint sendWindow_activate : 1;
+    uint sendWindow_close: 1;
+    uint sendWindow_create : 1;
+    uint sendWindow_deactivate : 1;
+//    uint sendWindow_desktop_create : 1;
+//    uint sendWindow_desktop_destroy : 1;
+    uint sendWindow_lower : 1;
+    uint sendWindow_maximize : 1;
+    uint sendWindow_minimize : 1;
+    uint sendWindow_move : 1;
+    uint sendWindow_raise : 1;
+    uint sendWindow_reparent : 1;
+    uint sendWindow_resize : 1;
+    uint sendWindow_restore : 1;
+    uint sendWindow_restyle : 1;
+    uint sendWindow_shade : 1;
+    uint sendWindow_unshade : 1;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#endif
diff --git a/src/platformsupport/linuxaccessibility/bridge.cpp b/src/platformsupport/linuxaccessibility/bridge.cpp
new file mode 100644 (file)
index 0000000..c36f5a5
--- /dev/null
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 "bridge_p.h"
+
+#include <atspi/atspi-constants.h>
+#include <qstring.h>
+
+#include "atspiadaptor_p.h"
+
+#include "cache_p.h"
+#include "constant_mappings_p.h"
+#include "dbusconnection_p.h"
+#include "struct_marshallers_p.h"
+
+#include "deviceeventcontroller_adaptor.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+    \class QSpiAccessibleBridge
+
+    \brief QSpiAccessibleBridge
+
+    QSpiAccessibleBridge
+*/
+
+QSpiAccessibleBridge::QSpiAccessibleBridge()
+    : cache(0)
+{
+    dbusConnection = new DBusConnection();
+    if (!dBusConnection().isConnected())
+        qWarning() << "Could not connect to dbus.";
+
+    qSpiInitializeStructTypes();
+    initializeConstantMappings();
+
+    /* Create the cache of accessible objects */
+    cache = new QSpiDBusCache(dBusConnection(), this);
+    dec = new DeviceEventControllerAdaptor(this);
+
+    bool reg = dBusConnection().registerObject(QLatin1String(ATSPI_DBUS_PATH_DEC), this, QDBusConnection::ExportAdaptors);
+    qDebug() << "Registered DEC: " << reg;
+
+    dbusAdaptor = new AtSpiAdaptor(dbusConnection, this);
+    dBusConnection().registerVirtualObject(QLatin1String(QSPI_OBJECT_PATH_ACCESSIBLE), dbusAdaptor, QDBusConnection::SubPath);
+    dbusAdaptor->registerApplication();
+}
+
+QSpiAccessibleBridge::~QSpiAccessibleBridge()
+{
+    delete dbusConnection;
+} // Qt currently doesn't delete plugins.
+
+QDBusConnection QSpiAccessibleBridge::dBusConnection() const
+{
+    return dbusConnection->connection();
+}
+
+void QSpiAccessibleBridge::setRootObject(QAccessibleInterface *interface)
+{
+    // the interface we get will be for the QApplication object.
+    // we already cache it in the constructor.
+    Q_ASSERT(interface->object() == qApp);
+    dbusAdaptor->setInitialized(true);
+}
+
+void QSpiAccessibleBridge::notifyAccessibilityUpdate(QAccessibleEvent *event)
+{
+    dbusAdaptor->notify(event);
+}
+
+struct RoleMapping {
+    QAccessible::Role role;
+    AtspiRole spiRole;
+    const char *name;
+    const char *localizedName;
+};
+
+static RoleMapping map[] = {
+    { QAccessible::NoRole, ATSPI_ROLE_INVALID, "invalid", QT_TR_NOOP("invalid role") },
+    { QAccessible::TitleBar, ATSPI_ROLE_TEXT, "text", QT_TR_NOOP("title bar") },
+    { QAccessible::MenuBar, ATSPI_ROLE_MENU_BAR, "menu bar", QT_TR_NOOP("menu bar") },
+    { QAccessible::ScrollBar, ATSPI_ROLE_SCROLL_BAR, "scroll bar", QT_TR_NOOP("scroll bar") },
+    { QAccessible::Grip, ATSPI_ROLE_UNKNOWN, "unknown", QT_TR_NOOP("grip") },
+    { QAccessible::Sound, ATSPI_ROLE_UNKNOWN, "unknown", QT_TR_NOOP("sound") },
+    { QAccessible::Cursor, ATSPI_ROLE_ARROW, "arrow", QT_TR_NOOP("cursor") },
+    { QAccessible::Caret, ATSPI_ROLE_UNKNOWN, "unknown", QT_TR_NOOP("caret") },
+    { QAccessible::AlertMessage, ATSPI_ROLE_ALERT, "alert", QT_TR_NOOP("alert message") },
+    { QAccessible::Window, ATSPI_ROLE_WINDOW, "window", QT_TR_NOOP("window") },
+    { QAccessible::Client, ATSPI_ROLE_FILLER, "filler", QT_TR_NOOP("filler") },
+    { QAccessible::PopupMenu, ATSPI_ROLE_POPUP_MENU, "popup menu", QT_TR_NOOP("popup menu") },
+    { QAccessible::MenuItem, ATSPI_ROLE_MENU_ITEM, "menu item", QT_TR_NOOP("menu item") },
+    { QAccessible::ToolTip, ATSPI_ROLE_TOOL_TIP, "tool tip", QT_TR_NOOP("tool tip") },
+    { QAccessible::Application, ATSPI_ROLE_APPLICATION, "application", QT_TR_NOOP("application") },
+    { QAccessible::Document, ATSPI_ROLE_DOCUMENT_FRAME, "document frame", QT_TR_NOOP("document") },
+    { QAccessible::Pane, ATSPI_ROLE_PANEL, "panel", QT_TR_NOOP("pane") },
+    { QAccessible::Chart, ATSPI_ROLE_CHART, "chart", QT_TR_NOOP("chart") },
+    { QAccessible::Dialog, ATSPI_ROLE_DIALOG, "dialog", QT_TR_NOOP("dialog") },
+    { QAccessible::Border, ATSPI_ROLE_FRAME, "frame", QT_TR_NOOP("border") },
+    { QAccessible::Grouping, ATSPI_ROLE_PANEL, "panel", QT_TR_NOOP("grouping") },
+    { QAccessible::Separator, ATSPI_ROLE_SEPARATOR, "separator", QT_TR_NOOP("separator") },
+    { QAccessible::ToolBar, ATSPI_ROLE_TOOL_BAR, "tool bar", QT_TR_NOOP("tool bar") },
+    { QAccessible::StatusBar, ATSPI_ROLE_STATUS_BAR, "statusbar", QT_TR_NOOP("status bar") },
+    { QAccessible::Table, ATSPI_ROLE_TABLE, "table", QT_TR_NOOP("table") },
+    { QAccessible::ColumnHeader, ATSPI_ROLE_TABLE_COLUMN_HEADER, "column header", QT_TR_NOOP("column header") },
+    { QAccessible::RowHeader, ATSPI_ROLE_TABLE_ROW_HEADER, "row header", QT_TR_NOOP("row header") },
+    { QAccessible::Column, ATSPI_ROLE_TABLE_CELL, "table cell", QT_TR_NOOP("column") },
+    { QAccessible::Row, ATSPI_ROLE_TABLE_CELL, "table cell", QT_TR_NOOP("row") },
+    { QAccessible::Cell, ATSPI_ROLE_TABLE_CELL, "table cell", QT_TR_NOOP("cell") },
+    { QAccessible::Link, ATSPI_ROLE_LINK, "link", QT_TR_NOOP("link") },
+    { QAccessible::HelpBalloon, ATSPI_ROLE_DIALOG, "dialog", QT_TR_NOOP("help balloon") },
+    { QAccessible::Assistant, ATSPI_ROLE_DIALOG, "dialog", QT_TR_NOOP("assistant") },
+    { QAccessible::List, ATSPI_ROLE_LIST, "list", QT_TR_NOOP("list") },
+    { QAccessible::ListItem, ATSPI_ROLE_LIST_ITEM, "list item", QT_TR_NOOP("list item") },
+    { QAccessible::Tree, ATSPI_ROLE_TREE, "tree", QT_TR_NOOP("tree") },
+    { QAccessible::TreeItem, ATSPI_ROLE_TABLE_CELL, "tree item", QT_TR_NOOP("tree item") },
+    { QAccessible::PageTab, ATSPI_ROLE_PAGE_TAB, "page tab", QT_TR_NOOP("page tab") },
+    { QAccessible::PropertyPage, ATSPI_ROLE_PAGE_TAB, "page tab", QT_TR_NOOP("property page") },
+    { QAccessible::Indicator, ATSPI_ROLE_UNKNOWN, "unknown", QT_TR_NOOP("indicator") },
+    { QAccessible::Graphic, ATSPI_ROLE_IMAGE, "image", QT_TR_NOOP("graphic") },
+    { QAccessible::StaticText, ATSPI_ROLE_LABEL, "label", QT_TR_NOOP("label") },
+    { QAccessible::EditableText, ATSPI_ROLE_TEXT, "text", QT_TR_NOOP("text") },
+    { QAccessible::PushButton, ATSPI_ROLE_PUSH_BUTTON, "push button", QT_TR_NOOP("push button") },
+    { QAccessible::CheckBox, ATSPI_ROLE_CHECK_BOX, "check box", QT_TR_NOOP("check box") },
+    { QAccessible::RadioButton, ATSPI_ROLE_RADIO_BUTTON, "radio button", QT_TR_NOOP("radio box") },
+    { QAccessible::ComboBox, ATSPI_ROLE_COMBO_BOX, "combo box", QT_TR_NOOP("combo box") },
+    { QAccessible::ProgressBar, ATSPI_ROLE_PROGRESS_BAR, "progress bar", QT_TR_NOOP("progress bar") },
+    { QAccessible::Dial, ATSPI_ROLE_DIAL, "accelerator label", QT_TR_NOOP("dial") },
+    { QAccessible::HotkeyField, ATSPI_ROLE_TEXT, "text", QT_TR_NOOP("hotkey field") }, //FIXME text?
+    { QAccessible::Slider, ATSPI_ROLE_SLIDER, "slider", QT_TR_NOOP("slider") },
+    { QAccessible::SpinBox, ATSPI_ROLE_SPIN_BUTTON, "spin button", QT_TR_NOOP("spin box") },
+    { QAccessible::Canvas, ATSPI_ROLE_CANVAS, "canvas", QT_TR_NOOP("canvas") },
+    { QAccessible::Animation, ATSPI_ROLE_ANIMATION, "animation", QT_TR_NOOP("animation") },
+    { QAccessible::Equation, ATSPI_ROLE_TEXT, "text", QT_TR_NOOP("equation") },
+    { QAccessible::ButtonDropDown, ATSPI_ROLE_PUSH_BUTTON, "push button", QT_TR_NOOP("button drop down") },
+    { QAccessible::ButtonMenu, ATSPI_ROLE_PUSH_BUTTON, "push button", QT_TR_NOOP("button menu") },
+    { QAccessible::ButtonDropGrid, ATSPI_ROLE_PUSH_BUTTON, "push button", QT_TR_NOOP("button drop grid") },
+    { QAccessible::Whitespace, ATSPI_ROLE_FILLER, "filler", QT_TR_NOOP("whitespace") },
+    { QAccessible::PageTabList, ATSPI_ROLE_PAGE_TAB_LIST, "page tab list", QT_TR_NOOP("page tab list") },
+    { QAccessible::Clock, ATSPI_ROLE_UNKNOWN, "unknown", QT_TR_NOOP("clock") },
+    { QAccessible::Splitter, ATSPI_ROLE_SPLIT_PANE, "split pane", QT_TR_NOOP("splitter") },
+    { QAccessible::LayeredPane, ATSPI_ROLE_LAYERED_PANE, "layered pane", QT_TR_NOOP("layered pane") },
+    { QAccessible::UserRole, ATSPI_ROLE_UNKNOWN, "unknown", QT_TR_NOOP("user role") }
+};
+
+void QSpiAccessibleBridge::initializeConstantMappings()
+{
+    for (uint i = 0; i < sizeof(map) / sizeof(RoleMapping); ++i)
+        qSpiRoleMapping.insert(map[i].role, RoleNames(map[i].spiRole, QLatin1String(map[i].name), tr(map[i].localizedName)));
+}
+
+QT_END_NAMESPACE
diff --git a/src/platformsupport/linuxaccessibility/bridge_p.h b/src/platformsupport/linuxaccessibility/bridge_p.h
new file mode 100644 (file)
index 0000000..4a77fa7
--- /dev/null
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 QSPIACCESSIBLEBRIDGE_H
+#define QSPIACCESSIBLEBRIDGE_H
+
+#include <QtDBus/qdbusconnection.h>
+#include <qpa/qplatformaccessibility.h>
+
+class DeviceEventControllerAdaptor;
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+class DBusConnection;
+class QSpiDBusCache;
+class AtSpiAdaptor;
+
+class QSpiAccessibleBridge: public QObject, public QPlatformAccessibility
+{
+    Q_OBJECT
+public:
+    QSpiAccessibleBridge();
+
+    virtual ~QSpiAccessibleBridge();
+    virtual void setRootObject(QAccessibleInterface *obj);
+
+    virtual void notifyAccessibilityUpdate(QAccessibleEvent *event);
+    QDBusConnection dBusConnection() const;
+
+private:
+    void initializeConstantMappings();
+
+    QSpiDBusCache *cache;
+    DeviceEventControllerAdaptor *dec;
+
+    AtSpiAdaptor *dbusAdaptor;
+
+    DBusConnection* dbusConnection;
+    bool initialized;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#endif
diff --git a/src/platformsupport/linuxaccessibility/cache.cpp b/src/platformsupport/linuxaccessibility/cache.cpp
new file mode 100644 (file)
index 0000000..1fa3257
--- /dev/null
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 "cache_p.h"
+#include "cache_adaptor.h"
+
+#include "bridge_p.h"
+
+#define QSPI_OBJECT_PATH_CACHE "/org/a11y/atspi/cache"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+    \class QSpiDBusCache
+
+    \brief This class is responsible for the AT-SPI cache interface.
+
+    The idea behind the cache is that starting an application would
+    result in many dbus calls. The way GTK/Gail/ATK work is that
+    they create accessibles for all objects on startup.
+    In order to avoid querying all the objects individually via DBus
+    they get sent by using the GetItems call of the cache.
+
+    Additionally the AddAccessible and RemoveAccessible signals
+    are responsible for adding/removing objects from the cache.
+
+    Currently the Qt bridge chooses to ignore these.
+*/
+
+QSpiDBusCache::QSpiDBusCache(QDBusConnection c, QObject* parent)
+    : QObject(parent)
+{
+    new CacheAdaptor(this);
+    c.registerObject(QLatin1String(QSPI_OBJECT_PATH_CACHE), this, QDBusConnection::ExportAdaptors);
+}
+
+void QSpiDBusCache::emitAddAccessible(const QSpiAccessibleCacheItem& item)
+{
+    emit AddAccessible(item);
+}
+
+void QSpiDBusCache::emitRemoveAccessible(const QSpiObjectReference& item)
+{
+    emit RemoveAccessible(item);
+}
+
+QSpiAccessibleCacheArray QSpiDBusCache::GetItems()
+{
+    QList <QSpiAccessibleCacheItem> cacheArray;
+    return cacheArray;
+}
+
+QT_END_NAMESPACE
diff --git a/src/platformsupport/linuxaccessibility/cache_p.h b/src/platformsupport/linuxaccessibility/cache_p.h
new file mode 100644 (file)
index 0000000..0e3d891
--- /dev/null
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 Q_SPI_CACHE_H
+#define Q_SPI_CACHE_H
+
+#include <QtCore/QObject>
+#include "struct_marshallers_p.h"
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+class QSpiDBusCache : public QObject
+{
+    Q_OBJECT
+
+public:
+    QSpiDBusCache(QDBusConnection c, QObject* parent = 0);
+    void emitAddAccessible(const QSpiAccessibleCacheItem& item);
+    void emitRemoveAccessible(const QSpiObjectReference& item);
+
+Q_SIGNALS:
+    void AddAccessible(const QSpiAccessibleCacheItem &nodeAdded);
+    void RemoveAccessible(const QSpiObjectReference &nodeRemoved);
+
+public Q_SLOTS:
+    QSpiAccessibleCacheArray GetItems();
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#endif /* Q_SPI_CACHE_H */
diff --git a/src/platformsupport/linuxaccessibility/constant_mappings.cpp b/src/platformsupport/linuxaccessibility/constant_mappings.cpp
new file mode 100644 (file)
index 0000000..d758723
--- /dev/null
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 "constant_mappings_p.h"
+
+#include <qobject.h>
+#include <qdebug.h>
+
+// FIXME the assignment of roles is quite arbitrary, at some point go through this list and sort and check that it makes sense
+//  "calendar" "check menu item"  "color chooser" "column header"    "dateeditor"  "desktop icon"  "desktop frame"
+//  "directory pane"  "drawing area"  "file chooser" "fontchooser"  "frame"  "glass pane"  "html container"  "icon"
+//  "internal frame"  "option pane"  "password text" "radio menu item"  "root pane"  "row header"    "scroll pane"
+//  "tear off menu item"  "terminal" "toggle button" "tree table"  "unknown"  "viewport" "header"  "footer"  "paragraph"
+//  "ruler"    "autocomplete"  "edit bar" "embedded component"  "entry"    "caption"
+//  "heading"  "page"  "section"  "redundant object"  "form"  "input method window"  "menu"
+
+QT_BEGIN_NAMESPACE
+
+QHash <QAccessible::Role, RoleNames> qSpiRoleMapping;
+
+quint64 spiStatesFromQState(QAccessible::State state)
+{
+    quint64 spiState = 0;
+
+    setSpiStateBit(&spiState, ATSPI_STATE_EDITABLE);
+    setSpiStateBit(&spiState, ATSPI_STATE_ENABLED);
+    setSpiStateBit(&spiState, ATSPI_STATE_SHOWING);
+    setSpiStateBit(&spiState, ATSPI_STATE_VISIBLE);
+    setSpiStateBit(&spiState, ATSPI_STATE_SENSITIVE);
+
+    if (state.disabled) {
+        unsetSpiStateBit(&spiState, ATSPI_STATE_ENABLED);
+        unsetSpiStateBit(&spiState, ATSPI_STATE_SHOWING);
+        unsetSpiStateBit(&spiState, ATSPI_STATE_VISIBLE);
+        unsetSpiStateBit(&spiState, ATSPI_STATE_SENSITIVE);
+    }
+
+    if (state.selected)
+        setSpiStateBit(&spiState, ATSPI_STATE_SELECTED);
+    if (state.focused)
+        setSpiStateBit(&spiState, ATSPI_STATE_FOCUSED);
+    if (state.pressed)
+        setSpiStateBit(&spiState, ATSPI_STATE_PRESSED);
+    if (state.checked)
+        setSpiStateBit(&spiState, ATSPI_STATE_CHECKED);
+    if (state.checkStateMixed)
+        setSpiStateBit(&spiState, ATSPI_STATE_INDETERMINATE);
+    if (state.readOnly)
+        unsetSpiStateBit(&spiState, ATSPI_STATE_EDITABLE);
+    //        if (state.HotTracked)
+    if (state.defaultButton)
+        setSpiStateBit(&spiState, ATSPI_STATE_IS_DEFAULT);
+    if (state.expanded)
+        setSpiStateBit(&spiState, ATSPI_STATE_EXPANDED);
+    if (state.collapsed)
+        setSpiStateBit(&spiState, ATSPI_STATE_COLLAPSED);
+    if (state.busy)
+        setSpiStateBit(&spiState, ATSPI_STATE_BUSY);
+    if (state.marqueed || state.animated)
+        setSpiStateBit(&spiState, ATSPI_STATE_ANIMATED);
+    if (state.invisible || state.offscreen) {
+        unsetSpiStateBit(&spiState, ATSPI_STATE_SHOWING);
+        unsetSpiStateBit(&spiState, ATSPI_STATE_VISIBLE);
+    }
+    if (state.sizeable)
+        setSpiStateBit(&spiState, ATSPI_STATE_RESIZABLE);
+    //        if (state.Movable)
+    //        if (state.SelfVoicing)
+    if (state.focusable)
+        setSpiStateBit(&spiState, ATSPI_STATE_FOCUSABLE);
+    if (state.selectable)
+        setSpiStateBit(&spiState, ATSPI_STATE_SELECTABLE);
+    //        if (state.Linked)
+    if (state.traversed)
+        setSpiStateBit(&spiState, ATSPI_STATE_VISITED);
+    if (state.multiSelectable)
+        setSpiStateBit(&spiState, ATSPI_STATE_MULTISELECTABLE);
+    if (state.extSelectable)
+        setSpiStateBit(&spiState, ATSPI_STATE_SELECTABLE);
+    //        if (state.Protected)
+    //        if (state.HasPopup)
+    if (state.modal)
+        setSpiStateBit(&spiState, ATSPI_STATE_MODAL);
+
+    // Not implemented in Qt
+    //    if (state.singleLine)
+    //        setSpiStateBit(&spiState, ATSPI_STATE_SINGLE_LINE);
+
+    return spiState;
+}
+
+QSpiUIntList spiStateSetFromSpiStates(quint64 states)
+{
+    uint low = states & 0xFFFFFFFF;
+    uint high = (states >> 32) & 0xFFFFFFFF;
+
+    QSpiUIntList stateList;
+    stateList.append(low);
+    stateList.append(high);
+    return stateList;
+}
+
+AtspiRelationType qAccessibleRelationToAtSpiRelation(QAccessible::Relation relation)
+{
+    switch (relation) {
+    case QAccessible::Label:
+        return ATSPI_RELATION_LABELLED_BY;
+    case QAccessible::Labelled:
+        return ATSPI_RELATION_LABEL_FOR;
+    case QAccessible::Controller:
+        return ATSPI_RELATION_CONTROLLED_BY;
+    case QAccessible::Controlled:
+        return ATSPI_RELATION_CONTROLLER_FOR;
+    default:
+        qWarning() << "Cannot return AT-SPI relation for:" << relation;
+    }
+    return ATSPI_RELATION_NULL;
+}
+
+QT_END_NAMESPACE
diff --git a/src/platformsupport/linuxaccessibility/constant_mappings_p.h b/src/platformsupport/linuxaccessibility/constant_mappings_p.h
new file mode 100644 (file)
index 0000000..e089233
--- /dev/null
@@ -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 QtGui module 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$
+**
+****************************************************************************/
+
+
+/*
+ * This file contains AT-SPI constants and mappings between QAccessible
+ * and AT-SPI constants such as 'role' and 'state' enumerations.
+ */
+
+#ifndef Q_SPI_CONSTANT_MAPPINGS_H
+#define Q_SPI_CONSTANT_MAPPINGS_H
+
+#include "struct_marshallers_p.h"
+
+#include <QtGui/QAccessible>
+#include <atspi/atspi-constants.h>
+
+
+// interface names from at-spi2-core/atspi/atspi-misc-private.h
+#define ATSPI_DBUS_NAME_REGISTRY "org.a11y.atspi.Registry"
+#define ATSPI_DBUS_PATH_REGISTRY "/org/a11y/atspi/registry"
+#define ATSPI_DBUS_INTERFACE_REGISTRY "org.a11y.atspi.Registry"
+
+#define ATSPI_DBUS_PATH_ROOT "/org/a11y/atspi/accessible/root"
+
+#define ATSPI_DBUS_PATH_DEC "/org/a11y/atspi/registry/deviceeventcontroller"
+#define ATSPI_DBUS_INTERFACE_DEC "org.a11y.atspi.DeviceEventController"
+#define ATSPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER "org.a11y.atspi.DeviceEventListener"
+
+#define ATSPI_DBUS_INTERFACE_CACHE "org.a11y.atspi.Cache"
+#define ATSPI_DBUS_INTERFACE_ACCESSIBLE "org.a11y.atspi.Accessible"
+#define ATSPI_DBUS_INTERFACE_ACTION "org.a11y.atspi.Action"
+#define ATSPI_DBUS_INTERFACE_APPLICATION "org.a11y.atspi.Application"
+#define ATSPI_DBUS_INTERFACE_COLLECTION "org.a11y.atspi.Collection"
+#define ATSPI_DBUS_INTERFACE_COMPONENT "org.a11y.atspi.Component"
+#define ATSPI_DBUS_INTERFACE_DOCUMENT "org.a11y.atspi.Document"
+#define ATSPI_DBUS_INTERFACE_EDITABLE_TEXT "org.a11y.atspi.EditableText"
+#define ATSPI_DBUS_INTERFACE_EVENT_KEYBOARD "org.a11y.atspi.Event.Keyboard"
+#define ATSPI_DBUS_INTERFACE_EVENT_MOUSE "org.a11y.atspi.Event.Mouse"
+#define ATSPI_DBUS_INTERFACE_EVENT_OBJECT "org.a11y.atspi.Event.Object"
+#define ATSPI_DBUS_INTERFACE_HYPERLINK "org.a11y.atspi.Hyperlink"
+#define ATSPI_DBUS_INTERFACE_HYPERTEXT "org.a11y.atspi.Hypertext"
+#define ATSPI_DBUS_INTERFACE_IMAGE "org.a11y.atspi.Image"
+#define ATSPI_DBUS_INTERFACE_SELECTION "org.a11y.atspi.Selection"
+#define ATSPI_DBUS_INTERFACE_TABLE "org.a11y.atspi.Table"
+#define ATSPI_DBUS_INTERFACE_TEXT "org.a11y.atspi.Text"
+#define ATSPI_DBUS_INTERFACE_VALUE "org.a11y.atspi.Value"
+#define ATSPI_DBUS_INTERFACE_SOCKET "org.a11y.atspi.Socket"
+
+// missing from at-spi2-core:
+#define ATSPI_DBUS_INTERFACE_EVENT_WINDOW "org.a11y.atspi.Event.Window"
+#define ATSPI_DBUS_INTERFACE_EVENT_FOCUS  "org.a11y.atspi.Event.Focus"
+
+#define QSPI_OBJECT_PATH_ACCESSIBLE  "/org/a11y/atspi/accessible"
+#define QSPI_OBJECT_PATH_PREFIX      "/org/a11y/atspi/accessible/"
+#define QSPI_OBJECT_PATH_ROOT    QSPI_OBJECT_PATH_PREFIX "root"
+
+#define QSPI_REGISTRY_NAME "org.a11y.atspi.Registry"
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+struct RoleNames {
+    RoleNames() {}
+    RoleNames(AtspiRole r, const QString& n, const QString& ln)
+        :m_spiRole(r), m_name(n), m_localizedName(ln)
+    {}
+
+    AtspiRole spiRole() const {return m_spiRole;}
+    QString name() const {return m_name;}
+    QString localizedName() const {return m_localizedName;}
+
+private:
+    AtspiRole m_spiRole;
+    QString m_name;
+    QString m_localizedName;
+};
+
+extern QHash <QAccessible::Role, RoleNames> qSpiRoleMapping;
+extern QHash <int, AtspiStateType> qSpiStateMapping;
+
+inline void setSpiStateBit(quint64* state, AtspiStateType spiState)
+{
+    *state |= quint64(1) << spiState;
+}
+
+inline void unsetSpiStateBit(quint64* state, AtspiStateType spiState)
+{
+    *state &= ~(quint64(1) << spiState);
+}
+
+quint64 spiStatesFromQState(QAccessible::State state);
+QSpiUIntList spiStateSetFromSpiStates(quint64 states);
+
+AtspiRelationType qAccessibleRelationToAtSpiRelation(QAccessible::Relation relation);
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#endif /* Q_SPI_CONSTANT_MAPPINGS_H */
diff --git a/src/platformsupport/linuxaccessibility/dbusconnection.cpp b/src/platformsupport/linuxaccessibility/dbusconnection.cpp
new file mode 100644 (file)
index 0000000..4d9f315
--- /dev/null
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 "dbusconnection_p.h"
+
+#include <QtDBus/QDBusMessage>
+#include <qdebug.h>
+
+/*!
+    \class DBusConnection
+
+    \brief DBusConnection
+
+    DBusConnection
+*/
+
+QT_BEGIN_NAMESPACE
+
+/*!
+  Connects to the accessibility dbus.
+
+  This is usually a different bus from the session bus.
+*/
+DBusConnection::DBusConnection()
+    : dbusConnection(connectDBus())
+{}
+
+QDBusConnection DBusConnection::connectDBus()
+{
+    QString address = getAccessibilityBusAddress();
+
+    if (!address.isEmpty()) {
+        QDBusConnection c = QDBusConnection::connectToBus(address, QStringLiteral("a11y"));
+        if (c.isConnected()) {
+            qDebug() << "Connected to accessibility bus at: " << address;
+            return c;
+        }
+        qWarning("Found Accessibility DBus address but cannot connect. Falling back to session bus.");
+    } else {
+        qWarning("Accessibility DBus not found. Falling back to session bus.");
+    }
+
+    QDBusConnection c = QDBusConnection::sessionBus();
+    if (!c.isConnected()) {
+        qWarning("Could not connect to DBus.");
+    }
+    return QDBusConnection::sessionBus();
+}
+
+QString DBusConnection::getAccessibilityBusAddress() const
+{
+    QDBusConnection c = QDBusConnection::sessionBus();
+
+    QDBusMessage m = QDBusMessage::createMethodCall(QLatin1String("org.a11y.Bus"),
+                                                    QLatin1String("/org/a11y/bus"),
+                                                    QLatin1String("org.a11y.Bus"), QLatin1String("GetAddress"));
+    QDBusMessage reply = c.call(m);
+    if (reply.type() == QDBusMessage::ErrorMessage) {
+        qWarning() << "Qt at-spi: error getting the accessibility dbus address: " << reply.errorMessage();
+        return QString();
+    }
+
+    QString busAddress = reply.arguments().at(0).toString();
+    qDebug() << "Got bus address: " << busAddress;
+    return busAddress;
+}
+
+/*!
+  Returns the DBus connection that got established.
+*/
+QDBusConnection DBusConnection::connection() const
+{
+    return dbusConnection;
+}
+
+QT_END_NAMESPACE
diff --git a/src/platformsupport/linuxaccessibility/dbusconnection_p.h b/src/platformsupport/linuxaccessibility/dbusconnection_p.h
new file mode 100644 (file)
index 0000000..387c0e0
--- /dev/null
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 DBUSCONNECTION_H
+#define DBUSCONNECTION_H
+
+#include <QtCore/QString>
+#include <QtDBus/QDBusConnection>
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+class DBusConnection
+{
+public:
+    DBusConnection();
+    QDBusConnection connection() const;
+
+private:
+    QString getAccessibilityBusAddress() const;
+    QDBusConnection connectDBus();
+
+    QDBusConnection dbusConnection;
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#endif // DBUSCONNECTION_H
diff --git a/src/platformsupport/linuxaccessibility/linuxaccessibility.pri b/src/platformsupport/linuxaccessibility/linuxaccessibility.pri
new file mode 100644 (file)
index 0000000..66d69ad
--- /dev/null
@@ -0,0 +1,27 @@
+contains(QT_CONFIG, dbus):contains(QT_CONFIG, xcb):contains(QT_CONFIG, accessibility) {
+
+    PKGCONFIG += atspi-2
+    CONFIG += link_pkgconfig
+    QT += dbus
+    include(../../3rdparty/atspi2/atspi2.pri)
+
+    INCLUDEPATH += $$PWD
+
+    HEADERS += \
+        $$PWD/application_p.h \
+        $$PWD/bridge_p.h \
+        $$PWD/cache_p.h  \
+        $$PWD/struct_marshallers_p.h \
+        $$PWD/constant_mappings_p.h \
+        $$PWD/dbusconnection_p.h \
+        $$PWD/atspiadaptor_p.h
+
+    SOURCES += \
+        $$PWD/application.cpp \
+        $$PWD/bridge.cpp \
+        $$PWD/cache.cpp  \
+        $$PWD/struct_marshallers.cpp \
+        $$PWD/constant_mappings.cpp \
+        $$PWD/dbusconnection.cpp \
+        $$PWD/atspiadaptor.cpp
+}
diff --git a/src/platformsupport/linuxaccessibility/main.cpp b/src/platformsupport/linuxaccessibility/main.cpp
new file mode 100644 (file)
index 0000000..9087286
--- /dev/null
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 "bridge.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+    \class QSpiAccessibleBridgePlugin
+
+    \brief QSpiAccessibleBridgePlugin
+
+    QSpiAccessibleBridgePlugin
+*/
+
+class QSpiAccessibleBridgePlugin: public QAccessibleBridgePlugin
+{
+    Q_OBJECT
+    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QAccessibleBridgeFactoryInterface" FILE "linuxaccessibility.json");
+public:
+    QSpiAccessibleBridgePlugin(QObject *parent = 0);
+    virtual ~QSpiAccessibleBridgePlugin() {}
+
+    virtual QAccessibleBridge* create(const QString &key);
+    virtual QStringList keys() const;
+};
+
+/*!
+  The contructor of the plugin.
+  */
+QSpiAccessibleBridgePlugin::QSpiAccessibleBridgePlugin(QObject *parent)
+: QAccessibleBridgePlugin(parent)
+{
+}
+
+/*!
+  Creates a new instance of the QAccessibleBridge plugin.
+  */
+QAccessibleBridge* QSpiAccessibleBridgePlugin::create(const QString &key)
+{
+    if (key == "QSPIACCESSIBLEBRIDGE")
+        return new QSpiAccessibleBridge();
+    return 0;
+}
+
+/*!
+
+  */
+QStringList QSpiAccessibleBridgePlugin::keys() const
+{
+    return QStringList() << "QSPIACCESSIBLEBRIDGE";
+}
+
+QT_END_NAMESPACE
diff --git a/src/platformsupport/linuxaccessibility/struct_marshallers.cpp b/src/platformsupport/linuxaccessibility/struct_marshallers.cpp
new file mode 100644 (file)
index 0000000..61e1a76
--- /dev/null
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 "struct_marshallers_p.h"
+
+#include <atspi/atspi-constants.h>
+#include <QtCore/qdebug.h>
+#include <QtDBus/qdbusmetatype.h>
+
+#include "bridge_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSpiObjectReference::QSpiObjectReference()
+    : path(QDBusObjectPath(ATSPI_DBUS_PATH_NULL))
+{}
+
+/* QSpiAccessibleCacheArray */
+/*---------------------------------------------------------------------------*/
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAccessibleCacheItem &item)
+{
+    argument.beginStructure();
+    argument << item.path;
+    argument << item.application;
+    argument << item.parent;
+    argument << item.children;
+    argument << item.supportedInterfaces;
+    argument << item.name;
+    argument << item.role;
+    argument << item.description;
+    argument << item.state;
+    argument.endStructure();
+    return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAccessibleCacheItem &item)
+{
+    argument.beginStructure();
+    argument >> item.path;
+    argument >> item.application;
+    argument >> item.parent;
+    argument >> item.children;
+    argument >> item.supportedInterfaces;
+    argument >> item.name;
+    argument >> item.role;
+    argument >> item.description;
+    argument >> item.state;
+    argument.endStructure();
+    return argument;
+}
+
+/* QSpiObjectReference */
+/*---------------------------------------------------------------------------*/
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiObjectReference &address)
+{
+    argument.beginStructure();
+    argument << address.service;
+    argument << address.path;
+    argument.endStructure();
+    return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiObjectReference &address)
+{
+    argument.beginStructure();
+    argument >> address.service;
+    argument >> address.path;
+    argument.endStructure();
+    return argument;
+}
+
+/* QSpiAction */
+/*---------------------------------------------------------------------------*/
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAction &action)
+{
+    argument.beginStructure();
+    argument << action.name;
+    argument << action.description;
+    argument << action.keyBinding;
+    argument.endStructure();
+    return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAction &action)
+{
+    Q_UNUSED(action)
+    qWarning() << "QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAction &action): implement me!";
+    return argument;
+}
+
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiEventListener &ev)
+{
+    argument.beginStructure();
+    argument << ev.listenerAddress;
+    argument << ev.eventName;
+    argument.endStructure();
+    return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiEventListener &ev)
+{
+    argument.beginStructure();
+    argument >> ev.listenerAddress;
+    argument >> ev.eventName;
+    argument.endStructure();
+    return argument;
+}
+
+/* QSpiAppUpdate */
+/*---------------------------------------------------------------------------*/
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAppUpdate &update) {
+    argument.beginStructure();
+    argument << update.type << update.address;
+    argument.endStructure();
+    return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAppUpdate &update) {
+    argument.beginStructure();
+    argument >> update.type >> update.address;
+    argument.endStructure();
+    return argument;
+}
+
+/* QSpiRelationArrayEntry */
+/*---------------------------------------------------------------------------*/
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiRelationArrayEntry &entry) {
+    argument.beginStructure();
+    argument << entry.first << entry.second;
+    argument.endStructure();
+    return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiRelationArrayEntry &entry) {
+    argument.beginStructure();
+    argument >> entry.first >> entry.second;
+    argument.endStructure();
+    return argument;
+}
+
+/* QSpiDeviceEvent */
+/*---------------------------------------------------------------------------*/
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiDeviceEvent &event) {
+    argument.beginStructure();
+    argument << event.type
+             << event.id
+             << event.hardwareCode
+             << event.modifiers
+             << event.timestamp
+             << event.text
+             << event.isText;
+    argument.endStructure();
+    return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiDeviceEvent &event) {
+    argument.beginStructure();
+    argument >> event.type
+             >> event.id
+             >> event.hardwareCode
+             >> event.modifiers
+             >> event.timestamp
+             >> event.text
+             >> event.isText;
+    argument.endStructure();
+    return argument;
+}
+
+void qSpiInitializeStructTypes()
+{
+    qDBusRegisterMetaType<QSpiIntList>();
+    qDBusRegisterMetaType<QSpiUIntList>();
+    qDBusRegisterMetaType<QSpiAccessibleCacheItem>();
+    qDBusRegisterMetaType<QSpiAccessibleCacheArray>();
+    qDBusRegisterMetaType<QSpiObjectReference>();
+    qDBusRegisterMetaType<QSpiObjectReferenceArray>();
+    qDBusRegisterMetaType<QSpiAttributeSet>();
+    qDBusRegisterMetaType<QSpiAction>();
+    qDBusRegisterMetaType<QSpiActionArray>();
+    qDBusRegisterMetaType<QSpiEventListener>();
+    qDBusRegisterMetaType<QSpiEventListenerArray>();
+    qDBusRegisterMetaType<QSpiDeviceEvent>();
+    qDBusRegisterMetaType<QSpiAppUpdate>();
+    qDBusRegisterMetaType<QSpiRelationArrayEntry>();
+    qDBusRegisterMetaType<QSpiRelationArray>();
+}
+
+QT_END_NAMESPACE
diff --git a/src/platformsupport/linuxaccessibility/struct_marshallers_p.h b/src/platformsupport/linuxaccessibility/struct_marshallers_p.h
new file mode 100644 (file)
index 0000000..f161b46
--- /dev/null
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 Q_SPI_STRUCT_MARSHALLERS_H
+#define Q_SPI_STRUCT_MARSHALLERS_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qpair.h>
+#include <QtDBus/QDBusArgument>
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusObjectPath>
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+typedef QList <int> QSpiIntList;
+typedef QList <uint> QSpiUIntList;
+
+// FIXME: make this copy on write
+struct QSpiObjectReference
+{
+    QString service;
+    QDBusObjectPath path;
+
+    QSpiObjectReference();
+    QSpiObjectReference(const QDBusConnection& connection, const QDBusObjectPath& path)
+        : service(connection.baseService()), path(path) {}
+};
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiObjectReference &address);
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiObjectReference &address);
+
+typedef QList <QSpiObjectReference> QSpiObjectReferenceArray;
+
+struct QSpiAccessibleCacheItem
+{
+    QSpiObjectReference         path;
+    QSpiObjectReference         application;
+    QSpiObjectReference         parent;
+    QList <QSpiObjectReference> children;
+    QStringList                 supportedInterfaces;
+    QString                     name;
+    uint                        role;
+    QString                     description;
+    QSpiUIntList                state;
+};
+
+typedef QList <QSpiAccessibleCacheItem> QSpiAccessibleCacheArray;
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAccessibleCacheItem &item);
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAccessibleCacheItem &item);
+
+struct QSpiAction
+{
+    QString name;
+    QString description;
+    QString keyBinding;
+};
+
+typedef QList <QSpiAction> QSpiActionArray;
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAction &action);
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAction &action);
+
+struct QSpiEventListener
+{
+    QString listenerAddress;
+    QString eventName;
+};
+
+typedef QList <QSpiEventListener> QSpiEventListenerArray;
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiEventListener &action);
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiEventListener &action);
+
+typedef QPair < unsigned int, QList < QSpiObjectReference > > QSpiRelationArrayEntry;
+typedef QList< QSpiRelationArrayEntry > QSpiRelationArray;
+
+//a(iisv)
+struct QSpiTextRange {
+    int startOffset;
+    int endOffset;
+    QString contents;
+    QVariant v;
+};
+typedef QList <QSpiTextRange> QSpiTextRangeList;
+typedef QMap <QString, QString> QSpiAttributeSet;
+
+enum QSpiAppUpdateType {
+    QSPI_APP_UPDATE_ADDED = 0,
+    QSPI_APP_UPDATE_REMOVED = 1
+};
+
+struct QSpiAppUpdate {
+    int type; /* Is an application added or removed */
+    QString address; /* D-Bus address of application added or removed */
+};
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiAppUpdate &update);
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiAppUpdate &update);
+
+struct QSpiDeviceEvent {
+    unsigned int type;
+    int id;
+    int hardwareCode;
+    int modifiers;
+    int timestamp;
+    QString text;
+    bool isText;
+};
+
+QDBusArgument &operator<<(QDBusArgument &argument, const QSpiDeviceEvent &event);
+const QDBusArgument &operator>>(const QDBusArgument &argument, QSpiDeviceEvent &event);
+
+void qSpiInitializeStructTypes();
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QSpiIntList)
+Q_DECLARE_METATYPE(QSpiUIntList)
+Q_DECLARE_METATYPE(QSpiObjectReference)
+Q_DECLARE_METATYPE(QSpiObjectReferenceArray)
+Q_DECLARE_METATYPE(QSpiAccessibleCacheItem)
+Q_DECLARE_METATYPE(QSpiAccessibleCacheArray)
+Q_DECLARE_METATYPE(QSpiAction)
+Q_DECLARE_METATYPE(QSpiActionArray)
+Q_DECLARE_METATYPE(QSpiEventListener)
+Q_DECLARE_METATYPE(QSpiEventListenerArray)
+Q_DECLARE_METATYPE(QSpiRelationArrayEntry)
+Q_DECLARE_METATYPE(QSpiRelationArray)
+Q_DECLARE_METATYPE(QSpiTextRange)
+Q_DECLARE_METATYPE(QSpiTextRangeList)
+Q_DECLARE_METATYPE(QSpiAttributeSet)
+Q_DECLARE_METATYPE(QSpiAppUpdate)
+Q_DECLARE_METATYPE(QSpiDeviceEvent)
+
+QT_END_HEADER
+#endif /* Q_SPI_STRUCT_MARSHALLERS_H */
index 848e4aa..5cb8833 100644 (file)
@@ -21,3 +21,4 @@ include(input/input.pri)
 include(devicediscovery/devicediscovery.pri)
 include(services/services.pri)
 include(themes/themes.pri)
+include(linuxaccessibility/linuxaccessibility.pri)
index f7576fe..501b18a 100644 (file)
@@ -85,6 +85,7 @@
 #include <QtGui/QScreen>
 #ifndef QT_NO_ACCESSIBILITY
 #include <qpa/qplatformaccessibility.h>
+#include "../../../platformsupport/linuxaccessibility/bridge_p.h"
 #endif
 
 QT_BEGIN_NAMESPACE
@@ -117,7 +118,7 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters)
     m_fontDatabase.reset(new QGenericUnixFontDatabase());
     m_inputContext.reset(QPlatformInputContextFactory::create());
 #ifndef QT_NO_ACCESSIBILITY
-    m_accessibility.reset(new QPlatformAccessibility());
+    m_accessibility.reset(new QSpiAccessibleBridge());
 #endif
 }
 
index b172bbf..0d96c7d 100644 (file)
@@ -16,6 +16,7 @@ SUBDIRS=\
    modeltest \
    networkselftest \
    qaccessibility \
+   qaccessibilitylinux \
    qcomplextext \
    qfocusevent \
    qnetworkaccessmanager_and_qprogressdialog \
@@ -45,7 +46,9 @@ cross_compile: SUBDIRS -= \
    atwrapper \
    compiler
 
-wince*|!contains(QT_CONFIG, accessibility):SUBDIRS -= qaccessibility
+wince*|!contains(QT_CONFIG, accessibility): SUBDIRS -= qaccessibility
+
+!contains(QT_CONFIG, accessibility)|!contains(QT_CONFIG, xcb): SUBDIRS -= qaccessibilitylinux
 
 !mac: SUBDIRS -= \
            macgui \
diff --git a/tests/auto/other/qaccessibilitylinux/qaccessibilitylinux.pro b/tests/auto/other/qaccessibilitylinux/qaccessibilitylinux.pro
new file mode 100644 (file)
index 0000000..db7001e
--- /dev/null
@@ -0,0 +1,12 @@
+CONFIG += testcase
+TARGET = tst_qaccessibilitylinux
+SOURCES += tst_qaccessibilitylinux.cpp \
+    ../../../../src/platformsupport/linuxaccessibility/dbusconnection.cpp \
+    ../../../../src/platformsupport/linuxaccessibility/struct_marshallers.cpp
+
+CONFIG += gui
+CONFIG += link_pkgconfig
+
+QT += gui widgets dbus testlib
+
+PKGCONFIG += atspi-2
diff --git a/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp b/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp
new file mode 100644 (file)
index 0000000..8cf9354
--- /dev/null
@@ -0,0 +1,467 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtGui module 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 <QtTest/QtTest>
+#include <QtGui>
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QLabel>
+#include <QtWidgets/QLineEdit>
+#include <QtWidgets/QListWidget>
+#include <QtWidgets/QTreeWidget>
+#include <QtWidgets/QTextEdit>
+
+#include <QDBusArgument>
+#include <QDBusConnection>
+#include <QDBusMessage>
+#include <QDBusInterface>
+#include <QDBusReply>
+
+#include <atspi/atspi-constants.h>
+
+#include "../../../../src/plugins/platforms/linuxaccessibility/dbusconnection.h"
+#include "../../../../src/plugins/platforms/linuxaccessibility/struct_marshallers.h"
+
+#define COMPARE3(v1, v2, v3) QCOMPARE(v1, v3); QCOMPARE(v2, v3);
+
+class AccessibleTestWindow : public QWidget
+{
+    Q_OBJECT
+public:
+    AccessibleTestWindow()
+    {
+        DBusConnection c;
+        m_address = c.connection().baseService().toLatin1().data();
+        new QHBoxLayout(this);
+    }
+    QString dbusAddress() const { return m_address; }
+
+    void addWidget(QWidget* widget)
+    {
+        layout()->addWidget(widget);
+        widget->show();
+        QTest::qWaitForWindowShown(widget);
+    }
+
+    void clearChildren()
+    {
+        qDeleteAll(children());
+        new QHBoxLayout(this);
+    }
+
+private:
+    QString m_address;
+    QString m_bus;
+};
+
+
+class tst_QtAtSpi : public QObject
+{
+    Q_OBJECT
+
+private slots:
+    void initTestCase();
+
+    void testLabel();
+    void testLineEdit();
+    void testListWidget();
+    void testTreeWidget();
+    void testTextEdit();
+    void testSlider();
+
+    void cleanupTestCase();
+
+private:
+    void registerDbus();
+    static QString getParent(QDBusInterface *interface);
+    static QStringList getChildren(QDBusInterface *interface);
+    QDBusInterface *getInterface(const QString &path, const QString &interfaceName);
+
+    AccessibleTestWindow *m_window;
+
+    QString bus;
+    QString address;
+    QDBusInterface *root; // the root object on dbus (for the app)
+    QDBusInterface *rootApplication;
+    QDBusInterface *mainWindow;
+
+    DBusConnection dbus;
+};
+
+// helper to find children of a dbus object
+QStringList tst_QtAtSpi::getChildren(QDBusInterface *interface)
+{
+    QSpiObjectReferenceArray list;
+    interface->call(QDBus::Block, "GetChildren").arguments().first().value<QDBusArgument>() >> list;
+
+    Q_ASSERT(interface->property("ChildCount").toInt() == list.count());
+
+    QStringList children;
+    Q_FOREACH (const QSpiObjectReference &ref, list)
+        children << ref.path.path();
+
+    return children;
+}
+
+QString tst_QtAtSpi::getParent(QDBusInterface *interface)
+{
+    if (!interface->isValid())
+        return QString();
+
+    QVariant var = interface->property("Parent");
+    if (!var.canConvert<QSpiObjectReference>()) {
+        qWarning() << "Invalid parent";
+        return QString();
+    }
+    QSpiObjectReference parent = var.value<QSpiObjectReference>();
+    return parent.path.path();
+}
+
+// helper to get dbus object
+QDBusInterface *tst_QtAtSpi::getInterface(const QString &path, const QString &interfaceName)
+{
+    return new QDBusInterface(address, path, interfaceName, dbus.connection(), this);
+}
+
+
+void tst_QtAtSpi::initTestCase()
+{
+    // Oxygen style creates many extra items, it's simply unusable here
+    qDebug() << "Using plastique style...";
+    qApp->setStyle("plastique");
+    qApp->setApplicationName("tst_QtAtSpi app");
+    dbus = DBusConnection();
+
+    m_window = new AccessibleTestWindow();
+    m_window->show();
+
+    // this has the side-effect of immediately activating accessibility
+    qDebug() << "Explicitly activating accessibility...";
+    delete QAccessible::queryAccessibleInterface(m_window);
+
+    QTest::qWaitForWindowShown(m_window);
+
+    address = m_window->dbusAddress();
+    registerDbus();
+
+    QStringList appChildren = getChildren(root);
+    QString window = appChildren.at(0);
+    mainWindow = getInterface(window, "org.a11y.atspi.Accessible");
+}
+
+void tst_QtAtSpi::cleanupTestCase()
+{
+    delete mainWindow;
+    delete rootApplication;
+    delete root;
+    delete m_window;
+}
+
+void tst_QtAtSpi::registerDbus()
+{
+    QVERIFY(dbus.connection().isConnected());
+
+    root = getInterface("/org/a11y/atspi/accessible/root",
+                        "org.a11y.atspi.Accessible");
+
+    rootApplication = getInterface("/org/a11y/atspi/accessible/root",
+                                   "org.a11y.atspi.Application");
+    QVERIFY(root->isValid());
+    QVERIFY(rootApplication->isValid());
+
+    QStringList appChildren = getChildren(root);
+    QString window = appChildren.at(0);
+    mainWindow = getInterface(window, "org.a11y.atspi.Accessible");
+}
+
+#define ROOTPATH "/org/a11y/atspi/accessible"
+
+void tst_QtAtSpi::testLabel()
+{
+    QLabel *l = new QLabel(m_window);
+    l->setText("Hello A11y");
+    m_window->addWidget(l);
+
+    // Application
+    QCOMPARE(getParent(mainWindow), QLatin1String(ATSPI_DBUS_PATH_ROOT));
+    QStringList children = getChildren(mainWindow);
+
+    QDBusInterface *labelInterface = getInterface(children.at(0), "org.a11y.atspi.Accessible");
+    QVERIFY(labelInterface->isValid());
+    QCOMPARE(labelInterface->property("Name").toString(), QLatin1String("Hello A11y"));
+    QCOMPARE(getChildren(labelInterface).count(), 0);
+    QCOMPARE(labelInterface->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("label"));
+    QCOMPARE(labelInterface->call(QDBus::Block, "GetRole").arguments().first().toUInt(), 29u);
+    QCOMPARE(getParent(labelInterface), mainWindow->path());
+
+    l->setText("New text");
+    QCOMPARE(labelInterface->property("Name").toString(), l->text());
+
+    m_window->clearChildren();
+    delete labelInterface;
+}
+
+void tst_QtAtSpi::testLineEdit()
+{
+    QLineEdit *lineEdit = new QLineEdit(m_window);
+    lineEdit->setText("a11y test QLineEdit");
+    m_window->addWidget(lineEdit);
+
+    QStringList children = getChildren(mainWindow);
+
+    QDBusInterface *accessibleInterface = getInterface(children.at(0), "org.a11y.atspi.Accessible");
+    QDBusInterface *editableTextInterface = getInterface(children.at(0), "org.a11y.atspi.EditableText");
+    QDBusInterface *textInterface = getInterface(children.at(0), "org.a11y.atspi.Text");
+    QVERIFY(accessibleInterface->isValid());
+    QVERIFY(editableTextInterface->isValid());
+    QVERIFY(textInterface->isValid());
+
+    QCOMPARE(accessibleInterface->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("text"));
+    QCOMPARE(textInterface->call(QDBus::Block,"GetText", 5, -1).arguments().first().toString(), QLatin1String("test QLineEdit"));
+    QString newText = "Text has changed!";
+    editableTextInterface->call(QDBus::Block, "SetTextContents", newText);
+    COMPARE3(lineEdit->text(), textInterface->call(QDBus::Block, "GetText", 0, -1).arguments().first().toString(), newText);
+    QCOMPARE(textInterface->call(QDBus::Block, "GetText", 0, 4).arguments().first().toString(), QLatin1String("Text"));
+    editableTextInterface->call(QDBus::Block, "DeleteText", 4, 8);
+    COMPARE3(lineEdit->text(), "Te" + textInterface->call(QDBus::Block, "GetText", 2, 10).arguments().first().toString() + "ed!", QLatin1String("Text changed!"));
+    editableTextInterface->call(QDBus::Block, "InsertText", 12, " again ", 6);
+    QCOMPARE(lineEdit->text(), QLatin1String("Text changed again!"));
+    COMPARE3(lineEdit->text().length(), textInterface->property("CharacterCount").toInt(), 19);
+
+    textInterface->call(QDBus::Block, "SetCaretOffset", 4);
+    COMPARE3(lineEdit->cursorPosition(), textInterface->property("CaretOffset").toInt(), 4);
+
+    textInterface->call(QDBus::Block, "AddSelection", 1, 4);
+    QList<QVariant> data = textInterface->call(QDBus::Block, "GetSelection", 0).arguments();
+    COMPARE3(data.at(0).toInt(), lineEdit->selectionStart(), 1);
+    QCOMPARE(data.at(1).toInt(), 4);
+    QCOMPARE(lineEdit->selectedText().length(), 3);
+    QCOMPARE(textInterface->call(QDBus::Block, "GetNSelections").arguments().first().toInt(), 1);
+    textInterface->call(QDBus::Block, "SetSelection", 0, 0, 5);
+    data = textInterface->call(QDBus::Block, "GetSelection", 0).arguments();
+    COMPARE3(data.at(0).toInt(), lineEdit->selectionStart(), 0);
+    COMPARE3(data.at(1).toInt(), lineEdit->selectedText().length(), 5);
+    textInterface->call(QDBus::Block, "RemoveSelection", 0);
+    QCOMPARE(lineEdit->selectionStart(), -1);
+    QCOMPARE(textInterface->call(QDBus::Block, "GetNSelections").arguments().first().toInt(), 0);
+
+    m_window->clearChildren();
+    delete accessibleInterface;
+    delete textInterface;
+    delete editableTextInterface;
+}
+
+void tst_QtAtSpi::testListWidget()
+{
+    QListWidget *lw = new QListWidget;
+    lw->addItem("Hello");
+    lw->addItem("Good morning");
+    lw->addItem("Good bye");
+    m_window->addWidget(lw);
+
+    QStringList children = getChildren(mainWindow);
+    QDBusInterface *listIface = getInterface(children.at(0), "org.a11y.atspi.Accessible");
+    QCOMPARE(listIface->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("list"));
+    QStringList tableChildren = getChildren(listIface);
+    QCOMPARE(tableChildren.size(), 3);
+
+    QDBusInterface *cell1 = getInterface(tableChildren.at(0), "org.a11y.atspi.Accessible");
+    QCOMPARE(cell1->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("list item"));
+    QCOMPARE(cell1->property("Name").toString(), QLatin1String("Hello"));
+
+    QDBusInterface *cell2 = getInterface(tableChildren.at(1), "org.a11y.atspi.Accessible");
+    QCOMPARE(cell2->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("list item"));
+    QCOMPARE(cell2->property("Name").toString(), QLatin1String("Good morning"));
+
+    QDBusInterface *cell3 = getInterface(tableChildren.at(2), "org.a11y.atspi.Accessible");
+    QCOMPARE(cell3->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("list item"));
+    QCOMPARE(cell3->property("Name").toString(), QLatin1String("Good bye"));
+
+    delete cell1; delete cell2; delete cell3;
+    m_window->clearChildren();
+    delete listIface;
+}
+
+void tst_QtAtSpi::testTreeWidget()
+{
+    QTreeWidget *tree = new QTreeWidget;
+    tree->setColumnCount(2);
+    tree->setHeaderLabels(QStringList() << "Header 1" << "Header 2");
+
+    QTreeWidgetItem *top1 = new QTreeWidgetItem(QStringList() << "0.0" << "0.1");
+    tree->addTopLevelItem(top1);
+
+    QTreeWidgetItem *top2 = new QTreeWidgetItem(QStringList() << "1.0" << "1.1");
+    tree->addTopLevelItem(top2);
+
+    QTreeWidgetItem *child1 = new QTreeWidgetItem(QStringList() << "1.0 0.0" << "1.0 0.1");
+    top2->addChild(child1);
+
+    m_window->addWidget(tree);
+
+    QStringList children = getChildren(mainWindow);
+    QDBusInterface *treeIface = getInterface(children.at(0), "org.a11y.atspi.Accessible");
+    QCOMPARE(treeIface->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("tree"));
+    QStringList tableChildren = getChildren(treeIface);
+
+    QCOMPARE(tableChildren.size(), 6);
+
+    QDBusInterface *cell1 = getInterface(tableChildren.at(0), "org.a11y.atspi.Accessible");
+    QCOMPARE(cell1->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("column header"));
+    QCOMPARE(cell1->property("Name").toString(), QLatin1String("Header 1"));
+
+    QDBusInterface *cell2 = getInterface(tableChildren.at(1), "org.a11y.atspi.Accessible");
+    QCOMPARE(cell2->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("column header"));
+    QCOMPARE(cell2->property("Name").toString(), QLatin1String("Header 2"));
+
+    QDBusInterface *cell3 = getInterface(tableChildren.at(2), "org.a11y.atspi.Accessible");
+    QCOMPARE(cell3->property("Name").toString(), QLatin1String("0.0"));
+
+    QDBusInterface *cell4 = getInterface(tableChildren.at(3), "org.a11y.atspi.Accessible");
+    QCOMPARE(cell4->property("Name").toString(), QLatin1String("0.1"));
+
+    tree->expandItem(top2);
+    tableChildren = getChildren(treeIface);
+    QCOMPARE(tableChildren.size(), 8);
+
+    QDBusInterface *cell5 = getInterface(tableChildren.at(6), "org.a11y.atspi.Accessible");
+    QCOMPARE(cell5->property("Name").toString(), QLatin1String("1.0 0.0"));
+
+    QDBusInterface *cell6 = getInterface(tableChildren.at(7), "org.a11y.atspi.Accessible");
+    QCOMPARE(cell6->property("Name").toString(), QLatin1String("1.0 0.1"));
+
+
+    QDBusInterface *treeTableIface = getInterface(children.at(0), "org.a11y.atspi.Table");
+
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 0).arguments().first().toInt(), -1);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 1).arguments().first().toInt(), -1);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 2).arguments().first().toInt(), 0);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 3).arguments().first().toInt(), 0);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 4).arguments().first().toInt(), 1);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 5).arguments().first().toInt(), 1);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 6).arguments().first().toInt(), 2);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 7).arguments().first().toInt(), 2);
+
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 0).arguments().first().toInt(), 0);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 1).arguments().first().toInt(), 1);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 2).arguments().first().toInt(), 0);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 3).arguments().first().toInt(), 1);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 4).arguments().first().toInt(), 0);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 5).arguments().first().toInt(), 1);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 6).arguments().first().toInt(), 0);
+    QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 7).arguments().first().toInt(), 1);
+
+    delete treeTableIface;
+    delete cell1; delete cell2; delete cell3; delete cell4; delete cell5; delete cell6;
+    m_window->clearChildren();
+    delete treeIface;
+}
+
+void tst_QtAtSpi::testTextEdit()
+{
+    QTextEdit *textEdit = new QTextEdit(m_window);
+    textEdit->setText("<html><head></head><body>This is a <b>sample</b> text.<br />"
+                      "How are you today</body></html>");
+    textEdit->show();
+    m_window->addWidget(textEdit);
+
+    QStringList children = getChildren(mainWindow);
+    QDBusInterface *accessibleInterface = getInterface(children.at(0), "org.a11y.atspi.Accessible");
+    QDBusInterface *editableTextInterface = getInterface(children.at(0), "org.a11y.atspi.EditableText");
+    QDBusInterface *textInterface = getInterface(children.at(0), "org.a11y.atspi.Text");
+    QVERIFY(accessibleInterface->isValid());
+    QVERIFY(editableTextInterface->isValid());
+    QVERIFY(textInterface->isValid());
+
+    QList<QVariant> callResult;
+
+    QDBusMessage msg = textInterface->call(QDBus::Block, "GetText", 0, 5);
+    callResult = msg.arguments();
+    QCOMPARE(callResult.at(0).toString(), QLatin1String("This "));
+
+    msg = textInterface->call(QDBus::Block, "GetTextAtOffset", 12, (uint) ATSPI_TEXT_BOUNDARY_WORD_START);
+    callResult = msg.arguments();
+
+    QEXPECT_FAIL("", "Word should contain space at end according to atspi.", Continue);
+    QCOMPARE(callResult.at(0).toString(), QLatin1String("sample "));
+    QCOMPARE(callResult.at(1).toInt(), 10);
+    QEXPECT_FAIL("", "Due to missing space the count is off by one.", Continue);
+    QCOMPARE(callResult.at(2).toInt(), 17);
+
+    // Check if at least CharacterExtents and RangeExtents give a consistent result
+    QDBusReply<QRect> replyRect20 = textInterface->call(QDBus::Block, "GetCharacterExtents", 20, ATSPI_COORD_TYPE_SCREEN);
+    QVERIFY(replyRect20.isValid());
+    QRect r1 = replyRect20.value();
+    QDBusReply<QRect> replyRect21  = textInterface->call(QDBus::Block, "GetCharacterExtents", 21, ATSPI_COORD_TYPE_SCREEN);
+    QRect r2 = replyRect21.value();
+    QDBusReply<QRect> reply = textInterface->call(QDBus::Block, "GetRangeExtents", 20, 21, ATSPI_COORD_TYPE_SCREEN);
+    QRect rect = reply.value();
+    QCOMPARE(rect, r1|r2);
+
+    m_window->clearChildren();
+    delete textInterface;
+}
+
+void tst_QtAtSpi::testSlider()
+{
+    QSlider *slider = new QSlider(m_window);
+    slider->setMinimum(2);
+    slider->setMaximum(5);
+    slider->setValue(3);
+    m_window->addWidget(slider);
+
+    QStringList children = getChildren(mainWindow);
+
+    QDBusInterface *accessibleInterface = getInterface(children.at(0), "org.a11y.atspi.Accessible");
+    QDBusInterface *valueInterface = getInterface(children.at(0), "org.a11y.atspi.Value");
+    QVERIFY(accessibleInterface->isValid());
+    QVERIFY(valueInterface->isValid());
+
+    QCOMPARE(valueInterface->property("CurrentValue").toInt(), 3);
+    QCOMPARE(valueInterface->property("MinimumValue").toInt(), 2);
+    QCOMPARE(valueInterface->property("MaximumValue").toInt(), 5);
+
+    valueInterface->setProperty("CurrentValue", 4);
+    QCOMPARE(valueInterface->property("CurrentValue").toInt(), 4);
+}
+
+QTEST_MAIN(tst_QtAtSpi)
+#include "tst_qaccessibilitylinux.moc"
+