1 /*
2     Copyright 2012 Frederik Gladhorn <gladhorn@kde.org>
3 
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Lesser General Public
6     License as published by the Free Software Foundation; either
7     version 2.1 of the License, or (at your option) version 3, or any
8     later version accepted by the membership of KDE e.V. (or its
9     successor approved by the membership of KDE e.V.), which shall
10     act as a proxy defined in Section 6 of version 3 of the license.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Lesser General Public License for more details.
16 
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "registry_p.h"
22 #include "registry.h"
23 
24 #include <qdbusmessage.h>
25 #include <qdbusargument.h>
26 #include <qdbusreply.h>
27 #include <qdbuspendingcall.h>
28 #include <qdbusinterface.h>
29 #include <qdbusargument.h>
30 #include <qdbusmetatype.h>
31 
32 #include <qdebug.h>
33 #include <qdbusmessage.h>
34 #include <qstringlist.h>
35 #include <qurl.h>
36 
37 #include "atspi/atspi-constants.h"
38 #include "atspi/qt-atspi.h"
39 #include "atspi/dbusconnection.h"
40 
41 #include <qstring.h>
42 #include <qhash.h>
43 
44 // interface names from at-spi2-core/atspi/atspi-misc-private.h
45 #define ATSPI_DBUS_NAME_REGISTRY "org.a11y.atspi.Registry"
46 #define ATSPI_DBUS_PATH_REGISTRY "/org/a11y/atspi/registry"
47 #define ATSPI_DBUS_INTERFACE_REGISTRY "org.a11y.atspi.Registry"
48 
49 #define ATSPI_DBUS_PATH_NULL "/org/a11y/atspi/null"
50 #define ATSPI_DBUS_PATH_ROOT "/org/a11y/atspi/accessible/root"
51 
52 #define ATSPI_DBUS_PATH_DEC "/org/a11y/atspi/registry/deviceeventcontroller"
53 #define ATSPI_DBUS_INTERFACE_DEC "org.a11y.atspi.DeviceEventController"
54 #define ATSPI_DBUS_INTERFACE_DEVICE_EVENT_LISTENER "org.a11y.atspi.DeviceEventListener"
55 
56 #define ATSPI_DBUS_INTERFACE_CACHE "org.a11y.atspi.Cache"
57 #define ATSPI_DBUS_INTERFACE_ACCESSIBLE "org.a11y.atspi.Accessible"
58 #define ATSPI_DBUS_INTERFACE_ACTION "org.a11y.atspi.Action"
59 #define ATSPI_DBUS_INTERFACE_APPLICATION "org.a11y.atspi.Application"
60 #define ATSPI_DBUS_INTERFACE_COLLECTION "org.a11y.atspi.Collection"
61 #define ATSPI_DBUS_INTERFACE_COMPONENT "org.a11y.atspi.Component"
62 #define ATSPI_DBUS_INTERFACE_DOCUMENT "org.a11y.atspi.Document"
63 #define ATSPI_DBUS_INTERFACE_EDITABLE_TEXT "org.a11y.atspi.EditableText"
64 #define ATSPI_DBUS_INTERFACE_EVENT_KEYBOARD "org.a11y.atspi.Event.Keyboard"
65 #define ATSPI_DBUS_INTERFACE_EVENT_MOUSE "org.a11y.atspi.Event.Mouse"
66 #define ATSPI_DBUS_INTERFACE_EVENT_OBJECT "org.a11y.atspi.Event.Object"
67 #define ATSPI_DBUS_INTERFACE_HYPERLINK "org.a11y.atspi.Hyperlink"
68 #define ATSPI_DBUS_INTERFACE_HYPERTEXT "org.a11y.atspi.Hypertext"
69 #define ATSPI_DBUS_INTERFACE_IMAGE "org.a11y.atspi.Image"
70 #define ATSPI_DBUS_INTERFACE_SELECTION "org.a11y.atspi.Selection"
71 #define ATSPI_DBUS_INTERFACE_TABLE "org.a11y.atspi.Table"
72 #define ATSPI_DBUS_INTERFACE_TEXT "org.a11y.atspi.Text"
73 #define ATSPI_DBUS_INTERFACE_VALUE "org.a11y.atspi.Value"
74 #define ATSPI_DBUS_INTERFACE_SOCKET "org.a11y.atspi.Socket"
75 
76 // missing from at-spi2-core:
77 #define ATSPI_DBUS_INTERFACE_EVENT_WINDOW "org.a11y.atspi.Event.Window"
78 #define ATSPI_DBUS_INTERFACE_EVENT_FOCUS  "org.a11y.atspi.Event.Focus"
79 
80 #define QSPI_OBJECT_PATH_ACCESSIBLE  "/org/a11y/atspi/accessible"
81 #define QSPI_OBJECT_PATH_PREFIX      "/org/a11y/atspi/accessible/"
82 #define QSPI_OBJECT_PATH_ROOT    QSPI_OBJECT_PATH_PREFIX "root"
83 
84 #define QSPI_REGISTRY_NAME "org.a11y.atspi.Registry"
85 
86 //#define ATSPI_DEBUG
87 
88 using namespace QAccessibleClient;
89 
90 QString RegistryPrivate::ACCESSIBLE_OBJECT_SCHEME_STRING = QLatin1String("accessibleobject");
91 
RegistryPrivate(Registry * qq)92 RegistryPrivate::RegistryPrivate(Registry *qq)
93     :q(qq)
94     , m_subscriptions(Registry::NoEventListeners)
95     , m_cache(nullptr)
96 {
97     qDBusRegisterMetaType<QVector<quint32> >();
98 
99     connect(&conn, SIGNAL(connectionFetched()), this, SLOT(connectionFetched()));
100     connect(&m_actionMapper, SIGNAL(mapped(QString)), this, SLOT(actionTriggered(QString)));
101     init();
102 }
103 
init()104 void RegistryPrivate::init()
105 {
106     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_CACHE)] = AccessibleObject::CacheInterface;
107     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_ACCESSIBLE)] = AccessibleObject::AccessibleInterface;
108     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_ACTION)] = AccessibleObject::ActionInterface;
109     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_APPLICATION)] = AccessibleObject::ApplicationInterface;
110     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_COLLECTION)] = AccessibleObject::CollectionInterface;
111     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT)] = AccessibleObject::ComponentInterface;
112     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_DOCUMENT)] = AccessibleObject::DocumentInterface;
113     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT)] = AccessibleObject::EditableTextInterface;
114     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_KEYBOARD)] = AccessibleObject::EventKeyboardInterface;
115     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_MOUSE)] = AccessibleObject::EventMouseInterface;
116     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT)] = AccessibleObject::EventObjectInterface;
117     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_HYPERLINK)] = AccessibleObject::HyperlinkInterface;
118     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_HYPERTEXT)] = AccessibleObject::HypertextInterface;
119     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_IMAGE)] = AccessibleObject::ImageInterface;
120     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_SELECTION)] = AccessibleObject::SelectionInterface;
121     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_TABLE)] = AccessibleObject::TableInterface;
122     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_TEXT)] = AccessibleObject::TextInterface;
123     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_VALUE)] = AccessibleObject::ValueInterface;
124     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_SOCKET)] = AccessibleObject::SocketInterface;
125     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_WINDOW)] = AccessibleObject::EventWindowInterface;
126     interfaceHash[QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_FOCUS)] = AccessibleObject::EventFocusInterface;
127 }
128 
isEnabled() const129 bool RegistryPrivate::isEnabled() const
130 {
131     if (conn.status() != DBusConnection::Connected)
132         return false;
133     QDBusMessage message = QDBusMessage::createMethodCall(
134                 QLatin1String("org.a11y.Bus"), QLatin1String("/org/a11y/bus"), QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Get"));
135     message.setArguments(QVariantList() << QLatin1String("org.a11y.Status") << QLatin1String("IsEnabled"));
136     QDBusReply<QVariant> reply  = QDBusConnection::sessionBus().call(message);
137     if (!reply.isValid())
138         return false;
139     return reply.value().toBool();
140 }
141 
setEnabled(bool enable)142 void RegistryPrivate::setEnabled(bool enable)
143 {
144     QDBusMessage message = QDBusMessage::createMethodCall(
145                 QLatin1String("org.a11y.Bus"), QLatin1String("/org/a11y/bus"), QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Set"));
146     message.setArguments(QVariantList() << QLatin1String("org.a11y.Status") << QLatin1String("IsEnabled") << QVariant::fromValue(QDBusVariant(enable)));
147     QDBusMessage reply = QDBusConnection::sessionBus().call(message);
148     if (reply.type() == QDBusMessage::ErrorMessage) {
149         qWarning() << "Could not set org.a11y.Status.isEnabled." << reply.errorName() << reply.errorMessage();
150     }
151 }
152 
isScreenReaderEnabled() const153 bool RegistryPrivate::isScreenReaderEnabled() const
154 {
155     if (conn.status() != DBusConnection::Connected)
156         return false;
157     QDBusMessage message = QDBusMessage::createMethodCall(
158                 QLatin1String("org.a11y.Bus"), QLatin1String("/org/a11y/bus"), QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Get"));
159     message.setArguments(QVariantList() << QLatin1String("org.a11y.Status") << QLatin1String("ScreenReaderEnabled"));
160     QDBusReply<QVariant> reply  = QDBusConnection::sessionBus().call(message);
161     if (!reply.isValid())
162         return false;
163     return reply.value().toBool();
164 }
165 
setScreenReaderEnabled(bool enable)166 void RegistryPrivate::setScreenReaderEnabled(bool enable)
167 {
168     QDBusMessage message = QDBusMessage::createMethodCall(
169                 QLatin1String("org.a11y.Bus"), QLatin1String("/org/a11y/bus"), QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Set"));
170     message.setArguments(QVariantList() << QLatin1String("org.a11y.Status") << QLatin1String("ScreenReaderEnabled") << QVariant::fromValue(QDBusVariant(enable)));
171     QDBusMessage reply = QDBusConnection::sessionBus().call(message);
172     if (reply.type() == QDBusMessage::ErrorMessage) {
173         qWarning() << "Could not set org.a11y.Status.ScreenReaderEnabled." << reply.errorName() << reply.errorMessage();
174     }
175 }
176 
fromUrl(const QUrl & url) const177 AccessibleObject RegistryPrivate::fromUrl(const QUrl &url) const
178 {
179     Q_ASSERT(url.scheme() == ACCESSIBLE_OBJECT_SCHEME_STRING);
180     if (url.scheme() != ACCESSIBLE_OBJECT_SCHEME_STRING)
181         return AccessibleObject();
182     QString path = url.path();
183     QString service = url.fragment();
184     return accessibleFromPath(service, path);
185 }
186 
connectionFetched()187 void RegistryPrivate::connectionFetched()
188 {
189     Q_ASSERT(conn.status() == DBusConnection::Connected);
190 
191     QDBusConnection session = QDBusConnection::sessionBus();
192     if (session.isConnected()) {
193         bool connected = session.connect(QLatin1String("org.a11y.Bus"), QLatin1String("/org/a11y/bus"), QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("PropertiesChanged"), this, SLOT(a11yConnectionChanged(QString,QVariantMap,QStringList)));
194         if (!connected)
195             qWarning() << Q_FUNC_INFO << "Failed to connect with signal org.a11y.Status.PropertiesChanged on org.a11y.Bus";
196     }
197 
198     if (m_pendingSubscriptions > 0) {
199         subscribeEventListeners(m_pendingSubscriptions);
200         m_pendingSubscriptions = nullptr;
201     }
202 }
203 
subscribeEventListeners(const Registry::EventListeners & listeners)204 void RegistryPrivate::subscribeEventListeners(const Registry::EventListeners &listeners)
205 {
206     if (conn.isFetchingConnection()) {
207         m_pendingSubscriptions = listeners;
208         return;
209     }
210 
211     Registry::EventListeners addedListeners = listeners & ~m_subscriptions;
212     Registry::EventListeners removedListeners = m_subscriptions & ~listeners;
213 
214     QStringList newSubscriptions;
215     QStringList removedSubscriptions;
216 
217     if (removedListeners.testFlag(Registry::Window)) {
218         removedSubscriptions << QLatin1String("window:");
219     } else if (addedListeners.testFlag(Registry::Window)) {
220         // subscribe all window events
221         newSubscriptions << QLatin1String("window:");
222 
223         bool created = conn.connection().connect(
224                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Create"),
225                     this, SLOT(slotWindowCreate(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
226         bool destroyed = conn.connection().connect(
227                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Destroy"),
228                     this, SLOT(slotWindowDestroy(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
229 
230         bool closed = conn.connection().connect(
231                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Close"),
232                     this, SLOT(slotWindowClose(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
233         bool reparented = conn.connection().connect(
234                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Reparent"),
235                     this, SLOT(slotWindowReparent(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
236 
237         bool minimized = conn.connection().connect(
238                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Minimize"),
239                     this, SLOT(slotWindowMinimize(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
240         bool maximized = conn.connection().connect(
241                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Maximize"),
242                     this, SLOT(slotWindowMaximize(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
243         bool restored = conn.connection().connect(
244                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Restore"),
245                     this, SLOT(slotWindowRestore(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
246 
247         bool activated = conn.connection().connect(
248                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Activate"),
249                     this, SLOT(slotWindowActivate(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
250         bool deactivated = conn.connection().connect(
251                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Deactivate"),
252                     this, SLOT(slotWindowDeactivate(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
253 
254         bool desktopCreated = conn.connection().connect(
255                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("DesktopCreate"),
256                     this, SLOT(slotWindowDesktopCreate(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
257         bool desktopDestroyed = conn.connection().connect(
258                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("DesktopDestroy"),
259                     this, SLOT(slotWindowDesktopDestroy(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
260         bool raised = conn.connection().connect(
261                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Raise"),
262                     this, SLOT(slotWindowRaise(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
263         bool lowered = conn.connection().connect(
264                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Lower"),
265                     this, SLOT(slotWindowLower(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
266         bool moved = conn.connection().connect(
267                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Move"),
268                     this, SLOT(slotWindowMove(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
269         bool resized = conn.connection().connect(
270                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Resize"),
271                     this, SLOT(slotWindowResize(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
272         bool shaded = conn.connection().connect(
273                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Shade"),
274                     this, SLOT(slotWindowShade(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
275         bool unshaded = conn.connection().connect(
276                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Window"), QLatin1String("Unshade"),
277                     this, SLOT(slotWindowUnshade(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
278 
279         if (!created || !destroyed || !closed || !reparented || !minimized || !maximized || !restored ||
280             !activated || !deactivated || !desktopCreated || !desktopDestroyed ||
281             !raised || !lowered || !moved || !resized || !shaded || !unshaded
282         ) {
283             qWarning() << "Could not subscribe to Window event(s)."
284                        << "created:" << created << "destroyed:" << destroyed
285                        << "closed:" << closed << "reparented:" << reparented
286                        << "minimized:" << minimized << "maximized:" << maximized << "restored:" << restored
287                        << "activated:" << activated << "deactivated:" << deactivated
288                        << "desktopCreated:" << desktopCreated << "desktopDestroyed:" << desktopDestroyed
289                        << "raised:" << raised << "lowered:" << lowered
290                        << "moved:" << moved << "resized:" << resized
291                        << "shaded:" << shaded << "unshaded:" << unshaded
292                        ;
293         }
294     }
295 
296     if (removedListeners.testFlag(Registry::ChildrenChanged)) {
297         removedSubscriptions << QLatin1String("object:children-changed");
298     } else if (addedListeners.testFlag(Registry::ChildrenChanged)) {
299         newSubscriptions << QLatin1String("object:children-changed");
300         bool success = conn.connection().connect(
301                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Object"), QLatin1String("ChildrenChanged"),
302                     this, SLOT(slotChildrenChanged(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
303         if (!success) qWarning() << "Could not subscribe to accessibility ChildrenChanged events.";
304     }
305 
306     if (removedListeners.testFlag(Registry::VisibleDataChanged)) {
307         removedSubscriptions << QLatin1String("object:visibledata-changed");
308     } else if (addedListeners.testFlag(Registry::VisibleDataChanged)) {
309         newSubscriptions << QLatin1String("object:visibledata-changed");
310         bool success = conn.connection().connect(
311                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Object"), QLatin1String("VisibleDataChanged"),
312                     this, SLOT(slotVisibleDataChanged(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
313         if (!success) qWarning() << "Could not subscribe to accessibility VisibleDataChanged events.";
314     }
315 
316     if (removedListeners.testFlag(Registry::SelectionChanged)) {
317         removedSubscriptions << QLatin1String("object:selection-changed");
318     } else if (addedListeners.testFlag(Registry::SelectionChanged)) {
319         newSubscriptions << QLatin1String("object:selection-changed");
320         bool success = conn.connection().connect(
321                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Object"), QLatin1String("SelectionChanged"),
322                     this, SLOT(slotSelectionChanged(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
323         if (!success) qWarning() << "Could not subscribe to accessibility SelectionChanged events.";
324     }
325 
326 
327     if (removedListeners.testFlag(Registry::ModelChanged)) {
328         removedSubscriptions << QLatin1String("object:model-changed");
329     } else if (addedListeners.testFlag(Registry::ModelChanged)) {
330         newSubscriptions << QLatin1String("object:model-changed");
331         bool success = conn.connection().connect(
332                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Object"), QLatin1String("ModelChanged"),
333                     this, SLOT(slotModelChanged(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
334         if (!success) qWarning() << "Could not subscribe to accessibility ModelChanged events.";
335     }
336 
337     // we need state-changed-focus for focus events
338     if ((removedListeners.testFlag(Registry::StateChanged) || removedListeners.testFlag(Registry::Focus))
339             && (!(addedListeners.testFlag(Registry::StateChanged) || addedListeners.testFlag(Registry::Focus)))) {
340         removedSubscriptions << QLatin1String("object:state-changed");
341     } else if (addedListeners.testFlag(Registry::StateChanged) || addedListeners.testFlag(Registry::Focus)) {
342         if (listeners.testFlag(Registry::Focus)) newSubscriptions << QLatin1String("focus:");
343         newSubscriptions << QLatin1String("object:state-changed");
344         bool success = conn.connection().connect(
345                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Object"), QLatin1String("StateChanged"),
346                     this, SLOT(slotStateChanged(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
347         if (!success) qWarning() << "Could not subscribe to accessibility Focus events.";
348     }
349 
350     if (removedListeners.testFlag(Registry::TextChanged)) {
351         removedSubscriptions << QLatin1String("object:text-changed");
352     } else if (addedListeners.testFlag(Registry::TextChanged)) {
353         newSubscriptions << QLatin1String("object:text-changed");
354         bool success = conn.connection().connect(
355                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Object"), QLatin1String("TextChanged"),
356                     this, SLOT(slotTextChanged(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
357         if (!success) qWarning() << "Could not subscribe to accessibility TextChanged events.";
358     }
359 
360     if (removedListeners.testFlag(Registry::TextCaretMoved)) {
361         removedSubscriptions << QLatin1String("object:text-caret-moved");
362     } else if (addedListeners.testFlag(Registry::TextCaretMoved)) {
363         newSubscriptions << QLatin1String("object:text-caret-moved");
364         bool success = conn.connection().connect(
365                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Object"), QLatin1String("TextCaretMoved"),
366                     this, SLOT(slotTextCaretMoved(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
367         if (!success) qWarning() << "Could not subscribe to accessibility TextCaretMoved events.";
368     }
369 
370     if (removedListeners.testFlag(Registry::TextSelectionChanged)) {
371         removedSubscriptions << QLatin1String("object:text-selection-changed");
372     } else if (addedListeners.testFlag(Registry::TextSelectionChanged)) {
373         newSubscriptions << QLatin1String("object:text-selection-changed");
374         bool success = conn.connection().connect(
375                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Object"), QLatin1String("TextSelectionChanged"),
376                     this, SLOT(slotTextSelectionChanged(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
377         if (!success) qWarning() << "Could not subscribe to accessibility TextSelectionChanged events.";
378     }
379 
380     if (removedListeners.testFlag(Registry::PropertyChanged)) {
381         removedSubscriptions << QLatin1String("object:property-change");
382     } else if (addedListeners.testFlag(Registry::PropertyChanged )) {
383         newSubscriptions << QLatin1String("object:property-change");
384         bool success = conn.connection().connect(
385                     QString(), QLatin1String(""), QLatin1String("org.a11y.atspi.Event.Object"), QLatin1String("PropertyChange"),
386                     this, SLOT(slotPropertyChange(QString,int,int,QDBusVariant,QAccessibleClient::QSpiObjectReference)));
387         if (!success) qWarning() << "Could not subscribe to accessibility PropertyChange events.";
388     }
389 
390     Q_FOREACH(const QString &subscription, newSubscriptions) {
391         QDBusMessage m = QDBusMessage::createMethodCall(QLatin1String("org.a11y.atspi.Registry"),
392                                                         QLatin1String("/org/a11y/atspi/registry"),
393                                                         QLatin1String("org.a11y.atspi.Registry"), QLatin1String("RegisterEvent"));
394         m.setArguments(QVariantList() << subscription);
395 
396         QDBusPendingCall async = conn.connection().asyncCall(m);
397         QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this);
398         QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(slotSubscribeEventListenerFinished(QDBusPendingCallWatcher*)));
399     }
400 
401     Q_FOREACH(const QString &subscription, removedSubscriptions) {
402         QDBusMessage m = QDBusMessage::createMethodCall(QLatin1String("org.a11y.atspi.Registry"),
403                                                         QLatin1String("/org/a11y/atspi/registry"),
404                                                         QLatin1String("org.a11y.atspi.Registry"), QLatin1String("DeregisterEvent"));
405         m.setArguments(QVariantList() << subscription);
406         conn.connection().asyncCall(m);
407     }
408 
409     m_subscriptions = listeners;
410 
411 // accerciser
412 //     (u':1.7', u'Object:StateChanged:'),
413 //     (u':1.7', u'Object:BoundsChanged:'),
414 //     (u':1.7', u'Object:VisibleDataChanged:'),
415 //     (u':1.7', u'Object:StateChanged:'),
416 // orca
417 //    [(u':1.8', u'Object:ChildrenChanged:'),
418 //     (u':1.8', u'Mouse:Button:'),
419 //     (u':1.8', u'Mouse:Abs:'),
420 //     (u':1.8', u'Object:StateChanged:Selected'),
421 //     (u':1.8', u'Object:StateChanged:Expanded'),
422 //     (u':1.8', u'Object:ValueChanged:'),
423 //     (u':1.8', u'Object:StateChanged:Focused'),
424 //     (u':1.8', u'Object:StateChanged:Active'),
425 //     (u':1.8', u'Window:Create:'),
426 //     (u':1.8', u'Object:TextAttributesChanged:'),
427 //     (u':1.8', u'Object:TextCaretMoved:'),
428 //     (u':1.8', u'Object:SelectionChanged:'),
429 //     (u':1.8', u'Focus::'),
430 //     (u':1.8', u'Object:ActiveDescendantChanged:'),
431 //     (u':1.8', u'Object:PropertyChange:AccessibleName'),
432 //     (u':1.8', u'Window:Activate:'),
433 //     (u':1.8', u'Window:Deactivate:'),
434 //     (u':1.8', u'Mouse:Button:'),
435 //     (u':1.8', u'Object:StateChanged:Indeterminate'),
436 //     (u':1.8', u'Object:LinkSelected:'),
437 //     (u':1.8', u'Object:TextChanged:Insert'),
438 //     (u':1.8', u'Object:PropertyChange:AccessibleValue'),
439 //     (u':1.8', u'Object:TextSelectionChanged:'),
440 // //     (u':1.8', u'Object:StateChanged:Showing'),
441 //     (u':1.8', u'Object:TextChanged:Delete'),
442 //     (u':1.8', u'Object:StateChanged:Pressed'),
443 //     (u':1.8', u'Object:StateChanged:Checked'),
444 //     (u':1.8', u'Object:ChildrenChanged:Remove')]
445 
446 }
447 
eventListeners() const448 Registry::EventListeners RegistryPrivate::eventListeners() const
449 {
450     return m_subscriptions | m_pendingSubscriptions;
451 }
452 
slotSubscribeEventListenerFinished(QDBusPendingCallWatcher * call)453 void RegistryPrivate::slotSubscribeEventListenerFinished(QDBusPendingCallWatcher *call)
454 {
455     if (call->isError()) {
456         qWarning() << "Could not subscribe to accessibility event: " << call->error().type() << call->error().message();
457     }
458     call->deleteLater();
459 }
460 
a11yConnectionChanged(const QString & interface,const QVariantMap & changedProperties,const QStringList & invalidatedProperties)461 void RegistryPrivate::a11yConnectionChanged(const QString &interface,const QVariantMap &changedProperties, const QStringList &invalidatedProperties)
462 {
463     //qDebug() << Q_FUNC_INFO << "interface=" << interface << "changedProperties=" << changedProperties << "invalidatedProperties=" << invalidatedProperties;
464     if (conn.status() != DBusConnection::Connected)
465         return;
466     if (interface == QLatin1String("org.a11y.Status")) {
467         QVariantMap::ConstIterator IsEnabledIt = changedProperties.constFind(QLatin1String("IsEnabled"));
468         if (IsEnabledIt != changedProperties.constEnd())
469             emit q->enabledChanged(IsEnabledIt.value().toBool());
470         else if (invalidatedProperties.contains(QLatin1String("IsEnabled")))
471             emit q->enabledChanged(isEnabled());
472 
473         QVariantMap::ConstIterator ScreenReaderEnabledIt = changedProperties.constFind(QLatin1String("ScreenReaderEnabled"));
474         if (ScreenReaderEnabledIt != changedProperties.constEnd())
475             emit q->screenReaderEnabledChanged(ScreenReaderEnabledIt.value().toBool());
476         else if (invalidatedProperties.contains(QLatin1String("ScreenReaderEnabled")))
477             emit q->screenReaderEnabledChanged(isScreenReaderEnabled());
478     }
479 }
480 
parentAccessible(const AccessibleObject & object) const481 AccessibleObject RegistryPrivate::parentAccessible(const AccessibleObject &object) const
482 {
483     QVariant parent = getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"), QLatin1String("Parent"));
484     if (!parent.isValid())
485         return AccessibleObject();
486     const QDBusArgument arg = parent.value<QDBusArgument>();
487     QSpiObjectReference ref;
488     arg >> ref;
489 
490     if (ref.path.path() == object.d->path) {
491         qWarning() << "WARNING: Accessible claims to be its own parent: " << object;
492         return AccessibleObject();
493     }
494 
495     if (ref.service.isEmpty() || ref.path.path().isEmpty())
496         return AccessibleObject();
497 
498     return AccessibleObject(const_cast<RegistryPrivate*>(this), ref.service, ref.path.path());
499 }
500 
childCount(const AccessibleObject & object) const501 int RegistryPrivate::childCount(const AccessibleObject &object) const
502 {
503     QVariant childCount = getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"), QLatin1String("ChildCount"));
504     return childCount.toInt();
505 }
506 
indexInParent(const AccessibleObject & object) const507 int RegistryPrivate::indexInParent(const AccessibleObject &object) const
508 {
509     QDBusMessage message = QDBusMessage::createMethodCall (
510                 object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"), QLatin1String("GetIndexInParent"));
511 
512     QDBusReply<int> reply = conn.connection().call(message);
513     if (!reply.isValid()) {
514         QDBusReply<uint> reply2 = conn.connection().call(message);
515         if (reply2.isValid()) {
516             qWarning() << "Found old api returning uint in GetIndexInParent." << reply.error().message();
517             return static_cast<int>(reply.value());
518         }
519         qWarning() << "Could not access index in parent." << reply.error().message();
520         return -1;
521     }
522     return reply.value();
523 }
524 
child(const AccessibleObject & object,int index) const525 AccessibleObject RegistryPrivate::child(const AccessibleObject &object, int index) const
526 {
527     QDBusMessage message = QDBusMessage::createMethodCall (
528                 object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"), QLatin1String("GetChildAtIndex"));
529     QVariantList args;
530     args << index;
531     message.setArguments(args);
532 
533     QDBusReply<QSpiObjectReference> reply = conn.connection().call(message);
534     if (!reply.isValid()) {
535         qWarning() << "Could not access child." << reply.error().message();
536         return AccessibleObject();
537     }
538     const QSpiObjectReference child = reply.value();
539     return AccessibleObject(const_cast<RegistryPrivate*>(this), child.service, child.path.path());
540 }
541 
children(const AccessibleObject & object) const542 QList<AccessibleObject> RegistryPrivate::children(const AccessibleObject &object) const
543 {
544     QList<AccessibleObject> accs;
545 
546     QDBusMessage message = QDBusMessage::createMethodCall (
547                 object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"), QLatin1String("GetChildren"));
548 
549     QDBusReply<QSpiObjectReferenceList> reply = conn.connection().call(message, QDBus::Block, 500);
550     if (!reply.isValid()) {
551         qWarning() << "Could not access children." << reply.error().message();
552         return accs;
553     }
554 
555     const QSpiObjectReferenceList children = reply.value();
556     Q_FOREACH(const QSpiObjectReference &child, children) {
557         accs.append(AccessibleObject(const_cast<RegistryPrivate*>(this), child.service, child.path.path()));
558     }
559 
560     return accs;
561 }
562 
topLevelAccessibles() const563 QList<AccessibleObject> RegistryPrivate::topLevelAccessibles() const
564 {
565     QString service = QLatin1String("org.a11y.atspi.Registry");
566     QString path = QLatin1String("/org/a11y/atspi/accessible/root");
567     return children(AccessibleObject(const_cast<RegistryPrivate*>(this), service, path));
568 }
569 
name(const AccessibleObject & object) const570 QString RegistryPrivate::name(const AccessibleObject &object) const
571 {
572     if (!object.isValid())
573         return QString();
574     return getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"), QLatin1String("Name")).toString();
575 }
576 
description(const AccessibleObject & object) const577 QString RegistryPrivate::description(const AccessibleObject &object) const
578 {
579     if (!object.isValid())
580         return QString();
581     return getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"), QLatin1String("Description")).toString();
582 }
583 
role(const AccessibleObject & object) const584 AccessibleObject::Role RegistryPrivate::role(const AccessibleObject &object) const
585 {
586     if (!object.isValid())
587         return AccessibleObject::NoRole;
588 
589     QDBusMessage message = QDBusMessage::createMethodCall (
590                 object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"), QLatin1String("GetRole"));
591 
592     QDBusReply<uint> reply = conn.connection().call(message);
593     if (!reply.isValid()) {
594         qWarning() << "Could not access role." << reply.error().message();
595         return AccessibleObject::NoRole;
596     }
597     return atspiRoleToRole(static_cast<AtspiRole>(reply.value()));
598 }
599 
atspiRoleToRole(AtspiRole role)600 AccessibleObject::Role RegistryPrivate::atspiRoleToRole(AtspiRole role)
601 {
602     switch (role) {
603     case ATSPI_ROLE_INVALID: return AccessibleObject::NoRole;
604 //    case ATSPI_ROLE_ACCELERATOR_LABEL: return AccessibleObject::;
605 //    case ATSPI_ROLE_ALERT: return AccessibleObject::;
606 //    case ATSPI_ROLE_ANIMATION: return AccessibleObject::;
607 //    case ATSPI_ROLE_ARROW: return AccessibleObject::;
608 //    case ATSPI_ROLE_CALENDAR: return AccessibleObject::;
609 //    case ATSPI_ROLE_CANVAS: return AccessibleObject::;
610     case ATSPI_ROLE_CHECK_BOX: return AccessibleObject::CheckBox;
611     case ATSPI_ROLE_CHECK_MENU_ITEM: return AccessibleObject::CheckableMenuItem;
612 //    case ATSPI_ROLE_COLOR_CHOOSER: return AccessibleObject::;
613     case ATSPI_ROLE_COLUMN_HEADER: return AccessibleObject::ColumnHeader;
614     case ATSPI_ROLE_COMBO_BOX: return AccessibleObject::ComboBox;
615 //    case ATSPI_ROLE_DATE_EDITOR: return AccessibleObject::;
616 //    case ATSPI_ROLE_DESKTOP_ICON: return AccessibleObject::;
617     case ATSPI_ROLE_DESKTOP_FRAME: return AccessibleObject::DesktopFrame;
618 //    case ATSPI_ROLE_DIAL: return AccessibleObject::;
619     case ATSPI_ROLE_DIALOG: return AccessibleObject::Dialog;
620 //    case ATSPI_ROLE_DIRECTORY_PANE: return AccessibleObject::;
621 //    case ATSPI_ROLE_DRAWING_AREA: return AccessibleObject::;
622 //    case ATSPI_ROLE_FILE_CHOOSER: return AccessibleObject::;
623     case ATSPI_ROLE_FILLER: return AccessibleObject::Filler;
624 //    case ATSPI_ROLE_FOCUS_TRAVERSABLE: return AccessibleObject::;
625 //    case ATSPI_ROLE_FONT_CHOOSER: return AccessibleObject::;
626     case ATSPI_ROLE_FRAME: return AccessibleObject::Frame;
627 //    case ATSPI_ROLE_GLASS_PANE: return AccessibleObject::;
628 //    case ATSPI_ROLE_HTML_CONTAINER: return AccessibleObject::;
629     case ATSPI_ROLE_ICON: return AccessibleObject::Icon;
630 //    case ATSPI_ROLE_IMAGE: return AccessibleObject::;
631 //    case ATSPI_ROLE_INTERNAL_FRAME: return AccessibleObject::;
632     case ATSPI_ROLE_LABEL: return AccessibleObject::Label;
633 //    case ATSPI_ROLE_LAYERED_PANE: return AccessibleObject::;
634     case ATSPI_ROLE_LIST: return AccessibleObject::ListView;
635     case ATSPI_ROLE_LIST_ITEM: return AccessibleObject::ListItem;
636     case ATSPI_ROLE_MENU: return AccessibleObject::Menu;
637     case ATSPI_ROLE_MENU_BAR: return AccessibleObject::MenuBar;
638     case ATSPI_ROLE_MENU_ITEM: return AccessibleObject::MenuItem;
639 //    case ATSPI_ROLE_OPTION_PANE: return AccessibleObject::;
640     case ATSPI_ROLE_PAGE_TAB: return AccessibleObject::Tab;
641     case ATSPI_ROLE_PAGE_TAB_LIST: return AccessibleObject::TabContainer;
642 //    case ATSPI_ROLE_PANEL: return AccessibleObject::;
643     case ATSPI_ROLE_PASSWORD_TEXT: return AccessibleObject::PasswordText;
644     case ATSPI_ROLE_POPUP_MENU: return AccessibleObject::PopupMenu;
645     case ATSPI_ROLE_PROGRESS_BAR: return AccessibleObject::ProgressBar;
646     case ATSPI_ROLE_PUSH_BUTTON: return AccessibleObject::Button;
647     case ATSPI_ROLE_RADIO_BUTTON: return AccessibleObject::RadioButton;
648     case ATSPI_ROLE_RADIO_MENU_ITEM: return AccessibleObject::RadioMenuItem;
649 //    case ATSPI_ROLE_ROOT_PANE: return AccessibleObject::;
650     case ATSPI_ROLE_ROW_HEADER: return AccessibleObject::RowHeader;
651     case ATSPI_ROLE_SCROLL_BAR: return AccessibleObject::ScrollBar;
652     case ATSPI_ROLE_SCROLL_PANE: return AccessibleObject::ScrollArea;
653     case ATSPI_ROLE_SEPARATOR: return AccessibleObject::Separator;
654     case ATSPI_ROLE_SLIDER: return AccessibleObject::Slider;
655     case ATSPI_ROLE_SPIN_BUTTON: return AccessibleObject::SpinButton;
656 //    case ATSPI_ROLE_SPLIT_PANE: return AccessibleObject::;
657     case ATSPI_ROLE_STATUS_BAR: return AccessibleObject::StatusBar;
658     case ATSPI_ROLE_TABLE: return AccessibleObject::TableView;
659     case ATSPI_ROLE_TABLE_CELL: return AccessibleObject::TableCell;
660     case ATSPI_ROLE_TABLE_COLUMN_HEADER: return AccessibleObject::TableColumnHeader;
661     case ATSPI_ROLE_TABLE_ROW_HEADER: return AccessibleObject::TableRowHeader;
662 //    case ATSPI_ROLE_TEAROFF_MENU_ITEM: return AccessibleObject::;
663     case ATSPI_ROLE_TERMINAL: return AccessibleObject::Terminal;
664     case ATSPI_ROLE_TEXT: return AccessibleObject::Text;
665     case ATSPI_ROLE_TOGGLE_BUTTON: return AccessibleObject::ToggleButton;
666     case ATSPI_ROLE_TOOL_BAR: return AccessibleObject::ToolBar;
667     case ATSPI_ROLE_TOOL_TIP: return AccessibleObject::ToolTip;
668     case ATSPI_ROLE_TREE: return AccessibleObject::TreeView;
669     case ATSPI_ROLE_TREE_TABLE: return AccessibleObject::TreeView;
670     case ATSPI_ROLE_UNKNOWN: return AccessibleObject::NoRole;
671 //    case ATSPI_ROLE_VIEWPORT: return AccessibleObject::;
672     case ATSPI_ROLE_WINDOW: return AccessibleObject::Window;
673 //    case ATSPI_ROLE_EXTENDED: return AccessibleObject::;
674 //    case ATSPI_ROLE_HEADER: return AccessibleObject::;
675 //    case ATSPI_ROLE_FOOTER: return AccessibleObject::;
676 //    case ATSPI_ROLE_PARAGRAPH: return AccessibleObject::;
677 //    case ATSPI_ROLE_RULER: return AccessibleObject::;
678 //    case ATSPI_ROLE_APPLICATION: return AccessibleObject::;
679 //    case ATSPI_ROLE_AUTOCOMPLETE: return AccessibleObject::;
680 //    case ATSPI_ROLE_EDITBAR: return AccessibleObject::;
681 //    case ATSPI_ROLE_EMBEDDED: return AccessibleObject::;
682 //    case ATSPI_ROLE_ENTRY: return AccessibleObject::;
683 //    case ATSPI_ROLE_CHART: return AccessibleObject::;
684 //    case ATSPI_ROLE_CAPTION: return AccessibleObject::;
685 //    case ATSPI_ROLE_DOCUMENT_FRAME: return AccessibleObject::;
686 //    case ATSPI_ROLE_HEADING: return AccessibleObject::;
687 //    case ATSPI_ROLE_PAGE: return AccessibleObject::;
688 //    case ATSPI_ROLE_SECTION: return AccessibleObject::;
689 //    case ATSPI_ROLE_REDUNDANT_OBJECT: return AccessibleObject::;
690 //    case ATSPI_ROLE_FORM: return AccessibleObject::;
691 //    case ATSPI_ROLE_LINK: return AccessibleObject::;
692 //    case ATSPI_ROLE_INPUT_METHOD_WINDOW: return AccessibleObject::;
693     case ATSPI_ROLE_TABLE_ROW: return AccessibleObject::TableRow;
694     case ATSPI_ROLE_TREE_ITEM: return AccessibleObject::TreeItem;
695 //    case ATSPI_ROLE_DOCUMENT_SPREADSHEET: return AccessibleObject::;
696 //    case ATSPI_ROLE_DOCUMENT_PRESENTATION: return AccessibleObject::;
697 //    case ATSPI_ROLE_DOCUMENT_TEXT: return AccessibleObject::;
698 //    case ATSPI_ROLE_DOCUMENT_WEB: return AccessibleObject::;
699 //    case ATSPI_ROLE_DOCUMENT_EMAIL: return AccessibleObject::;
700 //    case ATSPI_ROLE_COMMENT: return AccessibleObject::;
701 //    case ATSPI_ROLE_LIST_BOX: return AccessibleObject::;
702 //    case ATSPI_ROLE_GROUPING: return AccessibleObject::;
703 //    case ATSPI_ROLE_IMAGE_MAP: return AccessibleObject::;
704 //    case ATSPI_ROLE_NOTIFICATION: return AccessibleObject::;
705 //    case ATSPI_ROLE_INFO_BAR: return AccessibleObject::;
706 //    case ATSPI_ROLE_LAST_DEFINED: return AccessibleObject::;
707     }
708     return AccessibleObject::NoRole;
709 }
710 
roleName(const AccessibleObject & object) const711 QString RegistryPrivate::roleName(const AccessibleObject &object) const
712 {
713     QDBusMessage message = QDBusMessage::createMethodCall (
714                 object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"), QLatin1String("GetRoleName"));
715 
716     QDBusReply<QString> reply = conn.connection().call(message);
717     if (!reply.isValid()) {
718         qWarning() << "Could not access roleName." << reply.error().message();
719         return QString();
720     }
721     return reply.value();
722 }
723 
localizedRoleName(const AccessibleObject & object) const724 QString RegistryPrivate::localizedRoleName(const AccessibleObject &object) const
725 {
726     QDBusMessage message = QDBusMessage::createMethodCall (
727                 object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"), QLatin1String("GetLocalizedRoleName"));
728 
729     QDBusReply<QString> reply = conn.connection().call(message);
730     if (!reply.isValid()) {
731         qWarning() << "Could not access localizedRoleName." << reply.error().message();\
732         return QString();
733     }
734     return reply.value();
735 }
736 
state(const AccessibleObject & object) const737 quint64 RegistryPrivate::state(const AccessibleObject &object) const
738 {
739     if (m_cache) {
740         quint64 cachedValue = m_cache->state(object);
741         if (cachedValue != QAccessibleClient::ObjectCache::StateNotFound)
742             return cachedValue;
743     }
744 
745     QDBusMessage message = QDBusMessage::createMethodCall (
746                 object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"), QLatin1String("GetState"));
747 
748     QDBusReply<QVector<quint32> > reply = conn.connection().call(message);
749     if (!reply.isValid()) {
750         qWarning() << "Could not access state." << reply.error().message();
751         return 0;
752     }
753     if (reply.value().size() < 2) {
754         qWarning() << "Did not receive expected reply.";
755         return 0;
756     }
757     quint32 low = reply.value().at(0);
758     quint32 high = reply.value().at(1);
759     quint64 state = low + (static_cast<quint64>(high) << 32);
760 
761     if (m_cache) {
762         m_cache->setState(object, state);
763     }
764 
765     return state;
766 }
767 
layer(const AccessibleObject & object) const768 int RegistryPrivate::layer(const AccessibleObject &object) const
769 {
770     QDBusMessage message = QDBusMessage::createMethodCall (
771                 object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Component"), QLatin1String("GetLayer"));
772     QDBusReply<uint> reply = conn.connection().call(message);
773     if (!reply.isValid()) {
774         qWarning() << "Could not access layer." << reply.error().message();
775         return 1;
776     }
777     return reply.value();
778 }
779 
mdiZOrder(const AccessibleObject & object) const780 int RegistryPrivate::mdiZOrder(const AccessibleObject &object) const
781 {
782     QDBusMessage message = QDBusMessage::createMethodCall (
783                 object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Component"), QLatin1String("GetMDIZOrder"));
784     QDBusReply<short> reply = conn.connection().call(message);
785     if (!reply.isValid()) {
786         qWarning() << "Could not access mdiZOrder." << reply.error().message();
787         return 0;
788     }
789     return reply.value();
790 }
791 
alpha(const AccessibleObject & object) const792 double RegistryPrivate::alpha(const AccessibleObject &object) const
793 {
794     QDBusMessage message = QDBusMessage::createMethodCall (
795                 object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Component"), QLatin1String("GetAlpha"));
796     QDBusReply<double> reply = conn.connection().call(message);
797     if (!reply.isValid()) {
798         qWarning() << "Could not access alpha." << reply.error().message();
799         return 1.0;
800     }
801     return reply.value();
802 }
803 
boundingRect(const AccessibleObject & object) const804 QRect RegistryPrivate::boundingRect(const AccessibleObject &object) const
805 {
806     QDBusMessage message = QDBusMessage::createMethodCall(
807             object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Component"), QLatin1String("GetExtents") );
808     QVariantList args;
809     quint32 coords = ATSPI_COORD_TYPE_SCREEN;
810     args << coords;
811     message.setArguments(args);
812 
813     QDBusReply< QRect > reply = conn.connection().call(message);
814     if(!reply.isValid()){
815         qWarning() << "Could not get extents." << reply.error().message();
816         return QRect();
817     }
818 
819     return QRect( reply.value() );
820 }
821 
characterRect(const AccessibleObject & object,int offset) const822 QRect RegistryPrivate::characterRect(const AccessibleObject &object, int offset) const
823 {
824     QDBusMessage message = QDBusMessage::createMethodCall(
825             object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Text"),
826                     QLatin1String("GetCharacterExtents"));
827 
828     QVariantList args;
829     quint32 coords = ATSPI_COORD_TYPE_SCREEN;
830     args << offset;
831     args << coords;
832     message.setArguments(args);
833 
834 
835     QDBusReply< QRect > reply = conn.connection().call(message);
836     if(!reply.isValid()){
837         if (reply.error().type() == QDBusError::InvalidSignature) {
838             QDBusMessage reply2 = conn.connection().call(message);
839             if (reply2.signature() != QLatin1String("iiii")) {
840                 qWarning() << "Could not get Character Extents. " << reply.error().message();
841                 return QRect();
842             }
843             QList<QVariant> args = reply2.arguments();
844             QRect rect(args.at(0).toInt(), args.at(1).toInt(), args.at(2).toInt(), args.at(3).toInt());
845             return rect;
846         }
847     }
848 
849     return reply.value();
850 }
851 
supportedInterfaces(const AccessibleObject & object) const852 AccessibleObject::Interfaces RegistryPrivate::supportedInterfaces(const AccessibleObject &object) const
853 {
854     if (m_cache) {
855         AccessibleObject::Interfaces interfaces = m_cache->interfaces(object);
856         if (!(interfaces & AccessibleObject::InvalidInterface))
857             return interfaces;
858     }
859 
860     QDBusMessage message = QDBusMessage::createMethodCall(
861             object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"),
862                     QLatin1String("GetInterfaces"));
863 
864     QDBusReply<QStringList > reply = conn.connection().call(message);
865     if(!reply.isValid()){
866         qWarning() << "Could not get Interfaces. " << reply.error().message();
867         return AccessibleObject::NoInterface;
868     }
869 
870     AccessibleObject::Interfaces interfaces = AccessibleObject::NoInterface;
871     Q_FOREACH(const QString &interface, reply.value()){
872         interfaces |= interfaceHash[interface];
873     }
874 
875     if (m_cache) {
876         m_cache->setInterfaces(object, interfaces);
877     }
878 
879     return interfaces;
880 }
881 
caretOffset(const AccessibleObject & object) const882 int RegistryPrivate::caretOffset(const AccessibleObject &object) const
883 {
884     QVariant offset= getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Text"), QLatin1String("CaretOffset"));
885     if (offset.isNull()) qWarning() << "Could not get caret offset";
886     return offset.toInt();
887 }
888 
characterCount(const AccessibleObject & object) const889 int RegistryPrivate::characterCount(const AccessibleObject &object) const
890 {
891     QVariant count = getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Text"), QLatin1String("CharacterCount"));
892     if (count.isNull()) qWarning() << "Could not get character count";
893     return count.toInt();
894 }
895 
textSelections(const AccessibleObject & object) const896 QList< QPair<int,int> > RegistryPrivate::textSelections(const AccessibleObject &object) const
897 {
898     QList< QPair<int,int> > result;
899     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Text"), QLatin1String("GetNSelections"));
900     QDBusReply<int> reply = conn.connection().call(message);
901     if (!reply.isValid()) {
902         qWarning() << "Could not access GetNSelections." << reply.error().message();
903         return result;
904     }
905     int count = reply.value();
906     for(int i = 0; i < count; ++i) {
907         QDBusMessage m = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Text"), QLatin1String("GetSelection"));
908         m.setArguments(QVariantList() << i);
909         m = conn.connection().call(m);
910         QList<QVariant> args = m.arguments();
911         if (args.count() < 2) {
912             qWarning() << "Invalid number of arguments. Expected=2 Actual=" << args.count();
913             continue;
914         }
915         int startOffset = args[0].toInt();
916         int endOffset = args[1].toInt();
917         if (startOffset > endOffset)
918             qSwap(startOffset, endOffset);
919         result.append(qMakePair(startOffset, endOffset));
920     }
921     return result;
922 }
923 
setTextSelections(const AccessibleObject & object,const QList<QPair<int,int>> & selections)924 void RegistryPrivate::setTextSelections(const AccessibleObject &object, const QList< QPair<int,int> > &selections)
925 {
926     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Text"), QLatin1String("GetNSelections"));
927     QDBusReply<int> reply = conn.connection().call(message);
928     if (!reply.isValid()) {
929         qWarning() << "Could not access GetNSelections." << reply.error().message();
930         return;
931     }
932     int count = reply.value();
933     int setSel = qMin(selections.count(), count);
934     for(int i = 0; i < setSel; ++i) {
935         Q_ASSERT(i < selections.count());
936         QPair<int,int> p = selections[i];
937         QDBusMessage m = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Text"), QLatin1String("SetSelection"));
938         m.setArguments(QVariantList() << i << p.first << p.second);
939         QDBusReply<bool> r = conn.connection().call(m);
940         if (!r.isValid()) {
941             qWarning() << "Failed call text.SetSelection." << r.error().message();
942             continue;
943         }
944     }
945     int removeSel = qMax(0, count - selections.count());
946     for(int i = 0, k = selections.count(); i < removeSel; ++i, ++k) {
947         QDBusMessage m = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Text"), QLatin1String("RemoveSelection"));
948         m.setArguments(QVariantList() << k);
949         QDBusReply<bool> r = conn.connection().call(m);
950         if (!r.isValid()) {
951             qWarning() << "Failed call text.RemoveSelection." << r.error().message();
952             continue;
953         }
954     }
955     int addSel = qMax(0, selections.count() - count);
956     for(int i = 0, k = count; i < addSel; ++i, ++k) {
957         Q_ASSERT(k < selections.count());
958         QPair<int,int> p = selections[k];
959         QDBusMessage m = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Text"), QLatin1String("AddSelection"));
960         m.setArguments(QVariantList() << p.first << p.second);
961         QDBusReply<bool> r = conn.connection().call(m);
962         if (!r.isValid()) {
963             qWarning() << "Failed call text.AddSelection." << r.error().message();
964             continue;
965         }
966     }
967 }
968 
text(const AccessibleObject & object,int startOffset,int endOffset) const969 QString RegistryPrivate::text(const AccessibleObject &object, int startOffset, int endOffset) const
970 {
971     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Text"), QLatin1String("GetText"));
972     message.setArguments(QVariantList() << startOffset << endOffset);
973     QDBusReply<QString> reply = conn.connection().call(message);
974     if (!reply.isValid()) {
975         qWarning() << "Could not access text." << reply.error().message();
976         return QString();
977     }
978     return reply.value();
979 }
980 
textWithBoundary(const AccessibleObject & object,int offset,AccessibleObject::TextBoundary boundary,int * startOffset,int * endOffset) const981 QString RegistryPrivate::textWithBoundary(const AccessibleObject &object, int offset, AccessibleObject::TextBoundary boundary, int *startOffset, int *endOffset) const
982 {
983     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Text"), QLatin1String("GetTextAtOffset"));
984     message.setArguments(QVariantList() << offset << static_cast<AtspiTextBoundaryType>(boundary));
985     QDBusMessage reply = conn.connection().call(message);
986     if (reply.type() != QDBusMessage::ReplyMessage || reply.signature() != QStringLiteral("sii")) {
987         qWarning() << "Could not access text." << reply.errorMessage();
988         if (startOffset)
989             *startOffset = 0;
990         if (endOffset)
991             *endOffset = 0;
992         return QString();
993     }
994     if (startOffset)
995         *startOffset = reply.arguments().at(1).toInt();
996     if (endOffset)
997         *endOffset = reply.arguments().at(2).toInt();
998     return reply.arguments().first().toString();;
999 }
1000 
setText(const AccessibleObject & object,const QString & text)1001 bool RegistryPrivate::setText(const AccessibleObject &object, const QString &text)
1002 {
1003     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.EditableText"), QLatin1String("SetTextContents"));
1004     message.setArguments(QVariantList() << text);
1005     QDBusReply<bool> reply = conn.connection().call(message);
1006     if (!reply.isValid()) {
1007         qWarning() << "Could not set text." << reply.error().message();
1008         return false;
1009     }
1010     return reply.value();
1011 }
1012 
insertText(const AccessibleObject & object,const QString & text,int position,int length)1013 bool RegistryPrivate::insertText(const AccessibleObject &object, const QString &text, int position, int length)
1014 {
1015     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.EditableText"), QLatin1String("InsertText"));
1016     message.setArguments(QVariantList() << position << text << length);
1017     QDBusReply<bool> reply = conn.connection().call(message);
1018     if (!reply.isValid()) {
1019         qWarning() << "Could not insert text." << reply.error().message();
1020         return false;
1021     }
1022     return reply.value();
1023 }
1024 
copyText(const AccessibleObject & object,int startPos,int endPos)1025 bool RegistryPrivate::copyText(const AccessibleObject &object, int startPos, int endPos)
1026 {
1027     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.EditableText"), QLatin1String("CopyText"));
1028     message.setArguments(QVariantList() << startPos << endPos);
1029     conn.connection().call(message);
1030     return true;
1031 }
1032 
cutText(const AccessibleObject & object,int startPos,int endPos)1033 bool RegistryPrivate::cutText(const AccessibleObject &object, int startPos, int endPos)
1034 {
1035     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.EditableText"), QLatin1String("CutText"));
1036     message.setArguments(QVariantList() << startPos << endPos);
1037     QDBusReply<bool> reply = conn.connection().call(message);
1038     if (!reply.isValid()) {
1039         qWarning() << "Could not cut text." << reply.error().message();
1040         return false;
1041     }
1042     return reply.value();
1043 }
1044 
deleteText(const AccessibleObject & object,int startPos,int endPos)1045 bool RegistryPrivate::deleteText(const AccessibleObject &object, int startPos, int endPos)
1046 {
1047     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.EditableText"), QLatin1String("DeleteText"));
1048     message.setArguments(QVariantList() << startPos << endPos);
1049     QDBusReply<bool> reply = conn.connection().call(message);
1050     if (!reply.isValid()) {
1051         qWarning() << "Could not delete text." << reply.error().message();
1052         return false;
1053     }
1054     return reply.value();
1055 }
1056 
pasteText(const AccessibleObject & object,int position)1057 bool RegistryPrivate::pasteText(const AccessibleObject &object, int position)
1058 {
1059     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.EditableText"), QLatin1String("PasteText"));
1060     message.setArguments(QVariantList() << position);
1061     QDBusReply<bool> reply = conn.connection().call(message);
1062     if (!reply.isValid()) {
1063         qWarning() << "Could not paste text." << reply.error().message();
1064         return false;
1065     }
1066     return reply.value();
1067 }
1068 
application(const AccessibleObject & object) const1069 AccessibleObject RegistryPrivate::application(const AccessibleObject &object) const
1070 {
1071     QDBusMessage message = QDBusMessage::createMethodCall(
1072             object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Accessible"), QLatin1String("GetApplication"));
1073     QDBusReply<QSpiObjectReference> reply = conn.connection().call(message);
1074     if (!reply.isValid()) {
1075         qWarning() << "Could not access application." << reply.error().message();
1076         return AccessibleObject();
1077     }
1078     const QSpiObjectReference child = reply.value();
1079     return AccessibleObject(const_cast<RegistryPrivate*>(this), child.service, child.path.path());
1080 }
1081 
appToolkitName(const AccessibleObject & object) const1082 QString RegistryPrivate::appToolkitName(const AccessibleObject &object) const
1083 {
1084     QVariant v = getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Application"), QLatin1String("ToolkitName"));
1085     return v.toString();
1086 }
1087 
appVersion(const AccessibleObject & object) const1088 QString RegistryPrivate::appVersion(const AccessibleObject &object) const
1089 {
1090     QVariant v = getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Application"), QLatin1String("Version"));
1091     return v.toString();
1092 }
1093 
appId(const AccessibleObject & object) const1094 int RegistryPrivate::appId(const AccessibleObject &object) const
1095 {
1096     QVariant v = getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Application"), QLatin1String("Id"));
1097     return v.toInt();
1098 }
1099 
appLocale(const AccessibleObject & object,uint lctype) const1100 QString RegistryPrivate::appLocale(const AccessibleObject &object, uint lctype) const
1101 {
1102     // some apps misbehave and claim to be the service, but on :1.0 we have the atspi service which doesn't reply anything sensible here
1103     if (object.d->service == QLatin1String(":1.0"))
1104         return QString();
1105 
1106     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Application"), QLatin1String("GetLocale"));
1107 
1108     QVariantList args;
1109     args.append(lctype);
1110     message.setArguments(args);
1111 
1112     QDBusReply<QString> reply = conn.connection().call(message, QDBus::Block, 500);
1113     if (!reply.isValid()) {
1114         qWarning() << "Could not access appLocale." << reply.error().message();
1115         return QString();
1116     }
1117     return reply.value();
1118 }
1119 
appBusAddress(const AccessibleObject & object) const1120 QString RegistryPrivate::appBusAddress(const AccessibleObject &object) const
1121 {
1122     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Application"), QLatin1String("GetApplicationBusAddress"));
1123     QDBusReply<QString> reply = conn.connection().call(message);
1124     if (!reply.isValid()) {
1125         qWarning() << Q_FUNC_INFO << "Could not access application bus address. Error: " << reply.error().message() << " in response to: " << message;
1126         return QString();
1127     }
1128     return reply.value();
1129 }
1130 
minimumValue(const AccessibleObject & object) const1131 double RegistryPrivate::minimumValue(const AccessibleObject &object) const
1132 {
1133     QVariant v = getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Value"), QLatin1String("MinimumValue"));
1134     return v.toDouble();
1135 }
1136 
maximumValue(const AccessibleObject & object) const1137 double RegistryPrivate::maximumValue(const AccessibleObject &object) const
1138 {
1139     QVariant v = getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Value"), QLatin1String("MaximumValue"));
1140     return v.toDouble();
1141 }
1142 
minimumValueIncrement(const AccessibleObject & object) const1143 double RegistryPrivate::minimumValueIncrement(const AccessibleObject &object) const
1144 {
1145     QVariant v = getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Value"), QLatin1String("MinimumIncrement"));
1146     return v.toDouble();
1147 }
1148 
currentValue(const AccessibleObject & object) const1149 double RegistryPrivate::currentValue(const AccessibleObject &object) const
1150 {
1151     QVariant v = getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Value"), QLatin1String("CurrentValue"));
1152     return v.toDouble();
1153 }
1154 
setCurrentValue(const AccessibleObject & object,double value)1155 bool RegistryPrivate::setCurrentValue(const AccessibleObject &object, double value)
1156 {
1157     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Value"), QLatin1String("SetCurrentValue"));
1158 
1159     QVariantList arguments;
1160     arguments << QLatin1String("org.a11y.atspi.Value") <<  QLatin1String("CurrentValue");
1161     arguments << QVariant::fromValue(QDBusVariant(value));
1162     message.setArguments(arguments);
1163 
1164     QDBusReply<bool> reply = conn.connection().call(message);
1165     if (!reply.isValid()) {
1166         qWarning() << "Could not set text." << reply.error().message();
1167         return false;
1168     }
1169     return reply.value();
1170 }
1171 
selection(const AccessibleObject & object) const1172 QList<AccessibleObject> RegistryPrivate::selection(const AccessibleObject &object) const
1173 {
1174     QList<AccessibleObject> result;
1175     int count = getProperty(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Selection"), QLatin1String("CurrentValue")).toInt();
1176     for(int i = 0; i < count; ++i) {
1177         QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Selection"), QLatin1String("GetSelectedChild"));
1178         QDBusReply<QSpiObjectReference> reply = conn.connection().call(message);
1179         if (!reply.isValid()) {
1180             qWarning() << "Could not access selection." << reply.error().message();
1181             return QList<AccessibleObject>();
1182         }
1183         const QSpiObjectReference ref = reply.value();
1184         result.append(AccessibleObject(const_cast<RegistryPrivate*>(this), ref.service, ref.path.path()));
1185     }
1186     return result;
1187 }
1188 
imageDescription(const AccessibleObject & object) const1189 QString RegistryPrivate::imageDescription(const AccessibleObject &object) const
1190 {
1191     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Image"), QLatin1String("ImageDescription"));
1192     QDBusReply<QString> reply = conn.connection().call(message);
1193     if (!reply.isValid()) {
1194         qWarning() << "Could not access imageDescription." << reply.error().message();
1195         return QString();
1196     }
1197     return reply.value();
1198 }
1199 
imageLocale(const AccessibleObject & object) const1200 QString RegistryPrivate::imageLocale(const AccessibleObject &object) const
1201 {
1202     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Image"), QLatin1String("ImageLocale"));
1203     QDBusReply<QString> reply = conn.connection().call(message, QDBus::Block, 500);
1204     if (!reply.isValid()) {
1205         qWarning() << "Could not access imageLocale." << reply.error().message();
1206         return QString();
1207     }
1208     return reply.value();
1209 }
1210 
imageRect(const AccessibleObject & object) const1211 QRect RegistryPrivate::imageRect(const AccessibleObject &object) const
1212 {
1213     QDBusMessage message = QDBusMessage::createMethodCall(object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Image"), QLatin1String("GetImageExtents"));
1214     QVariantList args;
1215     quint32 coords = ATSPI_COORD_TYPE_SCREEN;
1216     args << coords;
1217     message.setArguments(args);
1218     QDBusReply<QRect> reply = conn.connection().call(message);
1219     if (!reply.isValid()) {
1220         qWarning() << "Could not access imageRect." << reply.error().message();
1221         return QRect();
1222     }
1223     return QRect( reply.value() );
1224 }
1225 
actions(const AccessibleObject & object)1226 QVector< QSharedPointer<QAction> > RegistryPrivate::actions(const AccessibleObject &object)
1227 {
1228     QDBusMessage message = QDBusMessage::createMethodCall (
1229                 object.d->service, object.d->path, QLatin1String("org.a11y.atspi.Action"), QLatin1String("GetActions"));
1230 
1231     QDBusReply<QSpiActionArray> reply = conn.connection().call(message, QDBus::Block, 500);
1232     if (!reply.isValid()) {
1233         qWarning() << "Could not access actions." << reply.error().message();
1234         return QVector< QSharedPointer<QAction> >();
1235     }
1236 
1237     QSpiActionArray actionArray = reply.value();
1238     QVector< QSharedPointer<QAction> > list;
1239     for(int i = 0; i < actionArray.count(); ++i) {
1240         const QSpiAction &a = actionArray[i];
1241         QAction *action = new QAction();
1242         QString id = QString(QLatin1String("%1;%2;%3")).arg(object.d->service).arg(object.d->path).arg(i);
1243         action->setObjectName(id);
1244         action->setText(a.name);
1245         action->setWhatsThis(a.description);
1246         QKeySequence shortcut(a.keyBinding);
1247         action->setShortcut(shortcut);
1248         m_actionMapper.setMapping(action, id);
1249         connect(action, SIGNAL(triggered()), &m_actionMapper, SLOT(map()));
1250         list.append(QSharedPointer<QAction>(action));
1251     }
1252     return list;
1253 }
1254 
actionTriggered(const QString & action)1255 void RegistryPrivate::actionTriggered(const QString &action)
1256 {
1257     QStringList actionParts = action.split(QLatin1Char(';'));
1258     Q_ASSERT(actionParts.count() == 3);
1259     QString service = actionParts[0];
1260     QString path = actionParts[1];
1261     int index = actionParts[2].toInt();
1262 
1263     QDBusMessage message = QDBusMessage::createMethodCall (
1264                 service, path, QLatin1String("org.a11y.atspi.Action"), QLatin1String("DoAction"));
1265 
1266     QVariantList args;
1267     args << index;
1268     message.setArguments(args);
1269 
1270     QDBusReply<bool> reply = conn.connection().call(message, QDBus::Block, 500);
1271     if (!reply.isValid()) {
1272         qWarning() << "Could not execute action=" << action << reply.error().message();
1273         return;
1274     }
1275 
1276     if (reply.value()) {
1277         qDebug() << "Successful executed action=" << action;
1278     } else {
1279         qWarning() << "Failed to execute action=" << action;
1280     }
1281 }
1282 
getProperty(const QString & service,const QString & path,const QString & interface,const QString & name) const1283 QVariant RegistryPrivate::getProperty(const QString &service, const QString &path, const QString &interface, const QString &name) const
1284 {
1285     QVariantList args;
1286     args.append(interface);
1287     args.append(name);
1288 
1289     QDBusMessage message = QDBusMessage::createMethodCall (
1290                 service, path, QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Get"));
1291 
1292     message.setArguments(args);
1293     QDBusMessage reply = conn.connection().call(message, QDBus::Block, 500);
1294     if (reply.arguments().isEmpty())
1295         return QVariant();
1296 
1297     QDBusVariant v = reply.arguments().at(0).value<QDBusVariant>();
1298     return v.variant();
1299 }
1300 
accessibleFromPath(const QString & service,const QString & path) const1301 AccessibleObject RegistryPrivate::accessibleFromPath(const QString &service, const QString &path) const
1302 {
1303     return AccessibleObject(const_cast<RegistryPrivate*>(this), service, path);
1304 }
1305 
accessibleFromReference(const QSpiObjectReference & reference) const1306 AccessibleObject RegistryPrivate::accessibleFromReference(const QSpiObjectReference &reference) const
1307 {
1308     return accessibleFromPath(reference.service, reference.path.path());
1309 }
1310 
accessibleFromContext() const1311 AccessibleObject RegistryPrivate::accessibleFromContext() const
1312 {
1313     return accessibleFromPath(QDBusContext::message().service(), QDBusContext::message().path());
1314 }
1315 
slotWindowCreate(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference &)1316 void RegistryPrivate::slotWindowCreate(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &)
1317 {
1318     emit q->windowCreated(accessibleFromContext());
1319 }
1320 
slotWindowDestroy(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1321 void RegistryPrivate::slotWindowDestroy(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1322 {
1323     emit q->windowDestroyed(accessibleFromContext());
1324 }
1325 
slotWindowClose(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1326 void RegistryPrivate::slotWindowClose(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1327 {
1328     emit q->windowClosed(accessibleFromContext());
1329 }
1330 
slotWindowReparent(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1331 void RegistryPrivate::slotWindowReparent(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1332 {
1333     emit q->windowReparented(accessibleFromContext());
1334 }
1335 
slotWindowMinimize(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1336 void RegistryPrivate::slotWindowMinimize(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1337 {
1338     emit q->windowMinimized(accessibleFromContext());
1339 }
1340 
slotWindowMaximize(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1341 void RegistryPrivate::slotWindowMaximize(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1342 {
1343     emit q->windowMaximized(accessibleFromContext());
1344 }
1345 
slotWindowRestore(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1346 void RegistryPrivate::slotWindowRestore(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1347 {
1348     emit q->windowRestored(accessibleFromContext());
1349 }
1350 
slotWindowActivate(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1351 void RegistryPrivate::slotWindowActivate(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1352 {
1353     emit q->windowActivated(accessibleFromContext());
1354 }
1355 
slotWindowDeactivate(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1356 void RegistryPrivate::slotWindowDeactivate(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1357 {
1358     emit q->windowDeactivated(accessibleFromContext());
1359 }
1360 
slotWindowDesktopCreate(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1361 void RegistryPrivate::slotWindowDesktopCreate(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1362 {
1363     emit q->windowDesktopCreated(accessibleFromContext());
1364 }
1365 
slotWindowDesktopDestroy(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1366 void RegistryPrivate::slotWindowDesktopDestroy(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1367 {
1368     emit q->windowDesktopDestroyed(accessibleFromContext());
1369 }
1370 
slotWindowRaise(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1371 void RegistryPrivate::slotWindowRaise(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1372 {
1373     emit q->windowRaised(accessibleFromContext());
1374 }
1375 
slotWindowLower(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1376 void RegistryPrivate::slotWindowLower(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1377 {
1378     emit q->windowLowered(accessibleFromContext());
1379 }
1380 
slotWindowMove(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1381 void RegistryPrivate::slotWindowMove(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1382 {
1383     emit q->windowMoved(accessibleFromContext());
1384 }
1385 
slotWindowResize(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1386 void RegistryPrivate::slotWindowResize(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1387 {
1388     emit q->windowResized(accessibleFromContext());
1389 }
1390 
slotWindowShade(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1391 void RegistryPrivate::slotWindowShade(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1392 {
1393     emit q->windowShaded(accessibleFromContext());
1394 }
1395 
slotWindowUnshade(const QString & state,int detail1,int detail2,const QDBusVariant &,const QAccessibleClient::QSpiObjectReference & reference)1396 void RegistryPrivate::slotWindowUnshade(const QString &state, int detail1, int detail2, const QDBusVariant &/*args*/, const QAccessibleClient::QSpiObjectReference &reference)
1397 {
1398     emit q->windowUnshaded(accessibleFromContext());
1399 }
1400 
slotPropertyChange(const QString & property,int detail1,int detail2,const QDBusVariant & args,const QSpiObjectReference & reference)1401 void RegistryPrivate::slotPropertyChange(const QString &property, int detail1, int detail2, const QDBusVariant &args, const QSpiObjectReference &reference)
1402 {
1403 #ifdef ATSPI_DEBUG
1404     qDebug() << Q_FUNC_INFO << property << detail1 << detail2 << args.variant() << reference.path.path();
1405 #endif
1406     if (property == QLatin1String("accessible-name")) {
1407         emit q->accessibleNameChanged(accessibleFromContext());
1408     } else if (property == QLatin1String("accessible-description")) {
1409         emit q->accessibleDescriptionChanged(accessibleFromContext());
1410     }
1411 }
1412 
slotStateChanged(const QString & state,int detail1,int detail2,const QDBusVariant & object,const QSpiObjectReference & reference)1413 void RegistryPrivate::slotStateChanged(const QString &state, int detail1, int detail2, const QDBusVariant &object, const QSpiObjectReference &reference)
1414 {
1415     //qDebug() << Q_FUNC_INFO << state << detail1 << detail2 << reference.service << reference.path.path() << QDBusContext::message();
1416 
1417     if (state == QLatin1String("defunct") && (detail1 == 1)) {
1418         QSpiObjectReference removed;
1419         removed.service = QDBusContext::message().service();
1420         removed.path = QDBusObjectPath(QDBusContext::message().path());
1421         removeAccessibleObject(removed);
1422         return;
1423     }
1424 
1425     AccessibleObject accessible = accessibleFromContext();
1426     if (m_cache) {
1427         m_cache->cleanState(accessible);
1428     }
1429 
1430     if (state == QLatin1String("focused") && (detail1 == 1) &&
1431             (q->subscribedEventListeners().testFlag(Registry::Focus))) {
1432         emit q->focusChanged(accessible);
1433     }
1434 
1435     if (q->subscribedEventListeners().testFlag(Registry::StateChanged)) {
1436         emit q->stateChanged(accessible, state, detail1 == 1);
1437     }
1438 }
1439 
1440 // void RegistryPrivate::slotLinkSelected(const QString &/*state*/, int /*detail1*/, int /*detail2*/, const QDBusVariant &args, const QAccessibleClient::QSpiObjectReference &reference)
1441 // {
1442 //     emit q->linkSelected(accessibleFromContext());
1443 // }
1444 
removeAccessibleObject(const QAccessibleClient::AccessibleObject & accessible)1445 bool RegistryPrivate::removeAccessibleObject(const QAccessibleClient::AccessibleObject &accessible)
1446 {
1447     Q_ASSERT(accessible.isValid());
1448     if (m_cache) {
1449         const QString id = accessible.id();
1450         if (m_cache->remove(id)) {
1451             emit q->removed(accessible);
1452         }
1453     } else {
1454         emit q->removed(accessible);
1455     }
1456     if (accessible.d)
1457         accessible.d->setDefunct();
1458     return true;
1459 }
1460 
removeAccessibleObject(const QAccessibleClient::QSpiObjectReference & reference)1461 bool RegistryPrivate::removeAccessibleObject(const QAccessibleClient::QSpiObjectReference &reference)
1462 {
1463     QAccessibleClient::AccessibleObject acc = accessibleFromReference(reference);
1464     if (acc.isValid()) {
1465         if (removeAccessibleObject(acc))
1466             return true;
1467     }
1468     return false;
1469 }
1470 
slotChildrenChanged(const QString & state,int detail1,int detail2,const QDBusVariant & args,const QAccessibleClient::QSpiObjectReference & reference)1471 void RegistryPrivate::slotChildrenChanged(const QString &state, int detail1, int detail2, const QDBusVariant &args, const QAccessibleClient::QSpiObjectReference &reference)
1472 {
1473 //    qDebug() << Q_FUNC_INFO << state << detail1 << detail2 << args.variant() << reference.path.path();
1474     QAccessibleClient::AccessibleObject parentAccessible = accessibleFromContext();
1475     if (!parentAccessible.isValid()) {
1476         qWarning() << Q_FUNC_INFO << "Children change with invalid parent." << reference.path.path();
1477         return;
1478     }
1479 
1480     int index = detail1;
1481     if (state == QLatin1String("add")) {
1482         emit q->childAdded(parentAccessible, index);
1483     } else if (state == QLatin1String("remove")) {
1484         emit q->childRemoved(parentAccessible, index);
1485     } else {
1486         qWarning() << "Invalid state in ChildrenChanged." << state;
1487     }
1488 }
1489 
slotVisibleDataChanged(const QString &,int,int,const QDBusVariant & args,const QAccessibleClient::QSpiObjectReference & reference)1490 void RegistryPrivate::slotVisibleDataChanged(const QString &/*state*/, int /*detail1*/, int /*detail2*/, const QDBusVariant &args, const QAccessibleClient::QSpiObjectReference &reference)
1491 {
1492     emit q->visibleDataChanged(accessibleFromContext());
1493 }
1494 
slotSelectionChanged(const QString &,int,int,const QDBusVariant & args,const QAccessibleClient::QSpiObjectReference & reference)1495 void RegistryPrivate::slotSelectionChanged(const QString &/*state*/, int /*detail1*/, int /*detail2*/, const QDBusVariant &args, const QAccessibleClient::QSpiObjectReference &reference)
1496 {
1497     emit q->selectionChanged(accessibleFromContext());
1498 }
1499 
slotModelChanged(const QString &,int,int,const QDBusVariant & args,const QAccessibleClient::QSpiObjectReference & reference)1500 void RegistryPrivate::slotModelChanged(const QString &/*state*/, int /*detail1*/, int /*detail2*/, const QDBusVariant &args, const QAccessibleClient::QSpiObjectReference &reference)
1501 {
1502     emit q->modelChanged(accessibleFromContext());
1503 }
1504 
slotTextCaretMoved(const QString &,int detail1,int,const QDBusVariant &,const QSpiObjectReference & reference)1505 void RegistryPrivate::slotTextCaretMoved(const QString &/*state*/, int detail1, int /*detail2*/, const QDBusVariant &/*args*/, const QSpiObjectReference &reference)
1506 {
1507     emit q->textCaretMoved(accessibleFromContext(), detail1);
1508 }
1509 
slotTextSelectionChanged(const QString &,int,int,const QDBusVariant &,const QSpiObjectReference & reference)1510 void RegistryPrivate::slotTextSelectionChanged(const QString &/*state*/, int /*detail1*/, int /*detail2*/, const QDBusVariant &/*args*/, const QSpiObjectReference &reference)
1511 {
1512     emit q->textSelectionChanged(accessibleFromContext());
1513 }
1514 
slotTextChanged(const QString & change,int start,int end,const QDBusVariant & textVariant,const QSpiObjectReference & reference)1515 void RegistryPrivate::slotTextChanged(const QString &change, int start, int end, const QDBusVariant &textVariant, const QSpiObjectReference &reference)
1516 {
1517     AccessibleObject object(accessibleFromContext());
1518     QString text = textVariant.variant().toString();
1519 
1520     if (change == QLatin1String("insert")) {
1521         emit q->textInserted(object, text, start, end);
1522     } else if (change == QLatin1String("remove")) {
1523         emit q->textRemoved(object, text, start, end);
1524     } else {
1525         emit q->textChanged(object, text, start, end);
1526     }
1527 }
1528 
1529 #include "moc_registry_p.cpp"
1530