| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtGui module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "atspiadaptor_p.h" |
| |
| #include <QtGui/qwindow.h> |
| #include <QtGui/qguiapplication.h> |
| #include <qdbusmessage.h> |
| #include <qdbusreply.h> |
| #include <qclipboard.h> |
| |
| #include <QtCore/qloggingcategory.h> |
| |
| #ifndef QT_NO_ACCESSIBILITY |
| #include "socket_interface.h" |
| #include "constant_mappings_p.h" |
| #include <QtAccessibilitySupport/private/qaccessiblebridgeutils_p.h> |
| |
| #include "application_p.h" |
| /*! |
| \class AtSpiAdaptor |
| \internal |
| |
| \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 coming from Qt via dbus and listens to incoming dbus requests. |
| */ |
| |
| QT_BEGIN_NAMESPACE |
| |
| Q_LOGGING_CATEGORY(lcAccessibilityAtspi, "qt.accessibility.atspi") |
| Q_LOGGING_CATEGORY(lcAccessibilityAtspiCreation, "qt.accessibility.atspi.creation") |
| |
| AtSpiAdaptor::AtSpiAdaptor(DBusConnection *connection, QObject *parent) |
| : QDBusVirtualObject(parent), m_dbus(connection) |
| , 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_state_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))); |
| |
| 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))); |
| } |
| |
| 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" |
| ); |
| |
| QAccessibleInterface * interface = interfaceFromPath(path); |
| if (!interface) { |
| qCDebug(lcAccessibilityAtspi) << "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; |
| } |
| |
| 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")) |
| || right.startsWith(QLatin1String("VisibledataChanged"))) { // typo in libatspi |
| sendObject_visible_data_changed = 1; |
| } else { |
| qCDebug(lcAccessibilityAtspi) << "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 { |
| qCDebug(lcAccessibilityAtspi) << "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: |
| qCDebug(lcAccessibilityAtspi) << "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(); |
| for (const QSpiEventListener &ev : evList) |
| setBitFlag(ev.eventName); |
| m_applicationAdaptor->sendEvents(!evList.isEmpty()); |
| } else { |
| qCDebug(lcAccessibilityAtspi) << "Could not query active accessibility event listeners."; |
| } |
| } |
| |
| void AtSpiAdaptor::eventListenerDeregistered(const QString &/*bus*/, const QString &/*path*/) |
| { |
| // qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::eventListenerDeregistered: " << bus << path; |
| updateEventListeners(); |
| } |
| |
| void AtSpiAdaptor::eventListenerRegistered(const QString &/*bus*/, const QString &/*path*/) |
| { |
| // qCDebug(lcAccessibilityAtspi) << "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); |
| Q_ASSERT(!active || iface->isValid()); |
| |
| QString windowTitle; |
| // in dtor it may be invalid |
| if (iface->isValid()) |
| windowTitle = iface->text(QAccessible::Name); |
| |
| 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); |
| } |
| |
| QAccessibleInterface *AtSpiAdaptor::interfaceFromPath(const QString& dbusPath) const |
| { |
| if (dbusPath == QLatin1String(QSPI_OBJECT_PATH_ROOT)) |
| return QAccessible::queryAccessibleInterface(qApp); |
| |
| QStringList parts = dbusPath.split(QLatin1Char('/')); |
| if (parts.size() != 6) { |
| qCDebug(lcAccessibilityAtspi) << "invalid path: " << dbusPath; |
| return 0; |
| } |
| |
| QString objectString = parts.at(5); |
| QAccessible::Id id = objectString.toUInt(); |
| |
| // The id is always in the range [INT_MAX+1, UINT_MAX] |
| if ((int)id >= 0) |
| qCWarning(lcAccessibilityAtspi) << "No accessible object found for id: " << id; |
| |
| return QAccessible::accessibleInterface(id); |
| } |
| |
| void AtSpiAdaptor::notifyStateChange(QAccessibleInterface *interface, const QString &state, int value) |
| { |
| QString path = pathForInterface(interface); |
| QVariantList stateArgs = packDBusSignalArguments(state, value, 0, variantForPath(path)); |
| sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), |
| QLatin1String("StateChanged"), stateArgs); |
| } |
| |
| |
| /*! |
| This function gets called when Qt notifies about accessibility updates. |
| */ |
| void AtSpiAdaptor::notify(QAccessibleEvent *event) |
| { |
| switch (event->type()) { |
| case QAccessible::ObjectCreated: |
| if (sendObject || sendObject_children_changed) |
| notifyAboutCreation(event->accessibleInterface()); |
| break; |
| case QAccessible::ObjectShow: { |
| if (sendObject || sendObject_state_changed) { |
| notifyStateChange(event->accessibleInterface(), QLatin1String("showing"), 1); |
| } |
| break; |
| } |
| case QAccessible::ObjectHide: { |
| if (sendObject || sendObject_state_changed) { |
| notifyStateChange(event->accessibleInterface(), QLatin1String("showing"), 0); |
| } |
| break; |
| } |
| case QAccessible::ObjectDestroyed: { |
| if (sendObject || sendObject_state_changed) |
| notifyAboutDestruction(event->accessibleInterface()); |
| break; |
| } |
| case QAccessible::ObjectReorder: { |
| if (sendObject || sendObject_children_changed) |
| childrenChanged(event->accessibleInterface()); |
| break; |
| } |
| case QAccessible::NameChanged: { |
| if (sendObject || sendObject_property_change || sendObject_property_change_accessible_name) { |
| QString path = pathForInterface(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(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(event->accessibleInterface()); |
| break; |
| } |
| case QAccessible::TextInserted: |
| case QAccessible::TextRemoved: |
| case QAccessible::TextUpdated: { |
| if (sendObject || sendObject_text_changed) { |
| QAccessibleInterface * iface = event->accessibleInterface(); |
| if (!iface || !iface->textInterface()) { |
| qCDebug(lcAccessibilityAtspi) << "Received text event for invalid interface."; |
| return; |
| } |
| 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::TextUpdated) { |
| 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) { |
| QAccessibleInterface * iface = event->accessibleInterface(); |
| if (!iface || !iface->textInterface()) { |
| qCWarning(lcAccessibilityAtspi) << "Sending TextCaretMoved from object that does not implement text interface: " << iface; |
| return; |
| } |
| |
| 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) { |
| QAccessibleInterface * iface = 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 || sendObject_property_change_accessible_value) { |
| QAccessibleInterface * iface = event->accessibleInterface(); |
| if (!iface) { |
| qCWarning(lcAccessibilityAtspi) << "ValueChanged event from invalid accessible."; |
| return; |
| } |
| if (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); |
| } else if (iface->role() == QAccessible::ComboBox) { |
| // Combo Box with AT-SPI likes to be special |
| // It requires a name-change to update caches and then selection-changed |
| QString path = pathForInterface(iface); |
| QVariantList args1 = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path)); |
| sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), |
| QLatin1String("PropertyChange"), args1); |
| QVariantList args2 = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(0)))); |
| sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), |
| QLatin1String("SelectionChanged"), args2); |
| } else { |
| qCWarning(lcAccessibilityAtspi) << "ValueChanged event and no ValueInterface or ComboBox: " << iface; |
| } |
| } |
| break; |
| } |
| case QAccessible::SelectionAdd: |
| case QAccessible::SelectionRemove: |
| case QAccessible::Selection: { |
| QAccessibleInterface * iface = event->accessibleInterface(); |
| if (!iface) { |
| qCWarning(lcAccessibilityAtspi) << "Selection event from invalid accessible."; |
| return; |
| } |
| 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) { |
| QAccessibleInterface * iface = event->accessibleInterface(); |
| if (!iface) { |
| qCWarning(lcAccessibilityAtspi) << "StateChanged event from invalid accessible."; |
| return; |
| } |
| int checked = iface->state().checked; |
| notifyStateChange(iface, QLatin1String("checked"), checked); |
| } else if (stateChange.active) { |
| QAccessibleInterface * iface = event->accessibleInterface(); |
| if (!iface || !(iface->role() == QAccessible::Window && (sendWindow || sendWindow_activate))) |
| return; |
| int isActive = iface->state().active; |
| QString windowTitle = iface->text(QAccessible::Name); |
| QDBusVariant data; |
| data.setVariant(windowTitle); |
| QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(data)); |
| QString status = isActive ? QLatin1String("Activate") : QLatin1String("Deactivate"); |
| QString path = pathForInterface(iface); |
| sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_WINDOW), status, args); |
| notifyStateChange(iface, QLatin1String("active"), isActive); |
| } else if (stateChange.disabled) { |
| QAccessibleInterface *iface = event->accessibleInterface(); |
| QAccessible::State state = iface->state(); |
| bool enabled = !state.disabled; |
| |
| notifyStateChange(iface, QLatin1String("enabled"), enabled); |
| notifyStateChange(iface, QLatin1String("sensitive"), enabled); |
| } |
| } |
| break; |
| } |
| // For now we ignore these events |
| case QAccessible::TableModelChanged: |
| // For tables, setting manages_descendants should |
| // indicate to the client that it cannot cache these |
| // interfaces. |
| case QAccessible::ParentChanged: |
| case QAccessible::DialogStart: |
| case QAccessible::DialogEnd: |
| case QAccessible::PopupMenuStart: |
| case QAccessible::PopupMenuEnd: |
| case QAccessible::SoundPlayed: |
| case QAccessible::Alert: |
| case QAccessible::ForegroundChanged: |
| case QAccessible::MenuStart: |
| case QAccessible::MenuEnd: |
| case QAccessible::ContextHelpStart: |
| case QAccessible::ContextHelpEnd: |
| case QAccessible::DragDropStart: |
| case QAccessible::DragDropEnd: |
| case QAccessible::ScrollingStart: |
| case QAccessible::ScrollingEnd: |
| case QAccessible::MenuCommand: |
| case QAccessible::ActionChanged: |
| case QAccessible::ActiveDescendantChanged: |
| case QAccessible::AttributeChanged: |
| case QAccessible::DocumentContentChanged: |
| case QAccessible::DocumentLoadComplete: |
| case QAccessible::DocumentLoadStopped: |
| case QAccessible::DocumentReload: |
| case QAccessible::HyperlinkEndIndexChanged: |
| case QAccessible::HyperlinkNumberOfAnchorsChanged: |
| case QAccessible::HyperlinkSelectedLinkChanged: |
| case QAccessible::HypertextLinkActivated: |
| case QAccessible::HypertextLinkSelected: |
| case QAccessible::HyperlinkStartIndexChanged: |
| case QAccessible::HypertextChanged: |
| case QAccessible::HypertextNLinksChanged: |
| case QAccessible::ObjectAttributeChanged: |
| case QAccessible::PageChanged: |
| case QAccessible::SectionChanged: |
| case QAccessible::TableCaptionChanged: |
| case QAccessible::TableColumnDescriptionChanged: |
| case QAccessible::TableColumnHeaderChanged: |
| case QAccessible::TableRowDescriptionChanged: |
| case QAccessible::TableRowHeaderChanged: |
| case QAccessible::TableSummaryChanged: |
| case QAccessible::TextAttributeChanged: |
| case QAccessible::TextColumnChanged: |
| case QAccessible::VisibleDataChanged: |
| case QAccessible::SelectionWithin: |
| case QAccessible::LocationChanged: |
| case QAccessible::HelpChanged: |
| case QAccessible::DefaultActionChanged: |
| case QAccessible::AcceleratorChanged: |
| case QAccessible::InvalidEvent: |
| break; |
| } |
| } |
| |
| void AtSpiAdaptor::sendFocusChanged(QAccessibleInterface *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::childrenChanged(QAccessibleInterface *interface) const |
| { |
| QString parentPath = pathForInterface(interface); |
| int childCount = interface->childCount(); |
| for (int i = 0; i < interface->childCount(); ++i) { |
| QString childPath = pathForInterface(interface->child(i)); |
| QVariantList args = packDBusSignalArguments(QLatin1String("add"), childCount, 0, childPath); |
| sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("ChildrenChanged"), args); |
| } |
| } |
| |
| void AtSpiAdaptor::notifyAboutCreation(QAccessibleInterface *interface) const |
| { |
| // // say hello to d-bus |
| // cache->emitAddAccessible(accessible->getCacheItem()); |
| |
| // notify about the new child of our parent |
| QAccessibleInterface * parent = interface->parent(); |
| if (!parent) { |
| qCDebug(lcAccessibilityAtspi) << "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(QAccessibleInterface *interface) const |
| { |
| if (!interface || !interface->isValid()) |
| return; |
| |
| QAccessibleInterface * parent = interface->parent(); |
| if (!parent) { |
| qCDebug(lcAccessibilityAtspi) << "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); |
| 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 |
| QAccessibleInterface * accessible = interfaceFromPath(message.path()); |
| if (!accessible) { |
| qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << message.path(); |
| return false; |
| } |
| if (!accessible->isValid()) { |
| qCWarning(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Accessible invalid: " << accessible << message.path(); |
| return false; |
| } |
| |
| QString interface = message.interface(); |
| QString function = message.member(); |
| |
| // qCDebug(lcAccessibilityAtspi) << "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); |
| |
| qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::handleMessage with unknown interface: " << message.path() << interface << function; |
| return false; |
| } |
| |
| // Application |
| bool AtSpiAdaptor::applicationInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) |
| { |
| if (message.path() != QLatin1String(ATSPI_DBUS_PATH_ROOT)) { |
| qCDebug(lcAccessibilityAtspi) << "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); |
| } |
| if (function == QLatin1String("GetVersion")) { |
| Q_ASSERT(message.signature() == QLatin1String("ss")); |
| QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(QLatin1String(qVersion())))); |
| return connection.send(reply); |
| } |
| if (function == QLatin1String("GetLocale")) { |
| Q_ASSERT(message.signature() == QLatin1String("u")); |
| QDBusMessage reply = message.createReply(QVariant::fromValue(QLocale().name())); |
| return connection.send(reply); |
| } |
| qCDebug(lcAccessibilityAtspi) << "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 { |
| qCDebug(lcAccessibilityAtspi) << "Error in contacting registry: " |
| << reply.error().name() |
| << reply.error().message(); |
| } |
| delete registry; |
| } |
| |
| // Accessible |
| bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *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; |
| QAccessibleInterface * parent = interface->parent(); |
| if (parent) { |
| childIndex = parent->indexOfChild(interface); |
| if (childIndex < 0) { |
| qCDebug(lcAccessibilityAtspi) << "GetIndexInParent get invalid index: " << childIndex << interface; |
| } |
| } |
| sendReply(connection, message, childIndex); |
| } else if (function == QLatin1String("GetParent")) { |
| QString path; |
| QAccessibleInterface * 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")) { |
| const int index = message.arguments().at(0).toInt(); |
| if (index < 0) { |
| sendReply(connection, message, QVariant::fromValue( |
| QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL)))); |
| } else { |
| QAccessibleInterface * childInterface = 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); |
| } |
| 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; |
| const int numChildren = interface->childCount(); |
| children.reserve(numChildren); |
| for (int i = 0; i < numChildren; ++i) { |
| QString childPath = pathForInterface(interface->child(i)); |
| QSpiObjectReference ref(connection, QDBusObjectPath(childPath)); |
| children << ref; |
| } |
| connection.send(message.createReply(QVariant::fromValue(children))); |
| } else { |
| qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::accessibleInterface does not implement " << function << message.path(); |
| return false; |
| } |
| return true; |
| } |
| |
| AtspiRole AtSpiAdaptor::getRole(QAccessibleInterface *interface) const |
| { |
| if ((interface->role() == QAccessible::EditableText) && interface->state().passwordEdit) |
| return ATSPI_ROLE_PASSWORD_TEXT; |
| return qSpiRoleMapping[interface->role()].spiRole(); |
| } |
| |
| QStringList AtSpiAdaptor::accessibleInterfaces(QAccessibleInterface *interface) const |
| { |
| QStringList ifaces; |
| qCDebug(lcAccessibilityAtspiCreation) << "AtSpiAdaptor::accessibleInterfaces create: " << interface->object(); |
| 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); |
| } else { |
| qCDebug(lcAccessibilityAtspiCreation) << " IS NOT a component"; |
| } |
| if (interface->role() == QAccessible::Application) |
| ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_APPLICATION); |
| |
| if (interface->actionInterface() || interface->valueInterface()) |
| 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(QAccessibleInterface *interface, const QDBusConnection &connection) const |
| { |
| typedef QPair<QAccessibleInterface*, QAccessible::Relation> RelationPair; |
| const QVector<RelationPair> relationInterfaces = interface->relations(); |
| |
| QSpiRelationArray relations; |
| for (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 |
| QSpiObjectReferenceArray related; |
| |
| QDBusObjectPath path = QDBusObjectPath(pathForInterface(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 (inheritsQAction(object)) { |
| qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::pathForObject: warning: creating path with QAction as object."; |
| } |
| |
| QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(object); |
| return pathForInterface(iface); |
| } |
| |
| QString AtSpiAdaptor::pathForInterface(QAccessibleInterface *interface) const |
| { |
| if (!interface || !interface->isValid()) |
| return QLatin1String(ATSPI_DBUS_PATH_NULL); |
| if (interface->role() == QAccessible::Application) |
| return QLatin1String(QSPI_OBJECT_PATH_ROOT); |
| |
| QAccessible::Id id = QAccessible::uniqueId(interface); |
| Q_ASSERT((int)id < 0); |
| return QLatin1String(QSPI_OBJECT_PATH_PREFIX) + QString::number(id); |
| } |
| |
| 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 QAccessibleInterface * getWindow(QAccessibleInterface * interface) |
| { |
| if (interface->role() == QAccessible::Window) |
| return interface; |
| |
| QAccessibleInterface * parent = interface->parent(); |
| while (parent && parent->role() != QAccessible::Window) |
| parent = parent->parent(); |
| |
| return parent; |
| } |
| |
| static QRect getRelativeRect(QAccessibleInterface *interface) |
| { |
| QAccessibleInterface * 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(QAccessibleInterface *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(); |
| if (coordType == ATSPI_COORD_TYPE_WINDOW) { |
| QWindow * window = interface->window(); |
| if (window) { |
| x += window->position().x(); |
| y += window->position().y(); |
| } |
| } |
| |
| QAccessibleInterface * childInterface(interface->childAt(x, y)); |
| QAccessibleInterface * iface = 0; |
| while (childInterface) { |
| iface = childInterface; |
| childInterface = 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")) { |
| QAccessibleActionInterface *actionIface = interface->actionInterface(); |
| if (actionIface && actionIface->actionNames().contains(QAccessibleActionInterface::setFocusAction())) { |
| actionIface->doAction(QAccessibleActionInterface::setFocusAction()); |
| sendReply(connection, message, true); |
| } else { |
| 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(); |
| qCDebug(lcAccessibilityAtspi) << "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(); |
| qCDebug(lcAccessibilityAtspi) << "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(); |
| qCDebug(lcAccessibilityAtspi) << "SetSize is not implemented."; |
| sendReply(connection, message, false); |
| } else { |
| qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::componentInterface does not implement " << function << message.path(); |
| return false; |
| } |
| return true; |
| } |
| |
| QRect AtSpiAdaptor::getExtents(QAccessibleInterface *interface, uint coordType) |
| { |
| return (coordType == ATSPI_COORD_TYPE_SCREEN) ? interface->rect() : getRelativeRect(interface); |
| } |
| |
| // Action interface |
| bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) |
| { |
| if (function == QLatin1String("GetNActions")) { |
| int count = QAccessibleBridgeUtils::effectiveActionNames(interface).count(); |
| sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(count)))); |
| } else if (function == QLatin1String("DoAction")) { |
| int index = message.arguments().at(0).toInt(); |
| const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); |
| if (index < 0 || index >= actionNames.count()) |
| return false; |
| const QString actionName = actionNames.at(index); |
| bool success = QAccessibleBridgeUtils::performEffectiveAction(interface, actionName); |
| sendReply(connection, message, success); |
| } else if (function == QLatin1String("GetActions")) { |
| sendReply(connection, message, QVariant::fromValue(getActions(interface))); |
| } else if (function == QLatin1String("GetName")) { |
| int index = message.arguments().at(0).toInt(); |
| const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); |
| if (index < 0 || index >= actionNames.count()) |
| return false; |
| sendReply(connection, message, actionNames.at(index)); |
| } else if (function == QLatin1String("GetDescription")) { |
| int index = message.arguments().at(0).toInt(); |
| const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); |
| if (index < 0 || index >= actionNames.count()) |
| return false; |
| QString description; |
| if (QAccessibleActionInterface *actionIface = interface->actionInterface()) |
| description = actionIface->localizedActionDescription(actionNames.at(index)); |
| else |
| description = qAccessibleLocalizedActionDescription(actionNames.at(index)); |
| sendReply(connection, message, description); |
| } else if (function == QLatin1String("GetKeyBinding")) { |
| int index = message.arguments().at(0).toInt(); |
| const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); |
| if (index < 0 || index >= actionNames.count()) |
| return false; |
| QStringList keyBindings; |
| if (QAccessibleActionInterface *actionIface = interface->actionInterface()) |
| keyBindings = actionIface->keyBindingsForAction(actionNames.at(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(QLatin1Char(';'))); |
| else |
| sendReply(connection, message, QString()); |
| } else { |
| qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::actionInterface does not implement " << function << message.path(); |
| return false; |
| } |
| return true; |
| } |
| |
| QSpiActionArray AtSpiAdaptor::getActions(QAccessibleInterface *interface) const |
| { |
| QAccessibleActionInterface *actionInterface = interface->actionInterface(); |
| QSpiActionArray actions; |
| const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); |
| actions.reserve(actionNames.size()); |
| for (const QString &actionName : actionNames) { |
| QSpiAction action; |
| |
| action.name = actionName; |
| if (actionInterface) { |
| action.description = actionInterface->localizedActionDescription(actionName); |
| const QStringList keyBindings = actionInterface->keyBindingsForAction(actionName); |
| if (!keyBindings.isEmpty()) |
| action.keyBinding = keyBindings.front(); |
| } else { |
| action.description = qAccessibleLocalizedActionDescription(actionName); |
| } |
| |
| actions.append(std::move(action)); |
| } |
| return actions; |
| } |
| |
| // Text interface |
| bool AtSpiAdaptor::textInterface(QAccessibleInterface *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) |
| qCDebug(lcAccessibilityAtspi) << "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, QAccessible::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")) { |
| qCDebug(lcAccessibilityAtspi) << 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 { |
| qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::textInterface does not implement " << function << message.path(); |
| return false; |
| } |
| return true; |
| } |
| |
| QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryType(int atspiTextBoundaryType) const |
| { |
| switch (atspiTextBoundaryType) { |
| case ATSPI_TEXT_BOUNDARY_CHAR: |
| return QAccessible::CharBoundary; |
| case ATSPI_TEXT_BOUNDARY_WORD_START: |
| case ATSPI_TEXT_BOUNDARY_WORD_END: |
| return QAccessible::WordBoundary; |
| case ATSPI_TEXT_BOUNDARY_SENTENCE_START: |
| case ATSPI_TEXT_BOUNDARY_SENTENCE_END: |
| return QAccessible::SentenceBoundary; |
| case ATSPI_TEXT_BOUNDARY_LINE_START: |
| case ATSPI_TEXT_BOUNDARY_LINE_END: |
| return QAccessible::LineBoundary; |
| } |
| Q_ASSERT_X(0, "", "Requested invalid boundary type."); |
| return QAccessible::CharBoundary; |
| } |
| |
| namespace |
| { |
| struct AtSpiAttribute { |
| QString name; |
| QString value; |
| AtSpiAttribute(const QString &aName, const QString &aValue) : name(aName), value(aValue) {} |
| bool isNull() const { return name.isNull() || value.isNull(); } |
| }; |
| |
| QString atspiColor(const QString &ia2Color) |
| { |
| // "rgb(%u,%u,%u)" -> "%u,%u,%u" |
| return ia2Color.mid(4, ia2Color.length() - (4+1)); |
| } |
| |
| QString atspiSize(const QString &ia2Size) |
| { |
| // "%fpt" -> "%f" |
| return ia2Size.left(ia2Size.length() - 2); |
| } |
| |
| AtSpiAttribute atspiTextAttribute(const QString &ia2Name, const QString &ia2Value) |
| { |
| QString name = ia2Name; |
| QString value = ia2Value; |
| |
| // IAccessible2: http://www.linuxfoundation.org/collaborate/workgroups/accessibility/iaccessible2/textattributes |
| // ATK attribute names: https://git.gnome.org/browse/orca/tree/src/orca/text_attribute_names.py |
| // ATK attribute values: https://developer.gnome.org/atk/unstable/AtkText.html#AtkTextAttribute |
| |
| // https://bugzilla.gnome.org/show_bug.cgi?id=744553 "ATK docs provide no guidance for allowed values of some text attributes" |
| // specifically for "weight", "invalid", "language" and value range for colors |
| |
| if (ia2Name == QLatin1String("background-color")) { |
| name = QStringLiteral("bg-color"); |
| value = atspiColor(value); |
| } else if (ia2Name == QLatin1String("font-family")) { |
| name = QStringLiteral("family-name"); |
| } else if (ia2Name == QLatin1String("color")) { |
| name = QStringLiteral("fg-color"); |
| value = atspiColor(value); |
| } else if (ia2Name == QLatin1String("text-align")) { |
| name = QStringLiteral("justification"); |
| if (value == QLatin1String("justify")) { |
| value = QStringLiteral("fill"); |
| } else { |
| if (value != QLatin1String("left") && |
| value != QLatin1String("right") && |
| value != QLatin1String("center") |
| ) { |
| value = QString(); |
| qCDebug(lcAccessibilityAtspi) << "Unknown text-align attribute value \"" << value << "\" cannot be translated to AT-SPI."; |
| } |
| } |
| } else if (ia2Name == QLatin1String("font-size")) { |
| name = QStringLiteral("size"); |
| value = atspiSize(value); |
| } else if (ia2Name == QLatin1String("font-style")) { |
| name = QStringLiteral("style"); |
| if (value != QLatin1String("normal") && |
| value != QLatin1String("italic") && |
| value != QLatin1String("oblique") |
| ) { |
| value = QString(); |
| qCDebug(lcAccessibilityAtspi) << "Unknown font-style attribute value \"" << value << "\" cannot be translated to AT-SPI."; |
| } |
| } else if (ia2Name == QLatin1String("text-underline-type")) { |
| name = QStringLiteral("underline"); |
| if (value != QLatin1String("none") && |
| value != QLatin1String("single") && |
| value != QLatin1String("double") |
| ) { |
| value = QString(); |
| qCDebug(lcAccessibilityAtspi) << "Unknown text-underline-type attribute value \"" << value << "\" cannot be translated to AT-SPI."; |
| } |
| } else if (ia2Name == QLatin1String("font-weight")) { |
| name = QStringLiteral("weight"); |
| if (value == QLatin1String("normal")) |
| // Orca seems to accept all IAccessible2 values except for "normal" |
| // (on which it produces traceback and fails to read any following text attributes), |
| // but that is the default value, so omit it anyway |
| value = QString(); |
| } else if (ia2Name == QLatin1String("text-position")) { |
| name = QStringLiteral("vertical-align"); |
| if (value != QLatin1String("baseline") && |
| value != QLatin1String("super") && |
| value != QLatin1String("sub") |
| ) { |
| value = QString(); |
| qCDebug(lcAccessibilityAtspi) << "Unknown text-position attribute value \"" << value << "\" cannot be translated to AT-SPI."; |
| } |
| } else if (ia2Name == QLatin1String("writing-mode")) { |
| name = QStringLiteral("direction"); |
| if (value == QLatin1String("lr")) |
| value = QStringLiteral("ltr"); |
| else if (value == QLatin1String("rl")) |
| value = QStringLiteral("rtl"); |
| else if (value == QLatin1String("tb")) { |
| // IAccessible2 docs refer to XSL, which specifies "tb" is shorthand for "tb-rl"; so at least give a hint about the horizontal direction (ATK does not support vertical direction in this attribute (yet)) |
| value = QStringLiteral("rtl"); |
| qCDebug(lcAccessibilityAtspi) << "writing-mode attribute value \"tb\" translated only w.r.t. horizontal direction; vertical direction ignored"; |
| } else { |
| value = QString(); |
| qCDebug(lcAccessibilityAtspi) << "Unknown writing-mode attribute value \"" << value << "\" cannot be translated to AT-SPI."; |
| } |
| } else if (ia2Name == QLatin1String("language")) { |
| // OK - ATK has no docs on the format of the value, IAccessible2 has reasonable format - leave it at that now |
| } else if (ia2Name == QLatin1String("invalid")) { |
| // OK - ATK docs are vague but suggest they support the same range of values as IAccessible2 |
| } else { |
| // attribute we know nothing about |
| name = QString(); |
| value = QString(); |
| } |
| return AtSpiAttribute(name, value); |
| } |
| } |
| |
| // FIXME all attribute methods below should share code |
| QVariantList AtSpiAdaptor::getAttributes(QAccessibleInterface *interface, int offset, bool includeDefaults) const |
| { |
| Q_UNUSED(includeDefaults); |
| |
| QSpiAttributeSet set; |
| int startOffset; |
| int endOffset; |
| |
| QString joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset); |
| const QStringList attributes = joined.split (QLatin1Char(';'), QString::SkipEmptyParts, Qt::CaseSensitive); |
| for (const QString &attr : attributes) { |
| QStringList items; |
| items = attr.split(QLatin1Char(':'), QString::SkipEmptyParts, Qt::CaseSensitive); |
| AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]); |
| if (!attribute.isNull()) |
| set[attribute.name] = attribute.value; |
| } |
| |
| QVariantList list; |
| list << QVariant::fromValue(set) << startOffset << endOffset; |
| |
| return list; |
| } |
| |
| QVariantList AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, int offset, const QString &attributeName) const |
| { |
| QString mapped; |
| QString joined; |
| QSpiAttributeSet map; |
| int startOffset; |
| int endOffset; |
| |
| joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset); |
| const QStringList attributes = joined.split (QLatin1Char(';'), QString::SkipEmptyParts, Qt::CaseSensitive); |
| for (const QString& attr : attributes) { |
| QStringList items; |
| items = attr.split(QLatin1Char(':'), QString::SkipEmptyParts, Qt::CaseSensitive); |
| AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]); |
| if (!attribute.isNull()) |
| map[attribute.name] = attribute.value; |
| } |
| mapped = map[attributeName]; |
| const bool defined = !mapped.isEmpty(); |
| QVariantList list; |
| list << mapped << startOffset << endOffset << defined; |
| return list; |
| } |
| |
| QList<QVariant> AtSpiAdaptor::getCharacterExtents(QAccessibleInterface *interface, int offset, uint coordType) const |
| { |
| QRect rect = interface->textInterface()->characterRect(offset); |
| |
| if (coordType == ATSPI_COORD_TYPE_WINDOW) |
| rect = translateRectToWindowCoordinates(interface, rect); |
| |
| return QList<QVariant>() << rect.x() << rect.y() << rect.width() << rect.height(); |
| } |
| |
| QList<QVariant> AtSpiAdaptor::getRangeExtents(QAccessibleInterface *interface, |
| int startOffset, int endOffset, uint coordType) const |
| { |
| if (endOffset == -1) |
| endOffset = interface->textInterface()->characterCount(); |
| |
| QAccessibleTextInterface *textInterface = interface->textInterface(); |
| if (endOffset <= startOffset || !textInterface) |
| return QList<QVariant>() << -1 << -1 << 0 << 0; |
| |
| 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 QList<QVariant>() << rect.x() << rect.y() << rect.width() << rect.height(); |
| } |
| |
| QRect AtSpiAdaptor::translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect) |
| { |
| QAccessibleInterface * 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(QAccessibleInterface *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, 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, startOffset, endOffset); |
| if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface()) |
| editableTextIface->deleteText(startOffset, endOffset); |
| else |
| replaceTextFallback(interface, 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, 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, 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, position, position, txt); |
| #endif |
| connection.send(message.createReply(true)); |
| } else if (function == QLatin1String("SetTextContents")) { |
| QString newContents = message.arguments().at(0).toString(); |
| if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface()) |
| editableTextIface->replaceText(0, interface->textInterface()->characterCount(), newContents); |
| else |
| replaceTextFallback(interface, 0, -1, newContents); |
| connection.send(message.createReply(true)); |
| } else if (function == QLatin1String("")) { |
| connection.send(message.createReply()); |
| } else { |
| qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::editableTextInterface does not implement " << function << message.path(); |
| return false; |
| } |
| return true; |
| } |
| |
| // Value interface |
| bool AtSpiAdaptor::valueInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) |
| { |
| QAccessibleValueInterface *valueIface = interface->valueInterface(); |
| if (!valueIface) |
| return false; |
| |
| 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 |
| valueIface->setCurrentValue(value); |
| connection.send(message.createReply()); // FIXME is the reply needed? |
| } else { |
| QVariant value; |
| if (function == QLatin1String("GetCurrentValue")) |
| value = valueIface->currentValue(); |
| else if (function == QLatin1String("GetMaximumValue")) |
| value = valueIface->maximumValue(); |
| else if (function == QLatin1String("GetMinimumIncrement")) |
| value = valueIface->minimumStepSize(); |
| else if (function == QLatin1String("GetMinimumValue")) |
| value = valueIface->minimumValue(); |
| else { |
| qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::valueInterface does not implement " << function << message.path(); |
| return false; |
| } |
| if (!value.canConvert(QVariant::Double)) { |
| qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::valueInterface: Could not convert to double: " << function; |
| } |
| |
| // explicitly convert to dbus-variant containing one double since atspi expects that |
| // everything else might fail to convert back on the other end |
| connection.send(message.createReply( |
| QVariant::fromValue(QDBusVariant(QVariant::fromValue(value.toDouble()))))); |
| } |
| return true; |
| } |
| |
| // Table interface |
| bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) |
| { |
| if (!(interface->tableInterface() || interface->tableCellInterface())) { |
| qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find table interface for: " << message.path() << interface; |
| return false; |
| } |
| |
| if (0) { |
| // properties |
| } else if (function == QLatin1String("GetCaption")) { |
| QAccessibleInterface * captionInterface= 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")) { |
| QAccessibleInterface * summary = interface->tableInterface() ? interface->tableInterface()->summary() : 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(); |
| if ((row < 0) || |
| (column < 0) || |
| (row >= interface->tableInterface()->rowCount()) || |
| (column >= interface->tableInterface()->columnCount())) { |
| qCDebug(lcAccessibilityAtspi) << "WARNING: invalid index for tableInterface GetAccessibleAt (" << row << ", " << column << ')'; |
| return false; |
| } |
| |
| QSpiObjectReference ref; |
| QAccessibleInterface * cell(interface->tableInterface()->cellAt(row, column)); |
| if (cell) { |
| ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(cell))); |
| } else { |
| qCDebug(lcAccessibilityAtspi) << "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) { |
| qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::GetIndexAt(" << row << ',' << column << ") did not find a cell. " << interface; |
| return false; |
| } |
| int index = interface->indexOfChild(cell); |
| qCDebug(lcAccessibilityAtspi) << "QSpiAdaptor::GetIndexAt row:" << row << " col:" << column << " logical index:" << index; |
| Q_ASSERT(index > 0); |
| 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) { |
| QAccessibleInterface * cell = 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()) { |
| qCDebug(lcAccessibilityAtspi) << "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()) { |
| qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell; |
| return false; |
| } |
| ret = cell->tableCellInterface()->rowIndex(); |
| } |
| } |
| } else { |
| qCDebug(lcAccessibilityAtspi) << "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 = -1; |
| int col = -1; |
| int rowExtents = -1; |
| int colExtents = -1; |
| bool isSelected = false; |
| |
| int cols = interface->tableInterface()->columnCount(); |
| if (cols > 0) { |
| row = index / cols; |
| col = index % cols; |
| QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, col)->tableCellInterface(); |
| if (cell) { |
| row = cell->rowIndex(); |
| col = cell->columnIndex(); |
| rowExtents = cell->rowExtent(); |
| colExtents = cell->columnExtent(); |
| isSelected = cell->isSelected(); |
| success = true; |
| } |
| } |
| 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; |
| |
| QAccessibleInterface * 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(header.takeAt(0)))); |
| } |
| } |
| 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(); |
| if (header.size() > 0) { |
| ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(header.takeAt(0)))); |
| } |
| } |
| 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())); |
| } 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 { |
| qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::tableInterface does not implement " << function << message.path(); |
| return false; |
| } |
| return true; |
| } |
| |
| QT_END_NAMESPACE |
| #endif //QT_NO_ACCESSIBILITY |