summaryrefslogtreecommitdiffstats
path: root/src/platformsupport/linuxaccessibility/atspiadaptor.cpp
diff options
context:
space:
mode:
authorFrederik Gladhorn <frederik.gladhorn@nokia.com>2012-05-10 23:52:05 +0200
committerQt by Nokia <qt-info@nokia.com>2012-08-14 01:40:06 +0200
commit8e1ff45e74f529c7f49688242ea0fd25ce2913f3 (patch)
treeae13e233a4b9da58d2f4cf5ae0b733e86fe0d5a9 /src/platformsupport/linuxaccessibility/atspiadaptor.cpp
parent85b624a969c2674f726b6e80af1418bdd9f91969 (diff)
Add Linux Accessibility Bridge
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>
Diffstat (limited to 'src/platformsupport/linuxaccessibility/atspiadaptor.cpp')
-rw-r--r--src/platformsupport/linuxaccessibility/atspiadaptor.cpp2316
1 files changed, 2316 insertions, 0 deletions
diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp
new file mode 100644
index 0000000000..f9aedb4d61
--- /dev/null
+++ b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp
@@ -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