1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "atspiadaptor_p.h"
41
42 #include <QtGui/qwindow.h>
43 #include <QtGui/qguiapplication.h>
44 #include <qdbusmessage.h>
45 #include <qdbusreply.h>
46 #include <qclipboard.h>
47
48 #include <QtCore/qloggingcategory.h>
49
50 #ifndef QT_NO_ACCESSIBILITY
51 #include "socket_interface.h"
52 #include "constant_mappings_p.h"
53 #include <QtAccessibilitySupport/private/qaccessiblebridgeutils_p.h>
54
55 #include "application_p.h"
56 /*!
57 \class AtSpiAdaptor
58 \internal
59
60 \brief AtSpiAdaptor is the main class to forward between QAccessibleInterface and AT-SPI DBus
61
62 AtSpiAdaptor implements the functions specified in all at-spi interfaces.
63 It sends notifications coming from Qt via dbus and listens to incoming dbus requests.
64 */
65
66 QT_BEGIN_NAMESPACE
67
68 Q_LOGGING_CATEGORY(lcAccessibilityAtspi, "qt.accessibility.atspi")
69 Q_LOGGING_CATEGORY(lcAccessibilityAtspiCreation, "qt.accessibility.atspi.creation")
70
AtSpiAdaptor(DBusConnection * connection,QObject * parent)71 AtSpiAdaptor::AtSpiAdaptor(DBusConnection *connection, QObject *parent)
72 : QDBusVirtualObject(parent), m_dbus(connection)
73 , sendFocus(0)
74 , sendObject(0)
75 , sendObject_active_descendant_changed(0)
76 , sendObject_attributes_changed(0)
77 , sendObject_bounds_changed(0)
78 , sendObject_children_changed(0)
79 // , sendObject_children_changed_add(0)
80 // , sendObject_children_changed_remove(0)
81 , sendObject_column_deleted(0)
82 , sendObject_column_inserted(0)
83 , sendObject_column_reordered(0)
84 , sendObject_link_selected(0)
85 , sendObject_model_changed(0)
86 , sendObject_property_change(0)
87 , sendObject_property_change_accessible_description(0)
88 , sendObject_property_change_accessible_name(0)
89 , sendObject_property_change_accessible_parent(0)
90 , sendObject_property_change_accessible_role(0)
91 , sendObject_property_change_accessible_table_caption(0)
92 , sendObject_property_change_accessible_table_column_description(0)
93 , sendObject_property_change_accessible_table_column_header(0)
94 , sendObject_property_change_accessible_table_row_description(0)
95 , sendObject_property_change_accessible_table_row_header(0)
96 , sendObject_property_change_accessible_table_summary(0)
97 , sendObject_property_change_accessible_value(0)
98 , sendObject_row_deleted(0)
99 , sendObject_row_inserted(0)
100 , sendObject_row_reordered(0)
101 , sendObject_selection_changed(0)
102 , sendObject_state_changed(0)
103 , sendObject_text_attributes_changed(0)
104 , sendObject_text_bounds_changed(0)
105 , sendObject_text_caret_moved(0)
106 , sendObject_text_changed(0)
107 // , sendObject_text_changed_delete(0)
108 // , sendObject_text_changed_insert(0)
109 , sendObject_text_selection_changed(0)
110 , sendObject_value_changed(0)
111 , sendObject_visible_data_changed(0)
112 , sendWindow(0)
113 , sendWindow_activate(0)
114 , sendWindow_close(0)
115 , sendWindow_create(0)
116 , sendWindow_deactivate(0)
117 // , sendWindow_desktop_create(0)
118 // , sendWindow_desktop_destroy(0)
119 , sendWindow_lower(0)
120 , sendWindow_maximize(0)
121 , sendWindow_minimize(0)
122 , sendWindow_move(0)
123 , sendWindow_raise(0)
124 , sendWindow_reparent(0)
125 , sendWindow_resize(0)
126 , sendWindow_restore(0)
127 , sendWindow_restyle(0)
128 , sendWindow_shade(0)
129 , sendWindow_unshade(0)
130 {
131 m_applicationAdaptor = new QSpiApplicationAdaptor(m_dbus->connection(), this);
132 connect(m_applicationAdaptor, SIGNAL(windowActivated(QObject*,bool)), this, SLOT(windowActivated(QObject*,bool)));
133
134 updateEventListeners();
135 bool success = m_dbus->connection().connect(QLatin1String("org.a11y.atspi.Registry"), QLatin1String("/org/a11y/atspi/registry"),
136 QLatin1String("org.a11y.atspi.Registry"), QLatin1String("EventListenerRegistered"), this,
137 SLOT(eventListenerRegistered(QString,QString)));
138 success = success && m_dbus->connection().connect(QLatin1String("org.a11y.atspi.Registry"), QLatin1String("/org/a11y/atspi/registry"),
139 QLatin1String("org.a11y.atspi.Registry"), QLatin1String("EventListenerDeregistered"), this,
140 SLOT(eventListenerDeregistered(QString,QString)));
141 }
142
~AtSpiAdaptor()143 AtSpiAdaptor::~AtSpiAdaptor()
144 {
145 }
146
147 /*!
148 Provide DBus introspection.
149 */
introspect(const QString & path) const150 QString AtSpiAdaptor::introspect(const QString &path) const
151 {
152 static const QLatin1String accessibleIntrospection(
153 " <interface name=\"org.a11y.atspi.Accessible\">\n"
154 " <property access=\"read\" type=\"s\" name=\"Name\"/>\n"
155 " <property access=\"read\" type=\"s\" name=\"Description\"/>\n"
156 " <property access=\"read\" type=\"(so)\" name=\"Parent\">\n"
157 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName\"/>\n"
158 " </property>\n"
159 " <property access=\"read\" type=\"i\" name=\"ChildCount\"/>\n"
160 " <method name=\"GetChildAtIndex\">\n"
161 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
162 " <arg direction=\"out\" type=\"(so)\"/>\n"
163 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
164 " </method>\n"
165 " <method name=\"GetChildren\">\n"
166 " <arg direction=\"out\" type=\"a(so)\"/>\n"
167 " <annotation value=\"QSpiObjectReferenceArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
168 " </method>\n"
169 " <method name=\"GetIndexInParent\">\n"
170 " <arg direction=\"out\" type=\"i\"/>\n"
171 " </method>\n"
172 " <method name=\"GetRelationSet\">\n"
173 " <arg direction=\"out\" type=\"a(ua(so))\"/>\n"
174 " <annotation value=\"QSpiRelationArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
175 " </method>\n"
176 " <method name=\"GetRole\">\n"
177 " <arg direction=\"out\" type=\"u\"/>\n"
178 " </method>\n"
179 " <method name=\"GetRoleName\">\n"
180 " <arg direction=\"out\" type=\"s\"/>\n"
181 " </method>\n"
182 " <method name=\"GetLocalizedRoleName\">\n"
183 " <arg direction=\"out\" type=\"s\"/>\n"
184 " </method>\n"
185 " <method name=\"GetState\">\n"
186 " <arg direction=\"out\" type=\"au\"/>\n"
187 " <annotation value=\"QSpiUIntList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
188 " </method>\n"
189 " <method name=\"GetAttributes\">\n"
190 " <arg direction=\"out\" type=\"a{ss}\"/>\n"
191 " <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
192 " </method>\n"
193 " <method name=\"GetApplication\">\n"
194 " <arg direction=\"out\" type=\"(so)\"/>\n"
195 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
196 " </method>\n"
197 " </interface>\n"
198 );
199
200 static const QLatin1String actionIntrospection(
201 " <interface name=\"org.a11y.atspi.Action\">\n"
202 " <property access=\"read\" type=\"i\" name=\"NActions\"/>\n"
203 " <method name=\"GetDescription\">\n"
204 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
205 " <arg direction=\"out\" type=\"s\"/>\n"
206 " </method>\n"
207 " <method name=\"GetName\">\n"
208 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
209 " <arg direction=\"out\" type=\"s\"/>\n"
210 " </method>\n"
211 " <method name=\"GetKeyBinding\">\n"
212 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
213 " <arg direction=\"out\" type=\"s\"/>\n"
214 " </method>\n"
215 " <method name=\"GetActions\">\n"
216 " <arg direction=\"out\" type=\"a(sss)\" name=\"index\"/>\n"
217 " <annotation value=\"QSpiActionArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
218 " </method>\n"
219 " <method name=\"DoAction\">\n"
220 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
221 " <arg direction=\"out\" type=\"b\"/>\n"
222 " </method>\n"
223 " </interface>\n"
224 );
225
226 static const QLatin1String applicationIntrospection(
227 " <interface name=\"org.a11y.atspi.Application\">\n"
228 " <property access=\"read\" type=\"s\" name=\"ToolkitName\"/>\n"
229 " <property access=\"read\" type=\"s\" name=\"Version\"/>\n"
230 " <property access=\"readwrite\" type=\"i\" name=\"Id\"/>\n"
231 " <method name=\"GetLocale\">\n"
232 " <arg direction=\"in\" type=\"u\" name=\"lctype\"/>\n"
233 " <arg direction=\"out\" type=\"s\"/>\n"
234 " </method>\n"
235 " <method name=\"GetApplicationBusAddress\">\n"
236 " <arg direction=\"out\" type=\"s\" name=\"address\"/>\n"
237 " </method>\n"
238 " </interface>\n"
239 );
240
241 static const QLatin1String componentIntrospection(
242 " <interface name=\"org.a11y.atspi.Component\">\n"
243 " <method name=\"Contains\">\n"
244 " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
245 " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
246 " <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
247 " <arg direction=\"out\" type=\"b\"/>\n"
248 " </method>\n"
249 " <method name=\"GetAccessibleAtPoint\">\n"
250 " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
251 " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
252 " <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
253 " <arg direction=\"out\" type=\"(so)\"/>\n"
254 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
255 " </method>\n"
256 " <method name=\"GetExtents\">\n"
257 " <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
258 " <arg direction=\"out\" type=\"(iiii)\"/>\n"
259 " <annotation value=\"QSpiRect\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
260 " </method>\n"
261 " <method name=\"GetPosition\">\n"
262 " <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
263 " <arg direction=\"out\" type=\"i\" name=\"x\"/>\n"
264 " <arg direction=\"out\" type=\"i\" name=\"y\"/>\n"
265 " </method>\n"
266 " <method name=\"GetSize\">\n"
267 " <arg direction=\"out\" type=\"i\" name=\"width\"/>\n"
268 " <arg direction=\"out\" type=\"i\" name=\"height\"/>\n"
269 " </method>\n"
270 " <method name=\"GetLayer\">\n"
271 " <arg direction=\"out\" type=\"u\"/>\n"
272 " </method>\n"
273 " <method name=\"GetMDIZOrder\">\n"
274 " <arg direction=\"out\" type=\"n\"/>\n"
275 " </method>\n"
276 " <method name=\"GrabFocus\">\n"
277 " <arg direction=\"out\" type=\"b\"/>\n"
278 " </method>\n"
279 " <method name=\"GetAlpha\">\n"
280 " <arg direction=\"out\" type=\"d\"/>\n"
281 " </method>\n"
282 " <method name=\"SetExtents\">\n"
283 " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
284 " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
285 " <arg direction=\"in\" type=\"i\" name=\"width\"/>\n"
286 " <arg direction=\"in\" type=\"i\" name=\"height\"/>\n"
287 " <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
288 " <arg direction=\"out\" type=\"b\"/>\n"
289 " </method>\n"
290 " <method name=\"SetPosition\">\n"
291 " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
292 " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
293 " <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
294 " <arg direction=\"out\" type=\"b\"/>\n"
295 " </method>\n"
296 " <method name=\"SetSize\">\n"
297 " <arg direction=\"in\" type=\"i\" name=\"width\"/>\n"
298 " <arg direction=\"in\" type=\"i\" name=\"height\"/>\n"
299 " <arg direction=\"out\" type=\"b\"/>\n"
300 " </method>\n"
301 " </interface>\n"
302 );
303
304 static const QLatin1String editableTextIntrospection(
305 " <interface name=\"org.a11y.atspi.EditableText\">\n"
306 " <method name=\"SetTextContents\">\n"
307 " <arg direction=\"in\" type=\"s\" name=\"newContents\"/>\n"
308 " <arg direction=\"out\" type=\"b\"/>\n"
309 " </method>\n"
310 " <method name=\"InsertText\">\n"
311 " <arg direction=\"in\" type=\"i\" name=\"position\"/>\n"
312 " <arg direction=\"in\" type=\"s\" name=\"text\"/>\n"
313 " <arg direction=\"in\" type=\"i\" name=\"length\"/>\n"
314 " <arg direction=\"out\" type=\"b\"/>\n"
315 " </method>\n"
316 " <method name=\"CopyText\">\n"
317 " <arg direction=\"in\" type=\"i\" name=\"startPos\"/>\n"
318 " <arg direction=\"in\" type=\"i\" name=\"endPos\"/>\n"
319 " </method>\n"
320 " <method name=\"CutText\">\n"
321 " <arg direction=\"in\" type=\"i\" name=\"startPos\"/>\n"
322 " <arg direction=\"in\" type=\"i\" name=\"endPos\"/>\n"
323 " <arg direction=\"out\" type=\"b\"/>\n"
324 " </method>\n"
325 " <method name=\"DeleteText\">\n"
326 " <arg direction=\"in\" type=\"i\" name=\"startPos\"/>\n"
327 " <arg direction=\"in\" type=\"i\" name=\"endPos\"/>\n"
328 " <arg direction=\"out\" type=\"b\"/>\n"
329 " </method>\n"
330 " <method name=\"PasteText\">\n"
331 " <arg direction=\"in\" type=\"i\" name=\"position\"/>\n"
332 " <arg direction=\"out\" type=\"b\"/>\n"
333 " </method>\n"
334 " </interface>\n"
335 );
336
337 static const QLatin1String tableIntrospection(
338 " <interface name=\"org.a11y.atspi.Table\">\n"
339 " <property access=\"read\" type=\"i\" name=\"NRows\"/>\n"
340 " <property access=\"read\" type=\"i\" name=\"NColumns\"/>\n"
341 " <property access=\"read\" type=\"(so)\" name=\"Caption\">\n"
342 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName\"/>\n"
343 " </property>\n"
344 " <property access=\"read\" type=\"(so)\" name=\"Summary\">\n"
345 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName\"/>\n"
346 " </property>\n"
347 " <property access=\"read\" type=\"i\" name=\"NSelectedRows\"/>\n"
348 " <property access=\"read\" type=\"i\" name=\"NSelectedColumns\"/>\n"
349 " <method name=\"GetAccessibleAt\">\n"
350 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
351 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
352 " <arg direction=\"out\" type=\"(so)\"/>\n"
353 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
354 " </method>\n"
355 " <method name=\"GetIndexAt\">\n"
356 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
357 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
358 " <arg direction=\"out\" type=\"i\"/>\n"
359 " </method>\n"
360 " <method name=\"GetRowAtIndex\">\n"
361 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
362 " <arg direction=\"out\" type=\"i\"/>\n"
363 " </method>\n"
364 " <method name=\"GetColumnAtIndex\">\n"
365 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
366 " <arg direction=\"out\" type=\"i\"/>\n"
367 " </method>\n"
368 " <method name=\"GetRowDescription\">\n"
369 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
370 " <arg direction=\"out\" type=\"s\"/>\n"
371 " </method>\n"
372 " <method name=\"GetColumnDescription\">\n"
373 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
374 " <arg direction=\"out\" type=\"s\"/>\n"
375 " </method>\n"
376 " <method name=\"GetRowExtentAt\">\n"
377 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
378 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
379 " <arg direction=\"out\" type=\"i\"/>\n"
380 " </method>\n"
381 " <method name=\"GetColumnExtentAt\">\n"
382 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
383 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
384 " <arg direction=\"out\" type=\"i\"/>\n"
385 " </method>\n"
386 " <method name=\"GetRowHeader\">\n"
387 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
388 " <arg direction=\"out\" type=\"(so)\"/>\n"
389 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
390 " </method>\n"
391 " <method name=\"GetColumnHeader\">\n"
392 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
393 " <arg direction=\"out\" type=\"(so)\"/>\n"
394 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
395 " </method>\n"
396 " <method name=\"GetSelectedRows\">\n"
397 " <arg direction=\"out\" type=\"ai\"/>\n"
398 " <annotation value=\"QSpiIntList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
399 " </method>\n"
400 " <method name=\"GetSelectedColumns\">\n"
401 " <arg direction=\"out\" type=\"ai\"/>\n"
402 " <annotation value=\"QSpiIntList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
403 " </method>\n"
404 " <method name=\"IsRowSelected\">\n"
405 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
406 " <arg direction=\"out\" type=\"b\"/>\n"
407 " </method>\n"
408 " <method name=\"IsColumnSelected\">\n"
409 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
410 " <arg direction=\"out\" type=\"b\"/>\n"
411 " </method>\n"
412 " <method name=\"IsSelected\">\n"
413 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
414 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
415 " <arg direction=\"out\" type=\"b\"/>\n"
416 " </method>\n"
417 " <method name=\"AddRowSelection\">\n"
418 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
419 " <arg direction=\"out\" type=\"b\"/>\n"
420 " </method>\n"
421 " <method name=\"AddColumnSelection\">\n"
422 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
423 " <arg direction=\"out\" type=\"b\"/>\n"
424 " </method>\n"
425 " <method name=\"RemoveRowSelection\">\n"
426 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
427 " <arg direction=\"out\" type=\"b\"/>\n"
428 " </method>\n"
429 " <method name=\"RemoveColumnSelection\">\n"
430 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
431 " <arg direction=\"out\" type=\"b\"/>\n"
432 " </method>\n"
433 " <method name=\"GetRowColumnExtentsAtIndex\">\n"
434 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
435 " <arg direction=\"out\" type=\"b\"/>\n"
436 " <arg direction=\"out\" type=\"i\" name=\"row\"/>\n"
437 " <arg direction=\"out\" type=\"i\" name=\"col\"/>\n"
438 " <arg direction=\"out\" type=\"i\" name=\"row_extents\"/>\n"
439 " <arg direction=\"out\" type=\"i\" name=\"col_extents\"/>\n"
440 " <arg direction=\"out\" type=\"b\" name=\"is_selected\"/>\n"
441 " </method>\n"
442 " </interface>\n"
443 );
444
445 static const QLatin1String textIntrospection(
446 " <interface name=\"org.a11y.atspi.Text\">\n"
447 " <property access=\"read\" type=\"i\" name=\"CharacterCount\"/>\n"
448 " <property access=\"read\" type=\"i\" name=\"CaretOffset\"/>\n"
449 " <method name=\"GetText\">\n"
450 " <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
451 " <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
452 " <arg direction=\"out\" type=\"s\"/>\n"
453 " </method>\n"
454 " <method name=\"SetCaretOffset\">\n"
455 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
456 " <arg direction=\"out\" type=\"b\"/>\n"
457 " </method>\n"
458 " <method name=\"GetTextBeforeOffset\">\n"
459 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
460 " <arg direction=\"in\" type=\"u\" name=\"type\"/>\n"
461 " <arg direction=\"out\" type=\"s\"/>\n"
462 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
463 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
464 " </method>\n"
465 " <method name=\"GetTextAtOffset\">\n"
466 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
467 " <arg direction=\"in\" type=\"u\" name=\"type\"/>\n"
468 " <arg direction=\"out\" type=\"s\"/>\n"
469 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
470 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
471 " </method>\n"
472 " <method name=\"GetTextAfterOffset\">\n"
473 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
474 " <arg direction=\"in\" type=\"u\" name=\"type\"/>\n"
475 " <arg direction=\"out\" type=\"s\"/>\n"
476 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
477 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
478 " </method>\n"
479 " <method name=\"GetCharacterAtOffset\">\n"
480 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
481 " <arg direction=\"out\" type=\"i\"/>\n"
482 " </method>\n"
483 " <method name=\"GetAttributeValue\">\n"
484 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
485 " <arg direction=\"in\" type=\"s\" name=\"attributeName\"/>\n"
486 " <arg direction=\"out\" type=\"s\"/>\n"
487 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
488 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
489 " <arg direction=\"out\" type=\"b\" name=\"defined\"/>\n"
490 " </method>\n"
491 " <method name=\"GetAttributes\">\n"
492 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
493 " <arg direction=\"out\" type=\"a{ss}\"/>\n"
494 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
495 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
496 " <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
497 " </method>\n"
498 " <method name=\"GetDefaultAttributes\">\n"
499 " <arg direction=\"out\" type=\"a{ss}\"/>\n"
500 " <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
501 " </method>\n"
502 " <method name=\"GetCharacterExtents\">\n"
503 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
504 " <arg direction=\"out\" type=\"i\" name=\"x\"/>\n"
505 " <arg direction=\"out\" type=\"i\" name=\"y\"/>\n"
506 " <arg direction=\"out\" type=\"i\" name=\"width\"/>\n"
507 " <arg direction=\"out\" type=\"i\" name=\"height\"/>\n"
508 " <arg direction=\"in\" type=\"u\" name=\"coordType\"/>\n"
509 " </method>\n"
510 " <method name=\"GetOffsetAtPoint\">\n"
511 " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
512 " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
513 " <arg direction=\"in\" type=\"u\" name=\"coordType\"/>\n"
514 " <arg direction=\"out\" type=\"i\"/>\n"
515 " </method>\n"
516 " <method name=\"GetNSelections\">\n"
517 " <arg direction=\"out\" type=\"i\"/>\n"
518 " <method name=\"GetSelection\">\n"
519 " <arg direction=\"in\" type=\"i\" name=\"selectionNum\"/>\n"
520 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
521 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
522 " </method>\n"
523 " <method name=\"AddSelection\">\n"
524 " <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
525 " <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
526 " <arg direction=\"out\" type=\"b\"/>\n"
527 " </method>\n"
528 " <method name=\"RemoveSelection\">\n"
529 " <arg direction=\"in\" type=\"i\" name=\"selectionNum\"/>\n"
530 " <arg direction=\"out\" type=\"b\"/>\n"
531 " </method>\n"
532 " <method name=\"SetSelection\">\n"
533 " <arg direction=\"in\" type=\"i\" name=\"selectionNum\"/>\n"
534 " <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
535 " <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
536 " <arg direction=\"out\" type=\"b\"/>\n"
537 " </method>\n"
538 " <method name=\"GetRangeExtents\">\n"
539 " <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
540 " <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
541 " <arg direction=\"out\" type=\"i\" name=\"x\"/>\n"
542 " <arg direction=\"out\" type=\"i\" name=\"y\"/>\n"
543 " <arg direction=\"out\" type=\"i\" name=\"width\"/>\n"
544 " <arg direction=\"out\" type=\"i\" name=\"height\"/>\n"
545 " <arg direction=\"in\" type=\"u\" name=\"coordType\"/>\n"
546 " </method>\n"
547 " <method name=\"GetBoundedRanges\">\n"
548 " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
549 " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
550 " <arg direction=\"in\" type=\"i\" name=\"width\"/>\n"
551 " <arg direction=\"in\" type=\"i\" name=\"height\"/>\n"
552 " <arg direction=\"in\" type=\"u\" name=\"coordType\"/>\n"
553 " <arg direction=\"in\" type=\"u\" name=\"xClipType\"/>\n"
554 " <arg direction=\"in\" type=\"u\" name=\"yClipType\"/>\n"
555 " <arg direction=\"out\" type=\"a(iisv)\"/>\n"
556 " <annotation value=\"QSpiRangeList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
557 " </method>\n"
558 " <method name=\"GetAttributeRun\">\n"
559 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
560 " <arg direction=\"in\" type=\"b\" name=\"includeDefaults\"/>\n"
561 " <arg direction=\"out\" type=\"a{ss}\"/>\n"
562 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
563 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
564 " <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
565 " </method>\n"
566 " <method name=\"GetDefaultAttributeSet\">\n"
567 " <arg direction=\"out\" type=\"a{ss}\"/>\n"
568 " <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
569 " </method>\n"
570 " </interface>\n"
571 );
572
573 static const QLatin1String valueIntrospection(
574 " <interface name=\"org.a11y.atspi.Value\">\n"
575 " <property access=\"read\" type=\"d\" name=\"MinimumValue\"/>\n"
576 " <property access=\"read\" type=\"d\" name=\"MaximumValue\"/>\n"
577 " <property access=\"read\" type=\"d\" name=\"MinimumIncrement\"/>\n"
578 " <property access=\"readwrite\" type=\"d\" name=\"CurrentValue\"/>\n"
579 " <method name=\"SetCurrentValue\">\n"
580 " <arg direction=\"in\" type=\"d\" name=\"value\"/>\n"
581 " </method>\n"
582 " </interface>\n"
583 );
584
585 QAccessibleInterface * interface = interfaceFromPath(path);
586 if (!interface) {
587 qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << path;
588 return QString();
589 }
590
591 QStringList interfaces = accessibleInterfaces(interface);
592
593 QString xml;
594 xml.append(accessibleIntrospection);
595
596 if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT)))
597 xml.append(componentIntrospection);
598 if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_TEXT)))
599 xml.append(textIntrospection);
600 if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT)))
601 xml.append(editableTextIntrospection);
602 if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_ACTION)))
603 xml.append(actionIntrospection);
604 if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_TABLE)))
605 xml.append(tableIntrospection);
606 if (interfaces.contains(QLatin1String(ATSPI_DBUS_INTERFACE_VALUE)))
607 xml.append(valueIntrospection);
608 if (path == QLatin1String(QSPI_OBJECT_PATH_ROOT))
609 xml.append(applicationIntrospection);
610
611 return xml;
612 }
613
setBitFlag(const QString & flag)614 void AtSpiAdaptor::setBitFlag(const QString &flag)
615 {
616 Q_ASSERT(flag.size());
617
618 // assume we don't get nonsense - look at first letter only
619 switch (flag.at(0).toLower().toLatin1()) {
620 case 'o': {
621 if (flag.size() <= 8) { // Object::
622 sendObject = 1;
623 } else { // Object:Foo:Bar
624 QString right = flag.mid(7);
625 if (false) {
626 } else if (right.startsWith(QLatin1String("ActiveDescendantChanged"))) {
627 sendObject_active_descendant_changed = 1;
628 } else if (right.startsWith(QLatin1String("AttributesChanged"))) {
629 sendObject_attributes_changed = 1;
630 } else if (right.startsWith(QLatin1String("BoundsChanged"))) {
631 sendObject_bounds_changed = 1;
632 } else if (right.startsWith(QLatin1String("ChildrenChanged"))) {
633 sendObject_children_changed = 1;
634 } else if (right.startsWith(QLatin1String("ColumnDeleted"))) {
635 sendObject_column_deleted = 1;
636 } else if (right.startsWith(QLatin1String("ColumnInserted"))) {
637 sendObject_column_inserted = 1;
638 } else if (right.startsWith(QLatin1String("ColumnReordered"))) {
639 sendObject_column_reordered = 1;
640 } else if (right.startsWith(QLatin1String("LinkSelected"))) {
641 sendObject_link_selected = 1;
642 } else if (right.startsWith(QLatin1String("ModelChanged"))) {
643 sendObject_model_changed = 1;
644 } else if (right.startsWith(QLatin1String("PropertyChange"))) {
645 if (right == QLatin1String("PropertyChange:AccessibleDescription")) {
646 sendObject_property_change_accessible_description = 1;
647 } else if (right == QLatin1String("PropertyChange:AccessibleName")) {
648 sendObject_property_change_accessible_name = 1;
649 } else if (right == QLatin1String("PropertyChange:AccessibleParent")) {
650 sendObject_property_change_accessible_parent = 1;
651 } else if (right == QLatin1String("PropertyChange:AccessibleRole")) {
652 sendObject_property_change_accessible_role = 1;
653 } else if (right == QLatin1String("PropertyChange:TableCaption")) {
654 sendObject_property_change_accessible_table_caption = 1;
655 } else if (right == QLatin1String("PropertyChange:TableColumnDescription")) {
656 sendObject_property_change_accessible_table_column_description = 1;
657 } else if (right == QLatin1String("PropertyChange:TableColumnHeader")) {
658 sendObject_property_change_accessible_table_column_header = 1;
659 } else if (right == QLatin1String("PropertyChange:TableRowDescription")) {
660 sendObject_property_change_accessible_table_row_description = 1;
661 } else if (right == QLatin1String("PropertyChange:TableRowHeader")) {
662 sendObject_property_change_accessible_table_row_header = 1;
663 } else if (right == QLatin1String("PropertyChange:TableSummary")) {
664 sendObject_property_change_accessible_table_summary = 1;
665 } else if (right == QLatin1String("PropertyChange:AccessibleValue")) {
666 sendObject_property_change_accessible_value = 1;
667 } else {
668 sendObject_property_change = 1;
669 }
670 } else if (right.startsWith(QLatin1String("RowDeleted"))) {
671 sendObject_row_deleted = 1;
672 } else if (right.startsWith(QLatin1String("RowInserted"))) {
673 sendObject_row_inserted = 1;
674 } else if (right.startsWith(QLatin1String("RowReordered"))) {
675 sendObject_row_reordered = 1;
676 } else if (right.startsWith(QLatin1String("SelectionChanged"))) {
677 sendObject_selection_changed = 1;
678 } else if (right.startsWith(QLatin1String("StateChanged"))) {
679 sendObject_state_changed = 1;
680 } else if (right.startsWith(QLatin1String("TextAttributesChanged"))) {
681 sendObject_text_attributes_changed = 1;
682 } else if (right.startsWith(QLatin1String("TextBoundsChanged"))) {
683 sendObject_text_bounds_changed = 1;
684 } else if (right.startsWith(QLatin1String("TextCaretMoved"))) {
685 sendObject_text_caret_moved = 1;
686 } else if (right.startsWith(QLatin1String("TextChanged"))) {
687 sendObject_text_changed = 1;
688 } else if (right.startsWith(QLatin1String("TextSelectionChanged"))) {
689 sendObject_text_selection_changed = 1;
690 } else if (right.startsWith(QLatin1String("ValueChanged"))) {
691 sendObject_value_changed = 1;
692 } else if (right.startsWith(QLatin1String("VisibleDataChanged"))
693 || right.startsWith(QLatin1String("VisibledataChanged"))) { // typo in libatspi
694 sendObject_visible_data_changed = 1;
695 } else {
696 qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag;
697 }
698 }
699 break;
700 }
701 case 'w': { // window
702 if (flag.size() <= 8) {
703 sendWindow = 1;
704 } else { // object:Foo:Bar
705 QString right = flag.mid(7);
706 if (false) {
707 } else if (right.startsWith(QLatin1String("Activate"))) {
708 sendWindow_activate = 1;
709 } else if (right.startsWith(QLatin1String("Close"))) {
710 sendWindow_close= 1;
711 } else if (right.startsWith(QLatin1String("Create"))) {
712 sendWindow_create = 1;
713 } else if (right.startsWith(QLatin1String("Deactivate"))) {
714 sendWindow_deactivate = 1;
715 } else if (right.startsWith(QLatin1String("Lower"))) {
716 sendWindow_lower = 1;
717 } else if (right.startsWith(QLatin1String("Maximize"))) {
718 sendWindow_maximize = 1;
719 } else if (right.startsWith(QLatin1String("Minimize"))) {
720 sendWindow_minimize = 1;
721 } else if (right.startsWith(QLatin1String("Move"))) {
722 sendWindow_move = 1;
723 } else if (right.startsWith(QLatin1String("Raise"))) {
724 sendWindow_raise = 1;
725 } else if (right.startsWith(QLatin1String("Reparent"))) {
726 sendWindow_reparent = 1;
727 } else if (right.startsWith(QLatin1String("Resize"))) {
728 sendWindow_resize = 1;
729 } else if (right.startsWith(QLatin1String("Restore"))) {
730 sendWindow_restore = 1;
731 } else if (right.startsWith(QLatin1String("Restyle"))) {
732 sendWindow_restyle = 1;
733 } else if (right.startsWith(QLatin1String("Shade"))) {
734 sendWindow_shade = 1;
735 } else if (right.startsWith(QLatin1String("Unshade"))) {
736 sendWindow_unshade = 1;
737 } else if (right.startsWith(QLatin1String("DesktopCreate"))) {
738 // ignore this one
739 } else if (right.startsWith(QLatin1String("DesktopDestroy"))) {
740 // ignore this one
741 } else {
742 qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag;
743 }
744 }
745 break;
746 }
747 case 'f': {
748 sendFocus = 1;
749 break;
750 }
751 case 'd': { // document is not implemented
752 break;
753 }
754 case 't': { // terminal is not implemented
755 break;
756 }
757 case 'm': { // mouse* is handled in a different way by the gnome atspi stack
758 break;
759 }
760 default:
761 qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag;
762 }
763 }
764
765 /*!
766 Checks via dbus which events should be sent.
767 */
updateEventListeners()768 void AtSpiAdaptor::updateEventListeners()
769 {
770 QDBusMessage m = QDBusMessage::createMethodCall(QLatin1String("org.a11y.atspi.Registry"),
771 QLatin1String("/org/a11y/atspi/registry"),
772 QLatin1String("org.a11y.atspi.Registry"), QLatin1String("GetRegisteredEvents"));
773 QDBusReply<QSpiEventListenerArray> listenersReply = m_dbus->connection().call(m);
774 if (listenersReply.isValid()) {
775 const QSpiEventListenerArray evList = listenersReply.value();
776 for (const QSpiEventListener &ev : evList)
777 setBitFlag(ev.eventName);
778 m_applicationAdaptor->sendEvents(!evList.isEmpty());
779 } else {
780 qCDebug(lcAccessibilityAtspi) << "Could not query active accessibility event listeners.";
781 }
782 }
783
eventListenerDeregistered(const QString &,const QString &)784 void AtSpiAdaptor::eventListenerDeregistered(const QString &/*bus*/, const QString &/*path*/)
785 {
786 // qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::eventListenerDeregistered: " << bus << path;
787 updateEventListeners();
788 }
789
eventListenerRegistered(const QString &,const QString &)790 void AtSpiAdaptor::eventListenerRegistered(const QString &/*bus*/, const QString &/*path*/)
791 {
792 // qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::eventListenerRegistered: " << bus << path;
793 updateEventListeners();
794 }
795
796 /*!
797 This slot needs to get called when a \a window has be activated or deactivated (become focused).
798 When \a active is true, the window just received focus, otherwise it lost the focus.
799 */
windowActivated(QObject * window,bool active)800 void AtSpiAdaptor::windowActivated(QObject* window, bool active)
801 {
802 if (!(sendWindow || sendWindow_activate))
803 return;
804
805 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window);
806 // If the window has been quickly activated or disabled, it will cause a crash.
807 if (iface == nullptr)
808 return;
809 Q_ASSERT(!active || iface->isValid());
810
811 QString windowTitle;
812 // in dtor it may be invalid
813 if (iface->isValid())
814 windowTitle = iface->text(QAccessible::Name);
815
816 QDBusVariant data;
817 data.setVariant(windowTitle);
818
819 QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(data));
820
821 QString status = active ? QLatin1String("Activate") : QLatin1String("Deactivate");
822 QString path = pathForObject(window);
823 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_WINDOW), status, args);
824
825 QVariantList stateArgs = packDBusSignalArguments(QLatin1String("active"), active ? 1 : 0, 0, variantForPath(path));
826 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
827 QLatin1String("StateChanged"), stateArgs);
828 }
829
packDBusSignalArguments(const QString & type,int data1,int data2,const QVariant & variantData) const830 QVariantList AtSpiAdaptor::packDBusSignalArguments(const QString &type, int data1, int data2, const QVariant &variantData) const
831 {
832 QVariantList arguments;
833 arguments << type << data1 << data2 << variantData
834 << QVariant::fromValue(QSpiObjectReference(m_dbus->connection(), QDBusObjectPath(QSPI_OBJECT_PATH_ROOT)));
835 return arguments;
836 }
837
variantForPath(const QString & path) const838 QVariant AtSpiAdaptor::variantForPath(const QString &path) const
839 {
840 QDBusVariant data;
841 data.setVariant(QVariant::fromValue(QSpiObjectReference(m_dbus->connection(), QDBusObjectPath(path))));
842 return QVariant::fromValue(data);
843 }
844
sendDBusSignal(const QString & path,const QString & interface,const QString & signalName,const QVariantList & arguments) const845 bool AtSpiAdaptor::sendDBusSignal(const QString &path, const QString &interface, const QString &signalName, const QVariantList &arguments) const
846 {
847 QDBusMessage message = QDBusMessage::createSignal(path, interface, signalName);
848 message.setArguments(arguments);
849 return m_dbus->connection().send(message);
850 }
851
interfaceFromPath(const QString & dbusPath) const852 QAccessibleInterface *AtSpiAdaptor::interfaceFromPath(const QString& dbusPath) const
853 {
854 if (dbusPath == QLatin1String(QSPI_OBJECT_PATH_ROOT))
855 return QAccessible::queryAccessibleInterface(qApp);
856
857 QStringList parts = dbusPath.split(QLatin1Char('/'));
858 if (parts.size() != 6) {
859 qCDebug(lcAccessibilityAtspi) << "invalid path: " << dbusPath;
860 return 0;
861 }
862
863 QString objectString = parts.at(5);
864 QAccessible::Id id = objectString.toUInt();
865
866 // The id is always in the range [INT_MAX+1, UINT_MAX]
867 if ((int)id >= 0)
868 qCWarning(lcAccessibilityAtspi) << "No accessible object found for id: " << id;
869
870 return QAccessible::accessibleInterface(id);
871 }
872
notifyStateChange(QAccessibleInterface * interface,const QString & state,int value)873 void AtSpiAdaptor::notifyStateChange(QAccessibleInterface *interface, const QString &state, int value)
874 {
875 QString path = pathForInterface(interface);
876 QVariantList stateArgs = packDBusSignalArguments(state, value, 0, variantForPath(path));
877 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
878 QLatin1String("StateChanged"), stateArgs);
879 }
880
881
882 /*!
883 This function gets called when Qt notifies about accessibility updates.
884 */
notify(QAccessibleEvent * event)885 void AtSpiAdaptor::notify(QAccessibleEvent *event)
886 {
887 switch (event->type()) {
888 case QAccessible::ObjectCreated:
889 if (sendObject || sendObject_children_changed)
890 notifyAboutCreation(event->accessibleInterface());
891 break;
892 case QAccessible::ObjectShow: {
893 if (sendObject || sendObject_state_changed) {
894 notifyStateChange(event->accessibleInterface(), QLatin1String("showing"), 1);
895 }
896 break;
897 }
898 case QAccessible::ObjectHide: {
899 if (sendObject || sendObject_state_changed) {
900 notifyStateChange(event->accessibleInterface(), QLatin1String("showing"), 0);
901 }
902 break;
903 }
904 case QAccessible::ObjectDestroyed: {
905 if (sendObject || sendObject_state_changed)
906 notifyAboutDestruction(event->accessibleInterface());
907 break;
908 }
909 case QAccessible::ObjectReorder: {
910 if (sendObject || sendObject_children_changed)
911 childrenChanged(event->accessibleInterface());
912 break;
913 }
914 case QAccessible::NameChanged: {
915 if (sendObject || sendObject_property_change || sendObject_property_change_accessible_name) {
916 QString path = pathForInterface(event->accessibleInterface());
917 QVariantList args = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path));
918 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
919 QLatin1String("PropertyChange"), args);
920 }
921 break;
922 }
923 case QAccessible::DescriptionChanged: {
924 if (sendObject || sendObject_property_change || sendObject_property_change_accessible_description) {
925 QString path = pathForInterface(event->accessibleInterface());
926 QVariantList args = packDBusSignalArguments(QLatin1String("accessible-description"), 0, 0, variantForPath(path));
927 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
928 QLatin1String("PropertyChange"), args);
929 }
930 break;
931 }
932 case QAccessible::Focus: {
933 if (sendFocus || sendObject || sendObject_state_changed)
934 sendFocusChanged(event->accessibleInterface());
935 break;
936 }
937 case QAccessible::TextInserted:
938 case QAccessible::TextRemoved:
939 case QAccessible::TextUpdated: {
940 if (sendObject || sendObject_text_changed) {
941 QAccessibleInterface * iface = event->accessibleInterface();
942 if (!iface || !iface->textInterface()) {
943 qCDebug(lcAccessibilityAtspi) << "Received text event for invalid interface.";
944 return;
945 }
946 QString path = pathForInterface(iface);
947
948 int changePosition = 0;
949 int cursorPosition = 0;
950 QString textRemoved;
951 QString textInserted;
952
953 if (event->type() == QAccessible::TextInserted) {
954 QAccessibleTextInsertEvent *textEvent = static_cast<QAccessibleTextInsertEvent*>(event);
955 textInserted = textEvent->textInserted();
956 changePosition = textEvent->changePosition();
957 cursorPosition = textEvent->cursorPosition();
958 } else if (event->type() == QAccessible::TextRemoved) {
959 QAccessibleTextRemoveEvent *textEvent = static_cast<QAccessibleTextRemoveEvent*>(event);
960 textRemoved = textEvent->textRemoved();
961 changePosition = textEvent->changePosition();
962 cursorPosition = textEvent->cursorPosition();
963 } else if (event->type() == QAccessible::TextUpdated) {
964 QAccessibleTextUpdateEvent *textEvent = static_cast<QAccessibleTextUpdateEvent*>(event);
965 textInserted = textEvent->textInserted();
966 textRemoved = textEvent->textRemoved();
967 changePosition = textEvent->changePosition();
968 cursorPosition = textEvent->cursorPosition();
969 }
970
971 QDBusVariant data;
972
973 if (!textRemoved.isEmpty()) {
974 data.setVariant(QVariant::fromValue(textRemoved));
975 QVariantList args = packDBusSignalArguments(QLatin1String("delete"), changePosition, textRemoved.length(), QVariant::fromValue(data));
976 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
977 QLatin1String("TextChanged"), args);
978 }
979
980 if (!textInserted.isEmpty()) {
981 data.setVariant(QVariant::fromValue(textInserted));
982 QVariantList args = packDBusSignalArguments(QLatin1String("insert"), changePosition, textInserted.length(), QVariant::fromValue(data));
983 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
984 QLatin1String("TextChanged"), args);
985 }
986
987 // send a cursor update
988 Q_UNUSED(cursorPosition)
989 // QDBusVariant cursorData;
990 // cursorData.setVariant(QVariant::fromValue(cursorPosition));
991 // QVariantList args = packDBusSignalArguments(QString(), cursorPosition, 0, QVariant::fromValue(cursorData));
992 // sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
993 // QLatin1String("TextCaretMoved"), args);
994 }
995 break;
996 }
997 case QAccessible::TextCaretMoved: {
998 if (sendObject || sendObject_text_caret_moved) {
999 QAccessibleInterface * iface = event->accessibleInterface();
1000 if (!iface || !iface->textInterface()) {
1001 qCWarning(lcAccessibilityAtspi) << "Sending TextCaretMoved from object that does not implement text interface: " << iface;
1002 return;
1003 }
1004
1005 QString path = pathForInterface(iface);
1006 QDBusVariant cursorData;
1007 int pos = iface->textInterface()->cursorPosition();
1008 cursorData.setVariant(QVariant::fromValue(pos));
1009 QVariantList args = packDBusSignalArguments(QString(), pos, 0, QVariant::fromValue(cursorData));
1010 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
1011 QLatin1String("TextCaretMoved"), args);
1012 }
1013 break;
1014 }
1015 case QAccessible::TextSelectionChanged: {
1016 if (sendObject || sendObject_text_selection_changed) {
1017 QAccessibleInterface * iface = event->accessibleInterface();
1018 QString path = pathForInterface(iface);
1019 QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
1020 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
1021 QLatin1String("TextSelectionChanged"), args);
1022 }
1023 break;
1024 }
1025 case QAccessible::ValueChanged: {
1026 if (sendObject || sendObject_value_changed || sendObject_property_change_accessible_value) {
1027 QAccessibleInterface * iface = event->accessibleInterface();
1028 if (!iface) {
1029 qCWarning(lcAccessibilityAtspi) << "ValueChanged event from invalid accessible.";
1030 return;
1031 }
1032 if (iface->valueInterface()) {
1033 QString path = pathForInterface(iface);
1034 QVariantList args = packDBusSignalArguments(QLatin1String("accessible-value"), 0, 0, variantForPath(path));
1035 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
1036 QLatin1String("PropertyChange"), args);
1037 } else if (iface->role() == QAccessible::ComboBox) {
1038 // Combo Box with AT-SPI likes to be special
1039 // It requires a name-change to update caches and then selection-changed
1040 QString path = pathForInterface(iface);
1041 QVariantList args1 = packDBusSignalArguments(QLatin1String("accessible-name"), 0, 0, variantForPath(path));
1042 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
1043 QLatin1String("PropertyChange"), args1);
1044 QVariantList args2 = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(0))));
1045 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
1046 QLatin1String("SelectionChanged"), args2);
1047 } else {
1048 qCWarning(lcAccessibilityAtspi) << "ValueChanged event and no ValueInterface or ComboBox: " << iface;
1049 }
1050 }
1051 break;
1052 }
1053 case QAccessible::SelectionAdd:
1054 case QAccessible::SelectionRemove:
1055 case QAccessible::Selection: {
1056 QAccessibleInterface * iface = event->accessibleInterface();
1057 if (!iface) {
1058 qCWarning(lcAccessibilityAtspi) << "Selection event from invalid accessible.";
1059 return;
1060 }
1061 QString path = pathForInterface(iface);
1062 int selected = iface->state().selected ? 1 : 0;
1063 QVariantList stateArgs = packDBusSignalArguments(QLatin1String("selected"), selected, 0, variantForPath(path));
1064 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
1065 QLatin1String("StateChanged"), stateArgs);
1066 break;
1067 }
1068
1069 case QAccessible::StateChanged: {
1070 if (sendObject || sendObject_state_changed || sendWindow || sendWindow_activate) {
1071 QAccessible::State stateChange = static_cast<QAccessibleStateChangeEvent*>(event)->changedStates();
1072 if (stateChange.checked) {
1073 QAccessibleInterface * iface = event->accessibleInterface();
1074 if (!iface) {
1075 qCWarning(lcAccessibilityAtspi) << "StateChanged event from invalid accessible.";
1076 return;
1077 }
1078 int checked = iface->state().checked;
1079 notifyStateChange(iface, QLatin1String("checked"), checked);
1080 } else if (stateChange.active) {
1081 QAccessibleInterface * iface = event->accessibleInterface();
1082 if (!iface || !(iface->role() == QAccessible::Window && (sendWindow || sendWindow_activate)))
1083 return;
1084 int isActive = iface->state().active;
1085 QString windowTitle = iface->text(QAccessible::Name);
1086 QDBusVariant data;
1087 data.setVariant(windowTitle);
1088 QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(data));
1089 QString status = isActive ? QLatin1String("Activate") : QLatin1String("Deactivate");
1090 QString path = pathForInterface(iface);
1091 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_WINDOW), status, args);
1092 notifyStateChange(iface, QLatin1String("active"), isActive);
1093 } else if (stateChange.disabled) {
1094 QAccessibleInterface *iface = event->accessibleInterface();
1095 QAccessible::State state = iface->state();
1096 bool enabled = !state.disabled;
1097
1098 notifyStateChange(iface, QLatin1String("enabled"), enabled);
1099 notifyStateChange(iface, QLatin1String("sensitive"), enabled);
1100 }
1101 }
1102 break;
1103 }
1104 // For now we ignore these events
1105 case QAccessible::TableModelChanged:
1106 // For tables, setting manages_descendants should
1107 // indicate to the client that it cannot cache these
1108 // interfaces.
1109 case QAccessible::ParentChanged:
1110 case QAccessible::DialogStart:
1111 case QAccessible::DialogEnd:
1112 case QAccessible::PopupMenuStart:
1113 case QAccessible::PopupMenuEnd:
1114 case QAccessible::SoundPlayed:
1115 case QAccessible::Alert:
1116 case QAccessible::ForegroundChanged:
1117 case QAccessible::MenuStart:
1118 case QAccessible::MenuEnd:
1119 case QAccessible::ContextHelpStart:
1120 case QAccessible::ContextHelpEnd:
1121 case QAccessible::DragDropStart:
1122 case QAccessible::DragDropEnd:
1123 case QAccessible::ScrollingStart:
1124 case QAccessible::ScrollingEnd:
1125 case QAccessible::MenuCommand:
1126 case QAccessible::ActionChanged:
1127 case QAccessible::ActiveDescendantChanged:
1128 case QAccessible::AttributeChanged:
1129 case QAccessible::DocumentContentChanged:
1130 case QAccessible::DocumentLoadComplete:
1131 case QAccessible::DocumentLoadStopped:
1132 case QAccessible::DocumentReload:
1133 case QAccessible::HyperlinkEndIndexChanged:
1134 case QAccessible::HyperlinkNumberOfAnchorsChanged:
1135 case QAccessible::HyperlinkSelectedLinkChanged:
1136 case QAccessible::HypertextLinkActivated:
1137 case QAccessible::HypertextLinkSelected:
1138 case QAccessible::HyperlinkStartIndexChanged:
1139 case QAccessible::HypertextChanged:
1140 case QAccessible::HypertextNLinksChanged:
1141 case QAccessible::ObjectAttributeChanged:
1142 case QAccessible::PageChanged:
1143 case QAccessible::SectionChanged:
1144 case QAccessible::TableCaptionChanged:
1145 case QAccessible::TableColumnDescriptionChanged:
1146 case QAccessible::TableColumnHeaderChanged:
1147 case QAccessible::TableRowDescriptionChanged:
1148 case QAccessible::TableRowHeaderChanged:
1149 case QAccessible::TableSummaryChanged:
1150 case QAccessible::TextAttributeChanged:
1151 case QAccessible::TextColumnChanged:
1152 case QAccessible::VisibleDataChanged:
1153 case QAccessible::SelectionWithin:
1154 case QAccessible::LocationChanged:
1155 case QAccessible::HelpChanged:
1156 case QAccessible::DefaultActionChanged:
1157 case QAccessible::AcceleratorChanged:
1158 case QAccessible::InvalidEvent:
1159 break;
1160 }
1161 }
1162
sendFocusChanged(QAccessibleInterface * interface) const1163 void AtSpiAdaptor::sendFocusChanged(QAccessibleInterface *interface) const
1164 {
1165 static QString lastFocusPath;
1166 // "remove" old focus
1167 if (!lastFocusPath.isEmpty()) {
1168 QVariantList stateArgs = packDBusSignalArguments(QLatin1String("focused"), 0, 0, variantForPath(lastFocusPath));
1169 sendDBusSignal(lastFocusPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
1170 QLatin1String("StateChanged"), stateArgs);
1171 }
1172 // send new focus
1173 {
1174 QString path = pathForInterface(interface);
1175
1176 QVariantList stateArgs = packDBusSignalArguments(QLatin1String("focused"), 1, 0, variantForPath(path));
1177 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
1178 QLatin1String("StateChanged"), stateArgs);
1179
1180 QVariantList focusArgs = packDBusSignalArguments(QString(), 0, 0, variantForPath(path));
1181 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_FOCUS),
1182 QLatin1String("Focus"), focusArgs);
1183 lastFocusPath = path;
1184 }
1185 }
1186
childrenChanged(QAccessibleInterface * interface) const1187 void AtSpiAdaptor::childrenChanged(QAccessibleInterface *interface) const
1188 {
1189 QString parentPath = pathForInterface(interface);
1190 int childCount = interface->childCount();
1191 for (int i = 0; i < interface->childCount(); ++i) {
1192 QString childPath = pathForInterface(interface->child(i));
1193 QVariantList args = packDBusSignalArguments(QLatin1String("add"), childCount, 0, childPath);
1194 sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("ChildrenChanged"), args);
1195 }
1196 }
1197
notifyAboutCreation(QAccessibleInterface * interface) const1198 void AtSpiAdaptor::notifyAboutCreation(QAccessibleInterface *interface) const
1199 {
1200 // // say hello to d-bus
1201 // cache->emitAddAccessible(accessible->getCacheItem());
1202
1203 // notify about the new child of our parent
1204 QAccessibleInterface * parent = interface->parent();
1205 if (!parent) {
1206 qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::notifyAboutCreation: Could not find parent for " << interface->object();
1207 return;
1208 }
1209 QString path = pathForInterface(interface);
1210 int childCount = parent->childCount();
1211 QString parentPath = pathForInterface(parent);
1212 QVariantList args = packDBusSignalArguments(QLatin1String("add"), childCount, 0, variantForPath(path));
1213 sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("ChildrenChanged"), args);
1214 }
1215
notifyAboutDestruction(QAccessibleInterface * interface) const1216 void AtSpiAdaptor::notifyAboutDestruction(QAccessibleInterface *interface) const
1217 {
1218 if (!interface || !interface->isValid())
1219 return;
1220
1221 QAccessibleInterface * parent = interface->parent();
1222 if (!parent) {
1223 qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::notifyAboutDestruction: Could not find parent for " << interface->object();
1224 return;
1225 }
1226 QString path = pathForInterface(interface);
1227
1228 // this is in the destructor. we have no clue which child we used to be.
1229 // FIXME
1230 int childIndex = -1;
1231 // if (child) {
1232 // childIndex = child;
1233 // } else {
1234 // childIndex = parent->indexOfChild(interface);
1235 // }
1236
1237 QString parentPath = pathForInterface(parent);
1238 QVariantList args = packDBusSignalArguments(QLatin1String("remove"), childIndex, 0, variantForPath(path));
1239 sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("ChildrenChanged"), args);
1240 }
1241
1242 /*!
1243 Handle incoming DBus message.
1244 This function dispatches the dbus message to the right interface handler.
1245 */
handleMessage(const QDBusMessage & message,const QDBusConnection & connection)1246 bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnection &connection)
1247 {
1248 // get accessible interface
1249 QAccessibleInterface * accessible = interfaceFromPath(message.path());
1250 if (!accessible) {
1251 qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << message.path();
1252 return false;
1253 }
1254 if (!accessible->isValid()) {
1255 qCWarning(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Accessible invalid: " << accessible << message.path();
1256 return false;
1257 }
1258
1259 QString interface = message.interface();
1260 QString function = message.member();
1261
1262 // qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::handleMessage: " << interface << function;
1263
1264 if (function == QLatin1String("Introspect")) {
1265 //introspect(message.path());
1266 return false;
1267 }
1268
1269 // handle properties like regular functions
1270 if (interface == QLatin1String("org.freedesktop.DBus.Properties")) {
1271 interface = message.arguments().at(0).toString();
1272 // Get/Set + Name
1273 function = message.member() + message.arguments().at(1).toString();
1274 }
1275
1276 // switch interface to call
1277 if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_ACCESSIBLE))
1278 return accessibleInterface(accessible, function, message, connection);
1279 if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_APPLICATION))
1280 return applicationInterface(accessible, function, message, connection);
1281 if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT))
1282 return componentInterface(accessible, function, message, connection);
1283 if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_ACTION))
1284 return actionInterface(accessible, function, message, connection);
1285 if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_TEXT))
1286 return textInterface(accessible, function, message, connection);
1287 if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT))
1288 return editableTextInterface(accessible, function, message, connection);
1289 if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_VALUE))
1290 return valueInterface(accessible, function, message, connection);
1291 if (interface == QLatin1String(ATSPI_DBUS_INTERFACE_TABLE))
1292 return tableInterface(accessible, function, message, connection);
1293
1294 qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::handleMessage with unknown interface: " << message.path() << interface << function;
1295 return false;
1296 }
1297
1298 // Application
applicationInterface(QAccessibleInterface * interface,const QString & function,const QDBusMessage & message,const QDBusConnection & connection)1299 bool AtSpiAdaptor::applicationInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
1300 {
1301 if (message.path() != QLatin1String(ATSPI_DBUS_PATH_ROOT)) {
1302 qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find application interface for: " << message.path() << interface;
1303 return false;
1304 }
1305
1306 if (function == QLatin1String("SetId")) {
1307 Q_ASSERT(message.signature() == QLatin1String("ssv"));
1308 QVariant value = qvariant_cast<QDBusVariant>(message.arguments().at(2)).variant();
1309
1310 m_applicationId = value.toInt();
1311 return true;
1312 }
1313 if (function == QLatin1String("GetId")) {
1314 Q_ASSERT(message.signature() == QLatin1String("ss"));
1315 QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(m_applicationId)));
1316 return connection.send(reply);
1317 }
1318 if (function == QLatin1String("GetToolkitName")) {
1319 Q_ASSERT(message.signature() == QLatin1String("ss"));
1320 QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(QLatin1String("Qt"))));
1321 return connection.send(reply);
1322 }
1323 if (function == QLatin1String("GetVersion")) {
1324 Q_ASSERT(message.signature() == QLatin1String("ss"));
1325 QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(QLatin1String(qVersion()))));
1326 return connection.send(reply);
1327 }
1328 if (function == QLatin1String("GetLocale")) {
1329 Q_ASSERT(message.signature() == QLatin1String("u"));
1330 QDBusMessage reply = message.createReply(QVariant::fromValue(QLocale().name()));
1331 return connection.send(reply);
1332 }
1333 qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::applicationInterface " << message.path() << interface << function;
1334 return false;
1335 }
1336
1337 /*!
1338 Register this application as accessible on the accessibility DBus.
1339 */
registerApplication()1340 void AtSpiAdaptor::registerApplication()
1341 {
1342 OrgA11yAtspiSocketInterface *registry;
1343 registry = new OrgA11yAtspiSocketInterface(QLatin1String(QSPI_REGISTRY_NAME),
1344 QLatin1String(QSPI_OBJECT_PATH_ROOT), m_dbus->connection());
1345
1346 QDBusPendingReply<QSpiObjectReference> reply;
1347 QSpiObjectReference ref = QSpiObjectReference(m_dbus->connection(), QDBusObjectPath(QSPI_OBJECT_PATH_ROOT));
1348 reply = registry->Embed(ref);
1349 reply.waitForFinished(); // TODO: make this async
1350 if (reply.isValid ()) {
1351 const QSpiObjectReference &socket = reply.value();
1352 accessibilityRegistry = QSpiObjectReference(socket);
1353 } else {
1354 qCDebug(lcAccessibilityAtspi) << "Error in contacting registry: "
1355 << reply.error().name()
1356 << reply.error().message();
1357 }
1358 delete registry;
1359 }
1360
1361 // Accessible
accessibleInterface(QAccessibleInterface * interface,const QString & function,const QDBusMessage & message,const QDBusConnection & connection)1362 bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
1363 {
1364 if (function == QLatin1String("GetRole")) {
1365 sendReply(connection, message, (uint) getRole(interface));
1366 } else if (function == QLatin1String("GetName")) {
1367 sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->text(QAccessible::Name))));
1368 } else if (function == QLatin1String("GetRoleName")) {
1369 sendReply(connection, message, qSpiRoleMapping[interface->role()].name());
1370 } else if (function == QLatin1String("GetLocalizedRoleName")) {
1371 sendReply(connection, message, QVariant::fromValue(qSpiRoleMapping[interface->role()].localizedName()));
1372 } else if (function == QLatin1String("GetChildCount")) {
1373 sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->childCount())));
1374 } else if (function == QLatin1String("GetIndexInParent")) {
1375 int childIndex = -1;
1376 QAccessibleInterface * parent = interface->parent();
1377 if (parent) {
1378 childIndex = parent->indexOfChild(interface);
1379 if (childIndex < 0) {
1380 qCDebug(lcAccessibilityAtspi) << "GetIndexInParent get invalid index: " << childIndex << interface;
1381 }
1382 }
1383 sendReply(connection, message, childIndex);
1384 } else if (function == QLatin1String("GetParent")) {
1385 QString path;
1386 QAccessibleInterface * parent = interface->parent();
1387 if (!parent) {
1388 path = QLatin1String(ATSPI_DBUS_PATH_NULL);
1389 } else if (parent->role() == QAccessible::Application) {
1390 path = QLatin1String(ATSPI_DBUS_PATH_ROOT);
1391 } else {
1392 path = pathForInterface(parent);
1393 }
1394 // Parent is a property, so it needs to be wrapped inside an extra variant.
1395 sendReply(connection, message, QVariant::fromValue(
1396 QDBusVariant(QVariant::fromValue(QSpiObjectReference(connection, QDBusObjectPath(path))))));
1397 } else if (function == QLatin1String("GetChildAtIndex")) {
1398 const int index = message.arguments().at(0).toInt();
1399 if (index < 0) {
1400 sendReply(connection, message, QVariant::fromValue(
1401 QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))));
1402 } else {
1403 QAccessibleInterface * childInterface = interface->child(index);
1404 sendReply(connection, message, QVariant::fromValue(
1405 QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(childInterface)))));
1406 }
1407 } else if (function == QLatin1String("GetInterfaces")) {
1408 sendReply(connection, message, accessibleInterfaces(interface));
1409 } else if (function == QLatin1String("GetDescription")) {
1410 sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->text(QAccessible::Description))));
1411 } else if (function == QLatin1String("GetState")) {
1412 quint64 spiState = spiStatesFromQState(interface->state());
1413 if (interface->tableInterface()) {
1414 setSpiStateBit(&spiState, ATSPI_STATE_MANAGES_DESCENDANTS);
1415 }
1416 QAccessible::Role role = interface->role();
1417 if (role == QAccessible::TreeItem ||
1418 role == QAccessible::ListItem) {
1419 /* Transient means libatspi2 will not cache items.
1420 This is important because when adding/removing an item
1421 the cache becomes outdated and we don't change the paths of
1422 items in lists/trees/tables. */
1423 setSpiStateBit(&spiState, ATSPI_STATE_TRANSIENT);
1424 }
1425 sendReply(connection, message,
1426 QVariant::fromValue(spiStateSetFromSpiStates(spiState)));
1427 } else if (function == QLatin1String("GetAttributes")) {
1428 sendReply(connection, message, QVariant::fromValue(QSpiAttributeSet()));
1429 } else if (function == QLatin1String("GetRelationSet")) {
1430 sendReply(connection, message, QVariant::fromValue(relationSet(interface, connection)));
1431 } else if (function == QLatin1String("GetApplication")) {
1432 sendReply(connection, message, QVariant::fromValue(
1433 QSpiObjectReference(connection, QDBusObjectPath(QSPI_OBJECT_PATH_ROOT))));
1434 } else if (function == QLatin1String("GetChildren")) {
1435 QSpiObjectReferenceArray children;
1436 const int numChildren = interface->childCount();
1437 children.reserve(numChildren);
1438 for (int i = 0; i < numChildren; ++i) {
1439 QString childPath = pathForInterface(interface->child(i));
1440 QSpiObjectReference ref(connection, QDBusObjectPath(childPath));
1441 children << ref;
1442 }
1443 connection.send(message.createReply(QVariant::fromValue(children)));
1444 } else {
1445 qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::accessibleInterface does not implement " << function << message.path();
1446 return false;
1447 }
1448 return true;
1449 }
1450
getRole(QAccessibleInterface * interface) const1451 AtspiRole AtSpiAdaptor::getRole(QAccessibleInterface *interface) const
1452 {
1453 if ((interface->role() == QAccessible::EditableText) && interface->state().passwordEdit)
1454 return ATSPI_ROLE_PASSWORD_TEXT;
1455 return qSpiRoleMapping[interface->role()].spiRole();
1456 }
1457
accessibleInterfaces(QAccessibleInterface * interface) const1458 QStringList AtSpiAdaptor::accessibleInterfaces(QAccessibleInterface *interface) const
1459 {
1460 QStringList ifaces;
1461 qCDebug(lcAccessibilityAtspiCreation) << "AtSpiAdaptor::accessibleInterfaces create: " << interface->object();
1462 ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_ACCESSIBLE);
1463
1464 if ( (!interface->rect().isEmpty()) ||
1465 (interface->object() && interface->object()->isWidgetType()) ||
1466 (interface->role() == QAccessible::ListItem) ||
1467 (interface->role() == QAccessible::Cell) ||
1468 (interface->role() == QAccessible::TreeItem) ||
1469 (interface->role() == QAccessible::Row) ||
1470 (interface->object() && interface->object()->inherits("QSGItem"))
1471 ) {
1472 ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_COMPONENT);
1473 } else {
1474 qCDebug(lcAccessibilityAtspiCreation) << " IS NOT a component";
1475 }
1476 if (interface->role() == QAccessible::Application)
1477 ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_APPLICATION);
1478
1479 if (interface->actionInterface() || interface->valueInterface())
1480 ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_ACTION);
1481
1482 if (interface->textInterface())
1483 ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_TEXT);
1484
1485 if (interface->editableTextInterface())
1486 ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT);
1487
1488 if (interface->valueInterface())
1489 ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_VALUE);
1490
1491 if (interface->tableInterface())
1492 ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_TABLE);
1493
1494 return ifaces;
1495 }
1496
relationSet(QAccessibleInterface * interface,const QDBusConnection & connection) const1497 QSpiRelationArray AtSpiAdaptor::relationSet(QAccessibleInterface *interface, const QDBusConnection &connection) const
1498 {
1499 typedef QPair<QAccessibleInterface*, QAccessible::Relation> RelationPair;
1500 const QVector<RelationPair> relationInterfaces = interface->relations();
1501
1502 QSpiRelationArray relations;
1503 for (const RelationPair &pair : relationInterfaces) {
1504 // FIXME: this loop seems a bit strange... "related" always have one item when we check.
1505 //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
1506 QSpiObjectReferenceArray related;
1507
1508 QDBusObjectPath path = QDBusObjectPath(pathForInterface(pair.first));
1509 related.append(QSpiObjectReference(connection, path));
1510
1511 if (!related.isEmpty())
1512 relations.append(QSpiRelationArrayEntry(qAccessibleRelationToAtSpiRelation(pair.second), related));
1513 }
1514 return relations;
1515 }
1516
sendReply(const QDBusConnection & connection,const QDBusMessage & message,const QVariant & argument) const1517 void AtSpiAdaptor::sendReply(const QDBusConnection &connection, const QDBusMessage &message, const QVariant &argument) const
1518 {
1519 QDBusMessage reply = message.createReply(argument);
1520 connection.send(reply);
1521 }
1522
1523
pathForObject(QObject * object) const1524 QString AtSpiAdaptor::pathForObject(QObject *object) const
1525 {
1526 Q_ASSERT(object);
1527
1528 if (inheritsQAction(object)) {
1529 qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::pathForObject: warning: creating path with QAction as object.";
1530 }
1531
1532 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(object);
1533 return pathForInterface(iface);
1534 }
1535
pathForInterface(QAccessibleInterface * interface) const1536 QString AtSpiAdaptor::pathForInterface(QAccessibleInterface *interface) const
1537 {
1538 if (!interface || !interface->isValid())
1539 return QLatin1String(ATSPI_DBUS_PATH_NULL);
1540 if (interface->role() == QAccessible::Application)
1541 return QLatin1String(QSPI_OBJECT_PATH_ROOT);
1542
1543 QAccessible::Id id = QAccessible::uniqueId(interface);
1544 Q_ASSERT((int)id < 0);
1545 return QLatin1String(QSPI_OBJECT_PATH_PREFIX) + QString::number(id);
1546 }
1547
inheritsQAction(QObject * object)1548 bool AtSpiAdaptor::inheritsQAction(QObject *object)
1549 {
1550 const QMetaObject *mo = object->metaObject();
1551 while (mo) {
1552 const QLatin1String cn(mo->className());
1553 if (cn == QLatin1String("QAction"))
1554 return true;
1555 mo = mo->superClass();
1556 }
1557 return false;
1558 }
1559
1560 // Component
getWindow(QAccessibleInterface * interface)1561 static QAccessibleInterface * getWindow(QAccessibleInterface * interface)
1562 {
1563 if (interface->role() == QAccessible::Window)
1564 return interface;
1565
1566 QAccessibleInterface * parent = interface->parent();
1567 while (parent && parent->role() != QAccessible::Window)
1568 parent = parent->parent();
1569
1570 return parent;
1571 }
1572
getRelativeRect(QAccessibleInterface * interface)1573 static QRect getRelativeRect(QAccessibleInterface *interface)
1574 {
1575 QAccessibleInterface * window;
1576 QRect wr, cr;
1577
1578 cr = interface->rect();
1579
1580 window = getWindow(interface);
1581 if (window) {
1582 wr = window->rect();
1583
1584 cr.setX(cr.x() - wr.x());
1585 cr.setY(cr.x() - wr.y());
1586 }
1587 return cr;
1588 }
1589
componentInterface(QAccessibleInterface * interface,const QString & function,const QDBusMessage & message,const QDBusConnection & connection)1590 bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
1591 {
1592 if (function == QLatin1String("Contains")) {
1593 bool ret = false;
1594 int x = message.arguments().at(0).toInt();
1595 int y = message.arguments().at(1).toInt();
1596 uint coordType = message.arguments().at(2).toUInt();
1597 if (coordType == ATSPI_COORD_TYPE_SCREEN)
1598 ret = interface->rect().contains(x, y);
1599 else
1600 ret = getRelativeRect(interface).contains(x, y);
1601 sendReply(connection, message, ret);
1602 } else if (function == QLatin1String("GetAccessibleAtPoint")) {
1603 int x = message.arguments().at(0).toInt();
1604 int y = message.arguments().at(1).toInt();
1605 uint coordType = message.arguments().at(2).toUInt();
1606 if (coordType == ATSPI_COORD_TYPE_WINDOW) {
1607 QWindow * window = interface->window();
1608 if (window) {
1609 x += window->position().x();
1610 y += window->position().y();
1611 }
1612 }
1613
1614 QAccessibleInterface * childInterface(interface->childAt(x, y));
1615 QAccessibleInterface * iface = 0;
1616 while (childInterface) {
1617 iface = childInterface;
1618 childInterface = iface->childAt(x, y);
1619 }
1620 if (iface) {
1621 QString path = pathForInterface(iface);
1622 sendReply(connection, message, QVariant::fromValue(
1623 QSpiObjectReference(connection, QDBusObjectPath(path))));
1624 } else {
1625 sendReply(connection, message, QVariant::fromValue(
1626 QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))));
1627 }
1628 } else if (function == QLatin1String("GetAlpha")) {
1629 sendReply(connection, message, (double) 1.0);
1630 } else if (function == QLatin1String("GetExtents")) {
1631 uint coordType = message.arguments().at(0).toUInt();
1632 sendReply(connection, message, QVariant::fromValue(getExtents(interface, coordType)));
1633 } else if (function == QLatin1String("GetLayer")) {
1634 sendReply(connection, message, QVariant::fromValue((uint)1));
1635 } else if (function == QLatin1String("GetMDIZOrder")) {
1636 sendReply(connection, message, QVariant::fromValue((short)0));
1637 } else if (function == QLatin1String("GetPosition")) {
1638 uint coordType = message.arguments().at(0).toUInt();
1639 QRect rect;
1640 if (coordType == ATSPI_COORD_TYPE_SCREEN)
1641 rect = interface->rect();
1642 else
1643 rect = getRelativeRect(interface);
1644 QVariantList pos;
1645 pos << rect.x() << rect.y();
1646 connection.send(message.createReply(pos));
1647 } else if (function == QLatin1String("GetSize")) {
1648 QRect rect = interface->rect();
1649 QVariantList size;
1650 size << rect.width() << rect.height();
1651 connection.send(message.createReply(size));
1652 } else if (function == QLatin1String("GrabFocus")) {
1653 QAccessibleActionInterface *actionIface = interface->actionInterface();
1654 if (actionIface && actionIface->actionNames().contains(QAccessibleActionInterface::setFocusAction())) {
1655 actionIface->doAction(QAccessibleActionInterface::setFocusAction());
1656 sendReply(connection, message, true);
1657 } else {
1658 sendReply(connection, message, false);
1659 }
1660 } else if (function == QLatin1String("SetExtents")) {
1661 // int x = message.arguments().at(0).toInt();
1662 // int y = message.arguments().at(1).toInt();
1663 // int width = message.arguments().at(2).toInt();
1664 // int height = message.arguments().at(3).toInt();
1665 // uint coordinateType = message.arguments().at(4).toUInt();
1666 qCDebug(lcAccessibilityAtspi) << "SetExtents is not implemented.";
1667 sendReply(connection, message, false);
1668 } else if (function == QLatin1String("SetPosition")) {
1669 // int x = message.arguments().at(0).toInt();
1670 // int y = message.arguments().at(1).toInt();
1671 // uint coordinateType = message.arguments().at(2).toUInt();
1672 qCDebug(lcAccessibilityAtspi) << "SetPosition is not implemented.";
1673 sendReply(connection, message, false);
1674 } else if (function == QLatin1String("SetSize")) {
1675 // int width = message.arguments().at(0).toInt();
1676 // int height = message.arguments().at(1).toInt();
1677 qCDebug(lcAccessibilityAtspi) << "SetSize is not implemented.";
1678 sendReply(connection, message, false);
1679 } else {
1680 qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::componentInterface does not implement " << function << message.path();
1681 return false;
1682 }
1683 return true;
1684 }
1685
getExtents(QAccessibleInterface * interface,uint coordType)1686 QRect AtSpiAdaptor::getExtents(QAccessibleInterface *interface, uint coordType)
1687 {
1688 return (coordType == ATSPI_COORD_TYPE_SCREEN) ? interface->rect() : getRelativeRect(interface);
1689 }
1690
1691 // Action interface
actionInterface(QAccessibleInterface * interface,const QString & function,const QDBusMessage & message,const QDBusConnection & connection)1692 bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
1693 {
1694 if (function == QLatin1String("GetNActions")) {
1695 int count = QAccessibleBridgeUtils::effectiveActionNames(interface).count();
1696 sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(count))));
1697 } else if (function == QLatin1String("DoAction")) {
1698 int index = message.arguments().at(0).toInt();
1699 const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface);
1700 if (index < 0 || index >= actionNames.count())
1701 return false;
1702 const QString actionName = actionNames.at(index);
1703 bool success = QAccessibleBridgeUtils::performEffectiveAction(interface, actionName);
1704 sendReply(connection, message, success);
1705 } else if (function == QLatin1String("GetActions")) {
1706 sendReply(connection, message, QVariant::fromValue(getActions(interface)));
1707 } else if (function == QLatin1String("GetName")) {
1708 int index = message.arguments().at(0).toInt();
1709 const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface);
1710 if (index < 0 || index >= actionNames.count())
1711 return false;
1712 sendReply(connection, message, actionNames.at(index));
1713 } else if (function == QLatin1String("GetDescription")) {
1714 int index = message.arguments().at(0).toInt();
1715 const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface);
1716 if (index < 0 || index >= actionNames.count())
1717 return false;
1718 QString description;
1719 if (QAccessibleActionInterface *actionIface = interface->actionInterface())
1720 description = actionIface->localizedActionDescription(actionNames.at(index));
1721 else
1722 description = qAccessibleLocalizedActionDescription(actionNames.at(index));
1723 sendReply(connection, message, description);
1724 } else if (function == QLatin1String("GetKeyBinding")) {
1725 int index = message.arguments().at(0).toInt();
1726 const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface);
1727 if (index < 0 || index >= actionNames.count())
1728 return false;
1729 QStringList keyBindings;
1730 if (QAccessibleActionInterface *actionIface = interface->actionInterface())
1731 keyBindings = actionIface->keyBindingsForAction(actionNames.at(index));
1732 if (keyBindings.isEmpty()) {
1733 QString acc = interface->text(QAccessible::Accelerator);
1734 if (!acc.isEmpty())
1735 keyBindings.append(acc);
1736 }
1737 if (keyBindings.length() > 0)
1738 sendReply(connection, message, keyBindings.join(QLatin1Char(';')));
1739 else
1740 sendReply(connection, message, QString());
1741 } else {
1742 qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::actionInterface does not implement " << function << message.path();
1743 return false;
1744 }
1745 return true;
1746 }
1747
getActions(QAccessibleInterface * interface) const1748 QSpiActionArray AtSpiAdaptor::getActions(QAccessibleInterface *interface) const
1749 {
1750 QAccessibleActionInterface *actionInterface = interface->actionInterface();
1751 QSpiActionArray actions;
1752 const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface);
1753 actions.reserve(actionNames.size());
1754 for (const QString &actionName : actionNames) {
1755 QSpiAction action;
1756
1757 action.name = actionName;
1758 if (actionInterface) {
1759 action.description = actionInterface->localizedActionDescription(actionName);
1760 const QStringList keyBindings = actionInterface->keyBindingsForAction(actionName);
1761 if (!keyBindings.isEmpty())
1762 action.keyBinding = keyBindings.front();
1763 } else {
1764 action.description = qAccessibleLocalizedActionDescription(actionName);
1765 }
1766
1767 actions.append(std::move(action));
1768 }
1769 return actions;
1770 }
1771
1772 // Text interface
textInterface(QAccessibleInterface * interface,const QString & function,const QDBusMessage & message,const QDBusConnection & connection)1773 bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
1774 {
1775 if (!interface->textInterface())
1776 return false;
1777
1778 // properties
1779 if (function == QLatin1String("GetCaretOffset")) {
1780 sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(interface->textInterface()->cursorPosition()))));
1781 } else if (function == QLatin1String("GetCharacterCount")) {
1782 sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(interface->textInterface()->characterCount()))));
1783
1784 // functions
1785 } else if (function == QLatin1String("AddSelection")) {
1786 int startOffset = message.arguments().at(0).toInt();
1787 int endOffset = message.arguments().at(1).toInt();
1788 int lastSelection = interface->textInterface()->selectionCount();
1789 interface->textInterface()->setSelection(lastSelection, startOffset, endOffset);
1790 sendReply(connection, message, (interface->textInterface()->selectionCount() > lastSelection));
1791 } else if (function == QLatin1String("GetAttributeRun")) {
1792 int offset = message.arguments().at(0).toInt();
1793 bool includeDefaults = message.arguments().at(1).toBool();
1794 Q_UNUSED(includeDefaults)
1795 connection.send(message.createReply(getAttributes(interface, offset, includeDefaults)));
1796 } else if (function == QLatin1String("GetAttributeValue")) {
1797 int offset = message.arguments().at(0).toInt();
1798 QString attributeName = message.arguments().at(1).toString();
1799 connection.send(message.createReply(getAttributeValue(interface, offset, attributeName)));
1800 } else if (function == QLatin1String("GetAttributes")) {
1801 int offset = message.arguments().at(0).toInt();
1802 connection.send(message.createReply(getAttributes(interface, offset, true)));
1803 } else if (function == QLatin1String("GetBoundedRanges")) {
1804 int x = message.arguments().at(0).toInt();
1805 int y = message.arguments().at(1).toInt();
1806 int width = message.arguments().at(2).toInt();
1807 int height = message.arguments().at(3).toInt();
1808 uint coordType = message.arguments().at(4).toUInt();
1809 uint xClipType = message.arguments().at(5).toUInt();
1810 uint yClipType = message.arguments().at(6).toUInt();
1811 Q_UNUSED(x) Q_UNUSED (y) Q_UNUSED(width)
1812 Q_UNUSED(height) Q_UNUSED(coordType)
1813 Q_UNUSED(xClipType) Q_UNUSED(yClipType)
1814 qCDebug(lcAccessibilityAtspi) << "Not implemented: QSpiAdaptor::GetBoundedRanges";
1815 sendReply(connection, message, QVariant::fromValue(QSpiTextRangeList()));
1816 } else if (function == QLatin1String("GetCharacterAtOffset")) {
1817 int offset = message.arguments().at(0).toInt();
1818 int start;
1819 int end;
1820 QString result = interface->textInterface()->textAtOffset(offset, QAccessible::CharBoundary, &start, &end);
1821 sendReply(connection, message, (int) *(qPrintable (result)));
1822 } else if (function == QLatin1String("GetCharacterExtents")) {
1823 int offset = message.arguments().at(0).toInt();
1824 int coordType = message.arguments().at(1).toUInt();
1825 connection.send(message.createReply(getCharacterExtents(interface, offset, coordType)));
1826 } else if (function == QLatin1String("GetDefaultAttributeSet") || function == QLatin1String("GetDefaultAttributes")) {
1827 // GetDefaultAttributes is deprecated in favour of GetDefaultAttributeSet.
1828 // Empty set seems reasonable. There is no default attribute set.
1829 sendReply(connection, message, QVariant::fromValue(QSpiAttributeSet()));
1830 } else if (function == QLatin1String("GetNSelections")) {
1831 sendReply(connection, message, interface->textInterface()->selectionCount());
1832 } else if (function == QLatin1String("GetOffsetAtPoint")) {
1833 qCDebug(lcAccessibilityAtspi) << message.signature();
1834 Q_ASSERT(!message.signature().isEmpty());
1835 QPoint point(message.arguments().at(0).toInt(), message.arguments().at(1).toInt());
1836 uint coordType = message.arguments().at(2).toUInt();
1837 if (coordType == ATSPI_COORD_TYPE_WINDOW) {
1838 QWindow *win = interface->window();
1839 point -= QPoint(win->x(), win->y());
1840 }
1841 int offset = interface->textInterface()->offsetAtPoint(point);
1842 sendReply(connection, message, offset);
1843 } else if (function == QLatin1String("GetRangeExtents")) {
1844 int startOffset = message.arguments().at(0).toInt();
1845 int endOffset = message.arguments().at(1).toInt();
1846 uint coordType = message.arguments().at(2).toUInt();
1847 connection.send(message.createReply(getRangeExtents(interface, startOffset, endOffset, coordType)));
1848 } else if (function == QLatin1String("GetSelection")) {
1849 int selectionNum = message.arguments().at(0).toInt();
1850 int start, end;
1851 interface->textInterface()->selection(selectionNum, &start, &end);
1852 if (start < 0)
1853 start = end = interface->textInterface()->cursorPosition();
1854 QVariantList sel;
1855 sel << start << end;
1856 connection.send(message.createReply(sel));
1857 } else if (function == QLatin1String("GetText")) {
1858 int startOffset = message.arguments().at(0).toInt();
1859 int endOffset = message.arguments().at(1).toInt();
1860 if (endOffset == -1) // AT-SPI uses -1 to signal all characters
1861 endOffset = interface->textInterface()->characterCount();
1862 sendReply(connection, message, interface->textInterface()->text(startOffset, endOffset));
1863 } else if (function == QLatin1String("GetTextAfterOffset")) {
1864 int offset = message.arguments().at(0).toInt();
1865 int type = message.arguments().at(1).toUInt();
1866 int startOffset, endOffset;
1867 QString text = interface->textInterface()->textAfterOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset);
1868 QVariantList ret;
1869 ret << text << startOffset << endOffset;
1870 connection.send(message.createReply(ret));
1871 } else if (function == QLatin1String("GetTextAtOffset")) {
1872 int offset = message.arguments().at(0).toInt();
1873 int type = message.arguments().at(1).toUInt();
1874 int startOffset, endOffset;
1875 QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset);
1876 QVariantList ret;
1877 ret << text << startOffset << endOffset;
1878 connection.send(message.createReply(ret));
1879 } else if (function == QLatin1String("GetTextBeforeOffset")) {
1880 int offset = message.arguments().at(0).toInt();
1881 int type = message.arguments().at(1).toUInt();
1882 int startOffset, endOffset;
1883 QString text = interface->textInterface()->textBeforeOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset);
1884 QVariantList ret;
1885 ret << text << startOffset << endOffset;
1886 connection.send(message.createReply(ret));
1887 } else if (function == QLatin1String("RemoveSelection")) {
1888 int selectionNum = message.arguments().at(0).toInt();
1889 interface->textInterface()->removeSelection(selectionNum);
1890 sendReply(connection, message, true);
1891 } else if (function == QLatin1String("SetCaretOffset")) {
1892 int offset = message.arguments().at(0).toInt();
1893 interface->textInterface()->setCursorPosition(offset);
1894 sendReply(connection, message, true);
1895 } else if (function == QLatin1String("SetSelection")) {
1896 int selectionNum = message.arguments().at(0).toInt();
1897 int startOffset = message.arguments().at(1).toInt();
1898 int endOffset = message.arguments().at(2).toInt();
1899 interface->textInterface()->setSelection(selectionNum, startOffset, endOffset);
1900 sendReply(connection, message, true);
1901 } else {
1902 qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::textInterface does not implement " << function << message.path();
1903 return false;
1904 }
1905 return true;
1906 }
1907
qAccessibleBoundaryType(int atspiTextBoundaryType) const1908 QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryType(int atspiTextBoundaryType) const
1909 {
1910 switch (atspiTextBoundaryType) {
1911 case ATSPI_TEXT_BOUNDARY_CHAR:
1912 return QAccessible::CharBoundary;
1913 case ATSPI_TEXT_BOUNDARY_WORD_START:
1914 case ATSPI_TEXT_BOUNDARY_WORD_END:
1915 return QAccessible::WordBoundary;
1916 case ATSPI_TEXT_BOUNDARY_SENTENCE_START:
1917 case ATSPI_TEXT_BOUNDARY_SENTENCE_END:
1918 return QAccessible::SentenceBoundary;
1919 case ATSPI_TEXT_BOUNDARY_LINE_START:
1920 case ATSPI_TEXT_BOUNDARY_LINE_END:
1921 return QAccessible::LineBoundary;
1922 }
1923 Q_ASSERT_X(0, "", "Requested invalid boundary type.");
1924 return QAccessible::CharBoundary;
1925 }
1926
1927 namespace
1928 {
1929 struct AtSpiAttribute {
1930 QString name;
1931 QString value;
AtSpiAttribute__anonac6c99f40111::AtSpiAttribute1932 AtSpiAttribute(const QString &aName, const QString &aValue) : name(aName), value(aValue) {}
isNull__anonac6c99f40111::AtSpiAttribute1933 bool isNull() const { return name.isNull() || value.isNull(); }
1934 };
1935
atspiColor(const QString & ia2Color)1936 QString atspiColor(const QString &ia2Color)
1937 {
1938 // "rgb(%u,%u,%u)" -> "%u,%u,%u"
1939 return ia2Color.mid(4, ia2Color.length() - (4+1));
1940 }
1941
atspiSize(const QString & ia2Size)1942 QString atspiSize(const QString &ia2Size)
1943 {
1944 // "%fpt" -> "%f"
1945 return ia2Size.left(ia2Size.length() - 2);
1946 }
1947
atspiTextAttribute(const QString & ia2Name,const QString & ia2Value)1948 AtSpiAttribute atspiTextAttribute(const QString &ia2Name, const QString &ia2Value)
1949 {
1950 QString name = ia2Name;
1951 QString value = ia2Value;
1952
1953 // IAccessible2: http://www.linuxfoundation.org/collaborate/workgroups/accessibility/iaccessible2/textattributes
1954 // ATK attribute names: https://git.gnome.org/browse/orca/tree/src/orca/text_attribute_names.py
1955 // ATK attribute values: https://developer.gnome.org/atk/unstable/AtkText.html#AtkTextAttribute
1956
1957 // https://bugzilla.gnome.org/show_bug.cgi?id=744553 "ATK docs provide no guidance for allowed values of some text attributes"
1958 // specifically for "weight", "invalid", "language" and value range for colors
1959
1960 if (ia2Name == QLatin1String("background-color")) {
1961 name = QStringLiteral("bg-color");
1962 value = atspiColor(value);
1963 } else if (ia2Name == QLatin1String("font-family")) {
1964 name = QStringLiteral("family-name");
1965 } else if (ia2Name == QLatin1String("color")) {
1966 name = QStringLiteral("fg-color");
1967 value = atspiColor(value);
1968 } else if (ia2Name == QLatin1String("text-align")) {
1969 name = QStringLiteral("justification");
1970 if (value == QLatin1String("justify")) {
1971 value = QStringLiteral("fill");
1972 } else {
1973 if (value != QLatin1String("left") &&
1974 value != QLatin1String("right") &&
1975 value != QLatin1String("center")
1976 ) {
1977 value = QString();
1978 qCDebug(lcAccessibilityAtspi) << "Unknown text-align attribute value \"" << value << "\" cannot be translated to AT-SPI.";
1979 }
1980 }
1981 } else if (ia2Name == QLatin1String("font-size")) {
1982 name = QStringLiteral("size");
1983 value = atspiSize(value);
1984 } else if (ia2Name == QLatin1String("font-style")) {
1985 name = QStringLiteral("style");
1986 if (value != QLatin1String("normal") &&
1987 value != QLatin1String("italic") &&
1988 value != QLatin1String("oblique")
1989 ) {
1990 value = QString();
1991 qCDebug(lcAccessibilityAtspi) << "Unknown font-style attribute value \"" << value << "\" cannot be translated to AT-SPI.";
1992 }
1993 } else if (ia2Name == QLatin1String("text-underline-type")) {
1994 name = QStringLiteral("underline");
1995 if (value != QLatin1String("none") &&
1996 value != QLatin1String("single") &&
1997 value != QLatin1String("double")
1998 ) {
1999 value = QString();
2000 qCDebug(lcAccessibilityAtspi) << "Unknown text-underline-type attribute value \"" << value << "\" cannot be translated to AT-SPI.";
2001 }
2002 } else if (ia2Name == QLatin1String("font-weight")) {
2003 name = QStringLiteral("weight");
2004 if (value == QLatin1String("normal"))
2005 // Orca seems to accept all IAccessible2 values except for "normal"
2006 // (on which it produces traceback and fails to read any following text attributes),
2007 // but that is the default value, so omit it anyway
2008 value = QString();
2009 } else if (ia2Name == QLatin1String("text-position")) {
2010 name = QStringLiteral("vertical-align");
2011 if (value != QLatin1String("baseline") &&
2012 value != QLatin1String("super") &&
2013 value != QLatin1String("sub")
2014 ) {
2015 value = QString();
2016 qCDebug(lcAccessibilityAtspi) << "Unknown text-position attribute value \"" << value << "\" cannot be translated to AT-SPI.";
2017 }
2018 } else if (ia2Name == QLatin1String("writing-mode")) {
2019 name = QStringLiteral("direction");
2020 if (value == QLatin1String("lr"))
2021 value = QStringLiteral("ltr");
2022 else if (value == QLatin1String("rl"))
2023 value = QStringLiteral("rtl");
2024 else if (value == QLatin1String("tb")) {
2025 // 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))
2026 value = QStringLiteral("rtl");
2027 qCDebug(lcAccessibilityAtspi) << "writing-mode attribute value \"tb\" translated only w.r.t. horizontal direction; vertical direction ignored";
2028 } else {
2029 value = QString();
2030 qCDebug(lcAccessibilityAtspi) << "Unknown writing-mode attribute value \"" << value << "\" cannot be translated to AT-SPI.";
2031 }
2032 } else if (ia2Name == QLatin1String("language")) {
2033 // OK - ATK has no docs on the format of the value, IAccessible2 has reasonable format - leave it at that now
2034 } else if (ia2Name == QLatin1String("invalid")) {
2035 // OK - ATK docs are vague but suggest they support the same range of values as IAccessible2
2036 } else {
2037 // attribute we know nothing about
2038 name = QString();
2039 value = QString();
2040 }
2041 return AtSpiAttribute(name, value);
2042 }
2043 }
2044
2045 // FIXME all attribute methods below should share code
getAttributes(QAccessibleInterface * interface,int offset,bool includeDefaults) const2046 QVariantList AtSpiAdaptor::getAttributes(QAccessibleInterface *interface, int offset, bool includeDefaults) const
2047 {
2048 Q_UNUSED(includeDefaults);
2049
2050 QSpiAttributeSet set;
2051 int startOffset;
2052 int endOffset;
2053
2054 QString joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset);
2055 const QStringList attributes = joined.split (QLatin1Char(';'), Qt::SkipEmptyParts, Qt::CaseSensitive);
2056 for (const QString &attr : attributes) {
2057 QStringList items;
2058 items = attr.split(QLatin1Char(':'), Qt::SkipEmptyParts, Qt::CaseSensitive);
2059 AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]);
2060 if (!attribute.isNull())
2061 set[attribute.name] = attribute.value;
2062 }
2063
2064 QVariantList list;
2065 list << QVariant::fromValue(set) << startOffset << endOffset;
2066
2067 return list;
2068 }
2069
getAttributeValue(QAccessibleInterface * interface,int offset,const QString & attributeName) const2070 QVariantList AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, int offset, const QString &attributeName) const
2071 {
2072 QString mapped;
2073 QString joined;
2074 QSpiAttributeSet map;
2075 int startOffset;
2076 int endOffset;
2077
2078 joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset);
2079 const QStringList attributes = joined.split (QLatin1Char(';'), Qt::SkipEmptyParts, Qt::CaseSensitive);
2080 for (const QString& attr : attributes) {
2081 QStringList items;
2082 items = attr.split(QLatin1Char(':'), Qt::SkipEmptyParts, Qt::CaseSensitive);
2083 AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]);
2084 if (!attribute.isNull())
2085 map[attribute.name] = attribute.value;
2086 }
2087 mapped = map[attributeName];
2088 const bool defined = !mapped.isEmpty();
2089 QVariantList list;
2090 list << mapped << startOffset << endOffset << defined;
2091 return list;
2092 }
2093
getCharacterExtents(QAccessibleInterface * interface,int offset,uint coordType) const2094 QList<QVariant> AtSpiAdaptor::getCharacterExtents(QAccessibleInterface *interface, int offset, uint coordType) const
2095 {
2096 QRect rect = interface->textInterface()->characterRect(offset);
2097
2098 if (coordType == ATSPI_COORD_TYPE_WINDOW)
2099 rect = translateRectToWindowCoordinates(interface, rect);
2100
2101 return QList<QVariant>() << rect.x() << rect.y() << rect.width() << rect.height();
2102 }
2103
getRangeExtents(QAccessibleInterface * interface,int startOffset,int endOffset,uint coordType) const2104 QList<QVariant> AtSpiAdaptor::getRangeExtents(QAccessibleInterface *interface,
2105 int startOffset, int endOffset, uint coordType) const
2106 {
2107 if (endOffset == -1)
2108 endOffset = interface->textInterface()->characterCount();
2109
2110 QAccessibleTextInterface *textInterface = interface->textInterface();
2111 if (endOffset <= startOffset || !textInterface)
2112 return QList<QVariant>() << -1 << -1 << 0 << 0;
2113
2114 QRect rect = textInterface->characterRect(startOffset);
2115 for (int i=startOffset + 1; i <= endOffset; i++)
2116 rect = rect | textInterface->characterRect(i);
2117
2118 // relative to window
2119 if (coordType == ATSPI_COORD_TYPE_WINDOW)
2120 rect = translateRectToWindowCoordinates(interface, rect);
2121
2122 return QList<QVariant>() << rect.x() << rect.y() << rect.width() << rect.height();
2123 }
2124
translateRectToWindowCoordinates(QAccessibleInterface * interface,const QRect & rect)2125 QRect AtSpiAdaptor::translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect)
2126 {
2127 QAccessibleInterface * window = getWindow(interface);
2128 if (window)
2129 return rect.translated(-window->rect().x(), -window->rect().y());
2130
2131 return rect;
2132 }
2133
2134
2135 // Editable Text interface
textForRange(QAccessibleInterface * accessible,int startOffset,int endOffset)2136 static QString textForRange(QAccessibleInterface *accessible, int startOffset, int endOffset)
2137 {
2138 if (QAccessibleTextInterface *textIface = accessible->textInterface()) {
2139 if (endOffset == -1)
2140 endOffset = textIface->characterCount();
2141 return textIface->text(startOffset, endOffset);
2142 }
2143 QString txt = accessible->text(QAccessible::Value);
2144 if (endOffset == -1)
2145 endOffset = txt.length();
2146 return txt.mid(startOffset, endOffset - startOffset);
2147 }
2148
replaceTextFallback(QAccessibleInterface * accessible,long startOffset,long endOffset,const QString & txt)2149 static void replaceTextFallback(QAccessibleInterface *accessible, long startOffset, long endOffset, const QString &txt)
2150 {
2151 QString t = textForRange(accessible, 0, -1);
2152 if (endOffset == -1)
2153 endOffset = t.length();
2154 if (endOffset - startOffset == 0)
2155 t.insert(startOffset, txt);
2156 else
2157 t.replace(startOffset, endOffset - startOffset, txt);
2158 accessible->setText(QAccessible::Value, t);
2159 }
2160
editableTextInterface(QAccessibleInterface * interface,const QString & function,const QDBusMessage & message,const QDBusConnection & connection)2161 bool AtSpiAdaptor::editableTextInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
2162 {
2163 if (function == QLatin1String("CopyText")) {
2164 #ifndef QT_NO_CLIPBOARD
2165 int startOffset = message.arguments().at(0).toInt();
2166 int endOffset = message.arguments().at(1).toInt();
2167 const QString t = textForRange(interface, startOffset, endOffset);
2168 QGuiApplication::clipboard()->setText(t);
2169 #endif
2170 connection.send(message.createReply(true));
2171 } else if (function == QLatin1String("CutText")) {
2172 #ifndef QT_NO_CLIPBOARD
2173 int startOffset = message.arguments().at(0).toInt();
2174 int endOffset = message.arguments().at(1).toInt();
2175 const QString t = textForRange(interface, startOffset, endOffset);
2176 if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
2177 editableTextIface->deleteText(startOffset, endOffset);
2178 else
2179 replaceTextFallback(interface, startOffset, endOffset, QString());
2180 QGuiApplication::clipboard()->setText(t);
2181 #endif
2182 connection.send(message.createReply(true));
2183 } else if (function == QLatin1String("DeleteText")) {
2184 int startOffset = message.arguments().at(0).toInt();
2185 int endOffset = message.arguments().at(1).toInt();
2186 if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
2187 editableTextIface->deleteText(startOffset, endOffset);
2188 else
2189 replaceTextFallback(interface, startOffset, endOffset, QString());
2190 connection.send(message.createReply(true));
2191 } else if (function == QLatin1String("InsertText")) {
2192 int position = message.arguments().at(0).toInt();
2193 QString text = message.arguments().at(1).toString();
2194 int length = message.arguments().at(2).toInt();
2195 text.resize(length);
2196 if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
2197 editableTextIface->insertText(position, text);
2198 else
2199 replaceTextFallback(interface, position, position, text);
2200 connection.send(message.createReply(true));
2201 } else if (function == QLatin1String("PasteText")) {
2202 #ifndef QT_NO_CLIPBOARD
2203 int position = message.arguments().at(0).toInt();
2204 const QString txt = QGuiApplication::clipboard()->text();
2205 if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
2206 editableTextIface->insertText(position, txt);
2207 else
2208 replaceTextFallback(interface, position, position, txt);
2209 #endif
2210 connection.send(message.createReply(true));
2211 } else if (function == QLatin1String("SetTextContents")) {
2212 QString newContents = message.arguments().at(0).toString();
2213 if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
2214 editableTextIface->replaceText(0, interface->textInterface()->characterCount(), newContents);
2215 else
2216 replaceTextFallback(interface, 0, -1, newContents);
2217 connection.send(message.createReply(true));
2218 } else if (function == QLatin1String("")) {
2219 connection.send(message.createReply());
2220 } else {
2221 qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::editableTextInterface does not implement " << function << message.path();
2222 return false;
2223 }
2224 return true;
2225 }
2226
2227 // Value interface
valueInterface(QAccessibleInterface * interface,const QString & function,const QDBusMessage & message,const QDBusConnection & connection)2228 bool AtSpiAdaptor::valueInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
2229 {
2230 QAccessibleValueInterface *valueIface = interface->valueInterface();
2231 if (!valueIface)
2232 return false;
2233
2234 if (function == QLatin1String("SetCurrentValue")) {
2235 QDBusVariant v = qvariant_cast<QDBusVariant>(message.arguments().at(2));
2236 double value = v.variant().toDouble();
2237 //Temporary fix
2238 //See https://bugzilla.gnome.org/show_bug.cgi?id=652596
2239 valueIface->setCurrentValue(value);
2240 connection.send(message.createReply()); // FIXME is the reply needed?
2241 } else {
2242 QVariant value;
2243 if (function == QLatin1String("GetCurrentValue"))
2244 value = valueIface->currentValue();
2245 else if (function == QLatin1String("GetMaximumValue"))
2246 value = valueIface->maximumValue();
2247 else if (function == QLatin1String("GetMinimumIncrement"))
2248 value = valueIface->minimumStepSize();
2249 else if (function == QLatin1String("GetMinimumValue"))
2250 value = valueIface->minimumValue();
2251 else {
2252 qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::valueInterface does not implement " << function << message.path();
2253 return false;
2254 }
2255 if (!value.canConvert(QMetaType::Double)) {
2256 qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::valueInterface: Could not convert to double: " << function;
2257 }
2258
2259 // explicitly convert to dbus-variant containing one double since atspi expects that
2260 // everything else might fail to convert back on the other end
2261 connection.send(message.createReply(
2262 QVariant::fromValue(QDBusVariant(QVariant::fromValue(value.toDouble())))));
2263 }
2264 return true;
2265 }
2266
2267 // Table interface
tableInterface(QAccessibleInterface * interface,const QString & function,const QDBusMessage & message,const QDBusConnection & connection)2268 bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
2269 {
2270 if (!(interface->tableInterface() || interface->tableCellInterface())) {
2271 qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find table interface for: " << message.path() << interface;
2272 return false;
2273 }
2274
2275 if (0) {
2276 // properties
2277 } else if (function == QLatin1String("GetCaption")) {
2278 QAccessibleInterface * captionInterface= interface->tableInterface()->caption();
2279 if (captionInterface) {
2280 QSpiObjectReference ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(captionInterface)));
2281 sendReply(connection, message, QVariant::fromValue(ref));
2282 } else {
2283 sendReply(connection, message, QVariant::fromValue(
2284 QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))));
2285 }
2286 } else if (function == QLatin1String("GetNColumns")) {
2287 connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
2288 QVariant::fromValue(interface->tableInterface()->columnCount())))));
2289 } else if (function == QLatin1String("GetNRows")) {
2290 connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
2291 QVariant::fromValue(interface->tableInterface()->rowCount())))));
2292 } else if (function == QLatin1String("GetNSelectedColumns")) {
2293 connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
2294 QVariant::fromValue(interface->tableInterface()->selectedColumnCount())))));
2295 } else if (function == QLatin1String("GetNSelectedRows")) {
2296 connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
2297 QVariant::fromValue(interface->tableInterface()->selectedRowCount())))));
2298 } else if (function == QLatin1String("GetSummary")) {
2299 QAccessibleInterface * summary = interface->tableInterface() ? interface->tableInterface()->summary() : 0;
2300 QSpiObjectReference ref(connection, QDBusObjectPath(pathForInterface(summary)));
2301 connection.send(message.createReply(QVariant::fromValue(QDBusVariant(QVariant::fromValue(ref)))));
2302 } else if (function == QLatin1String("GetAccessibleAt")) {
2303 int row = message.arguments().at(0).toInt();
2304 int column = message.arguments().at(1).toInt();
2305 if ((row < 0) ||
2306 (column < 0) ||
2307 (row >= interface->tableInterface()->rowCount()) ||
2308 (column >= interface->tableInterface()->columnCount())) {
2309 qCDebug(lcAccessibilityAtspi) << "WARNING: invalid index for tableInterface GetAccessibleAt (" << row << ", " << column << ')';
2310 return false;
2311 }
2312
2313 QSpiObjectReference ref;
2314 QAccessibleInterface * cell(interface->tableInterface()->cellAt(row, column));
2315 if (cell) {
2316 ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(cell)));
2317 } else {
2318 qCDebug(lcAccessibilityAtspi) << "WARNING: no cell interface returned for " << interface->object() << row << column;
2319 ref = QSpiObjectReference();
2320 }
2321 connection.send(message.createReply(QVariant::fromValue(ref)));
2322
2323 } else if (function == QLatin1String("GetIndexAt")) {
2324 int row = message.arguments().at(0).toInt();
2325 int column = message.arguments().at(1).toInt();
2326 QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column);
2327 if (!cell) {
2328 qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::GetIndexAt(" << row << ',' << column << ") did not find a cell. " << interface;
2329 return false;
2330 }
2331 int index = interface->indexOfChild(cell);
2332 qCDebug(lcAccessibilityAtspi) << "QSpiAdaptor::GetIndexAt row:" << row << " col:" << column << " logical index:" << index;
2333 Q_ASSERT(index > 0);
2334 connection.send(message.createReply(index));
2335 } else if ((function == QLatin1String("GetColumnAtIndex")) || (function == QLatin1String("GetRowAtIndex"))) {
2336 int index = message.arguments().at(0).toInt();
2337 int ret = -1;
2338 if (index >= 0) {
2339 QAccessibleInterface * cell = interface->child(index);
2340 if (cell) {
2341 if (function == QLatin1String("GetColumnAtIndex")) {
2342 if (cell->role() == QAccessible::ColumnHeader) {
2343 ret = index;
2344 } else if (cell->role() == QAccessible::RowHeader) {
2345 ret = -1;
2346 } else {
2347 if (!cell->tableCellInterface()) {
2348 qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell;
2349 return false;
2350 }
2351 ret = cell->tableCellInterface()->columnIndex();
2352 }
2353 } else {
2354 if (cell->role() == QAccessible::ColumnHeader) {
2355 ret = -1;
2356 } else if (cell->role() == QAccessible::RowHeader) {
2357 ret = index % interface->tableInterface()->columnCount();
2358 } else {
2359 if (!cell->tableCellInterface()) {
2360 qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell;
2361 return false;
2362 }
2363 ret = cell->tableCellInterface()->rowIndex();
2364 }
2365 }
2366 } else {
2367 qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No cell at index: " << index << interface;
2368 return false;
2369 }
2370 }
2371 connection.send(message.createReply(ret));
2372
2373 } else if (function == QLatin1String("GetColumnDescription")) {
2374 int column = message.arguments().at(0).toInt();
2375 connection.send(message.createReply(interface->tableInterface()->columnDescription(column)));
2376 } else if (function == QLatin1String("GetRowDescription")) {
2377 int row = message.arguments().at(0).toInt();
2378 connection.send(message.createReply(interface->tableInterface()->rowDescription(row)));
2379
2380
2381
2382 } else if (function == QLatin1String("GetRowColumnExtentsAtIndex")) {
2383 int index = message.arguments().at(0).toInt();
2384 bool success = false;
2385
2386 int row = -1;
2387 int col = -1;
2388 int rowExtents = -1;
2389 int colExtents = -1;
2390 bool isSelected = false;
2391
2392 int cols = interface->tableInterface()->columnCount();
2393 if (cols > 0) {
2394 row = index / cols;
2395 col = index % cols;
2396 QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, col)->tableCellInterface();
2397 if (cell) {
2398 row = cell->rowIndex();
2399 col = cell->columnIndex();
2400 rowExtents = cell->rowExtent();
2401 colExtents = cell->columnExtent();
2402 isSelected = cell->isSelected();
2403 success = true;
2404 }
2405 }
2406 QVariantList list;
2407 list << success << row << col << rowExtents << colExtents << isSelected;
2408 connection.send(message.createReply(list));
2409
2410 } else if (function == QLatin1String("GetColumnExtentAt")) {
2411 int row = message.arguments().at(0).toInt();
2412 int column = message.arguments().at(1).toInt();
2413 connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->columnExtent()));
2414
2415 } else if (function == QLatin1String("GetRowExtentAt")) {
2416 int row = message.arguments().at(0).toInt();
2417 int column = message.arguments().at(1).toInt();
2418 connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->rowExtent()));
2419
2420 } else if (function == QLatin1String("GetColumnHeader")) {
2421 int column = message.arguments().at(0).toInt();
2422 QSpiObjectReference ref;
2423
2424 QAccessibleInterface * cell(interface->tableInterface()->cellAt(0, column));
2425 if (cell && cell->tableCellInterface()) {
2426 QList<QAccessibleInterface*> header = cell->tableCellInterface()->columnHeaderCells();
2427 if (header.size() > 0) {
2428 ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(header.takeAt(0))));
2429 }
2430 }
2431 connection.send(message.createReply(QVariant::fromValue(ref)));
2432
2433 } else if (function == QLatin1String("GetRowHeader")) {
2434 int row = message.arguments().at(0).toInt();
2435 QSpiObjectReference ref;
2436 QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, 0)->tableCellInterface();
2437 if (cell) {
2438 QList<QAccessibleInterface*> header = cell->rowHeaderCells();
2439 if (header.size() > 0) {
2440 ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(header.takeAt(0))));
2441 }
2442 }
2443 connection.send(message.createReply(QVariant::fromValue(ref)));
2444
2445 } else if (function == QLatin1String("GetSelectedColumns")) {
2446 connection.send(message.createReply(QVariant::fromValue(interface->tableInterface()->selectedColumns())));
2447 } else if (function == QLatin1String("GetSelectedRows")) {
2448 connection.send(message.createReply(QVariant::fromValue(interface->tableInterface()->selectedRows())));
2449 } else if (function == QLatin1String("IsColumnSelected")) {
2450 int column = message.arguments().at(0).toInt();
2451 connection.send(message.createReply(interface->tableInterface()->isColumnSelected(column)));
2452 } else if (function == QLatin1String("IsRowSelected")) {
2453 int row = message.arguments().at(0).toInt();
2454 connection.send(message.createReply(interface->tableInterface()->isRowSelected(row)));
2455 } else if (function == QLatin1String("IsSelected")) {
2456 int row = message.arguments().at(0).toInt();
2457 int column = message.arguments().at(1).toInt();
2458 QAccessibleTableCellInterface* cell = interface->tableInterface()->cellAt(row, column)->tableCellInterface();
2459 connection.send(message.createReply(cell->isSelected()));
2460 } else if (function == QLatin1String("AddColumnSelection")) {
2461 int column = message.arguments().at(0).toInt();
2462 connection.send(message.createReply(interface->tableInterface()->selectColumn(column)));
2463 } else if (function == QLatin1String("AddRowSelection")) {
2464 int row = message.arguments().at(0).toInt();
2465 connection.send(message.createReply(interface->tableInterface()->selectRow(row)));
2466 } else if (function == QLatin1String("RemoveColumnSelection")) {
2467 int column = message.arguments().at(0).toInt();
2468 connection.send(message.createReply(interface->tableInterface()->unselectColumn(column)));
2469 } else if (function == QLatin1String("RemoveRowSelection")) {
2470 int row = message.arguments().at(0).toInt();
2471 connection.send(message.createReply(interface->tableInterface()->unselectRow(row)));
2472 } else {
2473 qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::tableInterface does not implement " << function << message.path();
2474 return false;
2475 }
2476 return true;
2477 }
2478
2479 QT_END_NAMESPACE
2480 #endif //QT_NO_ACCESSIBILITY
2481