Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
atspiadaptor.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "atspiadaptor_p.h"
6
7#include <QtGui/qwindow.h>
8#include <QtGui/qguiapplication.h>
9#include <qdbusmessage.h>
10#include <qdbusreply.h>
11#include <qclipboard.h>
12
13#include <QtCore/qloggingcategory.h>
14#include <QtCore/qtversion.h>
15
16#if QT_CONFIG(accessibility)
17#include "socket_interface.h"
19#include <QtCore/private/qstringiterator_p.h>
20#include <QtGui/private/qaccessiblebridgeutils_p.h>
21
33// ATSPI_COORD_TYPE_PARENT was added in at-spi 2.30, define here for older versions
34#if ATSPI_COORD_TYPE_COUNT < 3
35#define ATSPI_COORD_TYPE_PARENT 2
36#endif
37
39
40using namespace Qt::StringLiterals;
41
42Q_LOGGING_CATEGORY(lcAccessibilityAtspi, "qt.accessibility.atspi")
43Q_LOGGING_CATEGORY(lcAccessibilityAtspiCreation, "qt.accessibility.atspi.creation")
44
47 , sendFocus(0)
48 , sendObject(0)
49 , sendObject_active_descendant_changed(0)
50 , sendObject_attributes_changed(0)
51 , sendObject_bounds_changed(0)
52 , sendObject_children_changed(0)
53// , sendObject_children_changed_add(0)
54// , sendObject_children_changed_remove(0)
55 , sendObject_column_deleted(0)
56 , sendObject_column_inserted(0)
57 , sendObject_column_reordered(0)
58 , sendObject_link_selected(0)
59 , sendObject_model_changed(0)
60 , sendObject_property_change(0)
61 , sendObject_property_change_accessible_description(0)
62 , sendObject_property_change_accessible_name(0)
63 , sendObject_property_change_accessible_parent(0)
64 , sendObject_property_change_accessible_role(0)
65 , sendObject_property_change_accessible_table_caption(0)
66 , sendObject_property_change_accessible_table_column_description(0)
67 , sendObject_property_change_accessible_table_column_header(0)
68 , sendObject_property_change_accessible_table_row_description(0)
69 , sendObject_property_change_accessible_table_row_header(0)
70 , sendObject_property_change_accessible_table_summary(0)
71 , sendObject_property_change_accessible_value(0)
72 , sendObject_row_deleted(0)
73 , sendObject_row_inserted(0)
74 , sendObject_row_reordered(0)
75 , sendObject_selection_changed(0)
76 , sendObject_state_changed(0)
77 , sendObject_text_attributes_changed(0)
78 , sendObject_text_bounds_changed(0)
79 , sendObject_text_caret_moved(0)
80 , sendObject_text_changed(0)
81// , sendObject_text_changed_delete(0)
82// , sendObject_text_changed_insert(0)
83 , sendObject_text_selection_changed(0)
84 , sendObject_value_changed(0)
85 , sendObject_visible_data_changed(0)
86 , sendWindow(0)
87 , sendWindow_activate(0)
88 , sendWindow_close(0)
89 , sendWindow_create(0)
90 , sendWindow_deactivate(0)
91// , sendWindow_desktop_create(0)
92// , sendWindow_desktop_destroy(0)
93 , sendWindow_lower(0)
94 , sendWindow_maximize(0)
95 , sendWindow_minimize(0)
96 , sendWindow_move(0)
97 , sendWindow_raise(0)
98 , sendWindow_reparent(0)
99 , sendWindow_resize(0)
100 , sendWindow_restore(0)
101 , sendWindow_restyle(0)
102 , sendWindow_shade(0)
103 , sendWindow_unshade(0)
104{
105 m_applicationAdaptor = new QSpiApplicationAdaptor(m_dbus->connection(), this);
106 connect(m_applicationAdaptor, SIGNAL(windowActivated(QObject*,bool)), this, SLOT(windowActivated(QObject*,bool)));
107
108 updateEventListeners();
109 bool success = m_dbus->connection().connect("org.a11y.atspi.Registry"_L1, "/org/a11y/atspi/registry"_L1,
110 "org.a11y.atspi.Registry"_L1, "EventListenerRegistered"_L1, this,
111 SLOT(eventListenerRegistered(QString,QString)));
112 success = success && m_dbus->connection().connect("org.a11y.atspi.Registry"_L1, "/org/a11y/atspi/registry"_L1,
113 "org.a11y.atspi.Registry"_L1, "EventListenerDeregistered"_L1, this,
114 SLOT(eventListenerDeregistered(QString,QString)));
115}
116
118{
119}
120
125{
126 static const QLatin1StringView accessibleIntrospection(
127 " <interface name=\"org.a11y.atspi.Accessible\">\n"
128 " <property access=\"read\" type=\"s\" name=\"Name\"/>\n"
129 " <property access=\"read\" type=\"s\" name=\"Description\"/>\n"
130 " <property access=\"read\" type=\"(so)\" name=\"Parent\">\n"
131 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName\"/>\n"
132 " </property>\n"
133 " <property access=\"read\" type=\"i\" name=\"ChildCount\"/>\n"
134 " <method name=\"GetChildAtIndex\">\n"
135 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
136 " <arg direction=\"out\" type=\"(so)\"/>\n"
137 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
138 " </method>\n"
139 " <method name=\"GetChildren\">\n"
140 " <arg direction=\"out\" type=\"a(so)\"/>\n"
141 " <annotation value=\"QSpiObjectReferenceArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
142 " </method>\n"
143 " <method name=\"GetIndexInParent\">\n"
144 " <arg direction=\"out\" type=\"i\"/>\n"
145 " </method>\n"
146 " <method name=\"GetRelationSet\">\n"
147 " <arg direction=\"out\" type=\"a(ua(so))\"/>\n"
148 " <annotation value=\"QSpiRelationArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
149 " </method>\n"
150 " <method name=\"GetRole\">\n"
151 " <arg direction=\"out\" type=\"u\"/>\n"
152 " </method>\n"
153 " <method name=\"GetRoleName\">\n"
154 " <arg direction=\"out\" type=\"s\"/>\n"
155 " </method>\n"
156 " <method name=\"GetLocalizedRoleName\">\n"
157 " <arg direction=\"out\" type=\"s\"/>\n"
158 " </method>\n"
159 " <method name=\"GetState\">\n"
160 " <arg direction=\"out\" type=\"au\"/>\n"
161 " <annotation value=\"QSpiUIntList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
162 " </method>\n"
163 " <method name=\"GetAttributes\">\n"
164 " <arg direction=\"out\" type=\"a{ss}\"/>\n"
165 " <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
166 " </method>\n"
167 " <method name=\"GetApplication\">\n"
168 " <arg direction=\"out\" type=\"(so)\"/>\n"
169 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
170 " </method>\n"
171 " <method name=\"GetAccessibleId\">\n"
172 " <arg direction=\"out\" type=\"s\"/>\n"
173 " </method>\n"
174 " </interface>\n"
175 );
176
177 static const QLatin1StringView actionIntrospection(
178 " <interface name=\"org.a11y.atspi.Action\">\n"
179 " <property access=\"read\" type=\"i\" name=\"NActions\"/>\n"
180 " <method name=\"GetDescription\">\n"
181 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
182 " <arg direction=\"out\" type=\"s\"/>\n"
183 " </method>\n"
184 " <method name=\"GetName\">\n"
185 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
186 " <arg direction=\"out\" type=\"s\"/>\n"
187 " </method>\n"
188 " <method name=\"GetKeyBinding\">\n"
189 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
190 " <arg direction=\"out\" type=\"s\"/>\n"
191 " </method>\n"
192 " <method name=\"GetActions\">\n"
193 " <arg direction=\"out\" type=\"a(sss)\" name=\"index\"/>\n"
194 " <annotation value=\"QSpiActionArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
195 " </method>\n"
196 " <method name=\"DoAction\">\n"
197 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
198 " <arg direction=\"out\" type=\"b\"/>\n"
199 " </method>\n"
200 " </interface>\n"
201 );
202
203 static const QLatin1StringView applicationIntrospection(
204 " <interface name=\"org.a11y.atspi.Application\">\n"
205 " <property access=\"read\" type=\"s\" name=\"ToolkitName\"/>\n"
206 " <property access=\"read\" type=\"s\" name=\"Version\"/>\n"
207 " <property access=\"readwrite\" type=\"i\" name=\"Id\"/>\n"
208 " <method name=\"GetLocale\">\n"
209 " <arg direction=\"in\" type=\"u\" name=\"lctype\"/>\n"
210 " <arg direction=\"out\" type=\"s\"/>\n"
211 " </method>\n"
212 " <method name=\"GetApplicationBusAddress\">\n"
213 " <arg direction=\"out\" type=\"s\" name=\"address\"/>\n"
214 " </method>\n"
215 " </interface>\n"
216 );
217
218 static const QLatin1StringView componentIntrospection(
219 " <interface name=\"org.a11y.atspi.Component\">\n"
220 " <method name=\"Contains\">\n"
221 " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
222 " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
223 " <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
224 " <arg direction=\"out\" type=\"b\"/>\n"
225 " </method>\n"
226 " <method name=\"GetAccessibleAtPoint\">\n"
227 " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
228 " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
229 " <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
230 " <arg direction=\"out\" type=\"(so)\"/>\n"
231 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
232 " </method>\n"
233 " <method name=\"GetExtents\">\n"
234 " <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
235 " <arg direction=\"out\" type=\"(iiii)\"/>\n"
236 " <annotation value=\"QSpiRect\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
237 " </method>\n"
238 " <method name=\"GetPosition\">\n"
239 " <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
240 " <arg direction=\"out\" type=\"i\" name=\"x\"/>\n"
241 " <arg direction=\"out\" type=\"i\" name=\"y\"/>\n"
242 " </method>\n"
243 " <method name=\"GetSize\">\n"
244 " <arg direction=\"out\" type=\"i\" name=\"width\"/>\n"
245 " <arg direction=\"out\" type=\"i\" name=\"height\"/>\n"
246 " </method>\n"
247 " <method name=\"GetLayer\">\n"
248 " <arg direction=\"out\" type=\"u\"/>\n"
249 " </method>\n"
250 " <method name=\"GetMDIZOrder\">\n"
251 " <arg direction=\"out\" type=\"n\"/>\n"
252 " </method>\n"
253 " <method name=\"GrabFocus\">\n"
254 " <arg direction=\"out\" type=\"b\"/>\n"
255 " </method>\n"
256 " <method name=\"GetAlpha\">\n"
257 " <arg direction=\"out\" type=\"d\"/>\n"
258 " </method>\n"
259 " <method name=\"SetExtents\">\n"
260 " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
261 " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
262 " <arg direction=\"in\" type=\"i\" name=\"width\"/>\n"
263 " <arg direction=\"in\" type=\"i\" name=\"height\"/>\n"
264 " <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
265 " <arg direction=\"out\" type=\"b\"/>\n"
266 " </method>\n"
267 " <method name=\"SetPosition\">\n"
268 " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
269 " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
270 " <arg direction=\"in\" type=\"u\" name=\"coord_type\"/>\n"
271 " <arg direction=\"out\" type=\"b\"/>\n"
272 " </method>\n"
273 " <method name=\"SetSize\">\n"
274 " <arg direction=\"in\" type=\"i\" name=\"width\"/>\n"
275 " <arg direction=\"in\" type=\"i\" name=\"height\"/>\n"
276 " <arg direction=\"out\" type=\"b\"/>\n"
277 " </method>\n"
278 " </interface>\n"
279 );
280
281 static const QLatin1StringView editableTextIntrospection(
282 " <interface name=\"org.a11y.atspi.EditableText\">\n"
283 " <method name=\"SetTextContents\">\n"
284 " <arg direction=\"in\" type=\"s\" name=\"newContents\"/>\n"
285 " <arg direction=\"out\" type=\"b\"/>\n"
286 " </method>\n"
287 " <method name=\"InsertText\">\n"
288 " <arg direction=\"in\" type=\"i\" name=\"position\"/>\n"
289 " <arg direction=\"in\" type=\"s\" name=\"text\"/>\n"
290 " <arg direction=\"in\" type=\"i\" name=\"length\"/>\n"
291 " <arg direction=\"out\" type=\"b\"/>\n"
292 " </method>\n"
293 " <method name=\"CopyText\">\n"
294 " <arg direction=\"in\" type=\"i\" name=\"startPos\"/>\n"
295 " <arg direction=\"in\" type=\"i\" name=\"endPos\"/>\n"
296 " </method>\n"
297 " <method name=\"CutText\">\n"
298 " <arg direction=\"in\" type=\"i\" name=\"startPos\"/>\n"
299 " <arg direction=\"in\" type=\"i\" name=\"endPos\"/>\n"
300 " <arg direction=\"out\" type=\"b\"/>\n"
301 " </method>\n"
302 " <method name=\"DeleteText\">\n"
303 " <arg direction=\"in\" type=\"i\" name=\"startPos\"/>\n"
304 " <arg direction=\"in\" type=\"i\" name=\"endPos\"/>\n"
305 " <arg direction=\"out\" type=\"b\"/>\n"
306 " </method>\n"
307 " <method name=\"PasteText\">\n"
308 " <arg direction=\"in\" type=\"i\" name=\"position\"/>\n"
309 " <arg direction=\"out\" type=\"b\"/>\n"
310 " </method>\n"
311 " </interface>\n"
312 );
313
314 static const QLatin1StringView selectionIntrospection(
315 " <interface name=\"org.a11y.atspi.Selection\">\n"
316 " <property name=\"NSelectedChildren\" type=\"i\" access=\"read\"/>\n"
317 " <method name=\"GetSelectedChild\">\n"
318 " <arg direction=\"in\" name=\"selectedChildIndex\" type=\"i\"/>\n"
319 " <arg direction=\"out\" type=\"(so)\"/>\n"
320 " <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"QSpiObjectReference\"/>\n"
321 " </method>\n"
322 " <method name=\"SelectChild\">\n"
323 " <arg direction=\"in\" name=\"childIndex\" type=\"i\"/>\n"
324 " <arg direction=\"out\" type=\"b\"/>\n"
325 " </method>\n"
326 " <method name=\"DeselectSelectedChild\">\n"
327 " <arg direction=\"in\" name=\"selectedChildIndex\" type=\"i\"/>\n"
328 " <arg direction=\"out\" type=\"b\"/>\n"
329 " </method>\n"
330 " <method name=\"IsChildSelected\">\n"
331 " <arg direction=\"in\" name=\"childIndex\" type=\"i\"/>\n"
332 " <arg direction=\"out\" type=\"b\"/>\n"
333 " </method>\n"
334 " <method name=\"SelectAll\">\n"
335 " <arg direction=\"out\" type=\"b\"/>\n"
336 " </method>\n"
337 " <method name=\"ClearSelection\">\n"
338 " <arg direction=\"out\" type=\"b\"/>\n"
339 " </method>\n"
340 " <method name=\"DeselectChild\">\n"
341 " <arg direction=\"in\" name=\"childIndex\" type=\"i\"/>\n"
342 " <arg direction=\"out\" type=\"b\"/>\n"
343 " </method>\n"
344 " </interface>\n"
345 );
346
347 static const QLatin1StringView tableIntrospection(
348 " <interface name=\"org.a11y.atspi.Table\">\n"
349 " <property access=\"read\" type=\"i\" name=\"NRows\"/>\n"
350 " <property access=\"read\" type=\"i\" name=\"NColumns\"/>\n"
351 " <property access=\"read\" type=\"(so)\" name=\"Caption\">\n"
352 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName\"/>\n"
353 " </property>\n"
354 " <property access=\"read\" type=\"(so)\" name=\"Summary\">\n"
355 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName\"/>\n"
356 " </property>\n"
357 " <property access=\"read\" type=\"i\" name=\"NSelectedRows\"/>\n"
358 " <property access=\"read\" type=\"i\" name=\"NSelectedColumns\"/>\n"
359 " <method name=\"GetAccessibleAt\">\n"
360 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
361 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
362 " <arg direction=\"out\" type=\"(so)\"/>\n"
363 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
364 " </method>\n"
365 " <method name=\"GetIndexAt\">\n"
366 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
367 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
368 " <arg direction=\"out\" type=\"i\"/>\n"
369 " </method>\n"
370 " <method name=\"GetRowAtIndex\">\n"
371 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
372 " <arg direction=\"out\" type=\"i\"/>\n"
373 " </method>\n"
374 " <method name=\"GetColumnAtIndex\">\n"
375 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
376 " <arg direction=\"out\" type=\"i\"/>\n"
377 " </method>\n"
378 " <method name=\"GetRowDescription\">\n"
379 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
380 " <arg direction=\"out\" type=\"s\"/>\n"
381 " </method>\n"
382 " <method name=\"GetColumnDescription\">\n"
383 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
384 " <arg direction=\"out\" type=\"s\"/>\n"
385 " </method>\n"
386 " <method name=\"GetRowExtentAt\">\n"
387 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
388 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
389 " <arg direction=\"out\" type=\"i\"/>\n"
390 " </method>\n"
391 " <method name=\"GetColumnExtentAt\">\n"
392 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
393 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
394 " <arg direction=\"out\" type=\"i\"/>\n"
395 " </method>\n"
396 " <method name=\"GetRowHeader\">\n"
397 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
398 " <arg direction=\"out\" type=\"(so)\"/>\n"
399 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
400 " </method>\n"
401 " <method name=\"GetColumnHeader\">\n"
402 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
403 " <arg direction=\"out\" type=\"(so)\"/>\n"
404 " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
405 " </method>\n"
406 " <method name=\"GetSelectedRows\">\n"
407 " <arg direction=\"out\" type=\"ai\"/>\n"
408 " <annotation value=\"QSpiIntList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
409 " </method>\n"
410 " <method name=\"GetSelectedColumns\">\n"
411 " <arg direction=\"out\" type=\"ai\"/>\n"
412 " <annotation value=\"QSpiIntList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
413 " </method>\n"
414 " <method name=\"IsRowSelected\">\n"
415 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
416 " <arg direction=\"out\" type=\"b\"/>\n"
417 " </method>\n"
418 " <method name=\"IsColumnSelected\">\n"
419 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
420 " <arg direction=\"out\" type=\"b\"/>\n"
421 " </method>\n"
422 " <method name=\"IsSelected\">\n"
423 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
424 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
425 " <arg direction=\"out\" type=\"b\"/>\n"
426 " </method>\n"
427 " <method name=\"AddRowSelection\">\n"
428 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
429 " <arg direction=\"out\" type=\"b\"/>\n"
430 " </method>\n"
431 " <method name=\"AddColumnSelection\">\n"
432 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
433 " <arg direction=\"out\" type=\"b\"/>\n"
434 " </method>\n"
435 " <method name=\"RemoveRowSelection\">\n"
436 " <arg direction=\"in\" type=\"i\" name=\"row\"/>\n"
437 " <arg direction=\"out\" type=\"b\"/>\n"
438 " </method>\n"
439 " <method name=\"RemoveColumnSelection\">\n"
440 " <arg direction=\"in\" type=\"i\" name=\"column\"/>\n"
441 " <arg direction=\"out\" type=\"b\"/>\n"
442 " </method>\n"
443 " <method name=\"GetRowColumnExtentsAtIndex\">\n"
444 " <arg direction=\"in\" type=\"i\" name=\"index\"/>\n"
445 " <arg direction=\"out\" type=\"b\"/>\n"
446 " <arg direction=\"out\" type=\"i\" name=\"row\"/>\n"
447 " <arg direction=\"out\" type=\"i\" name=\"col\"/>\n"
448 " <arg direction=\"out\" type=\"i\" name=\"row_extents\"/>\n"
449 " <arg direction=\"out\" type=\"i\" name=\"col_extents\"/>\n"
450 " <arg direction=\"out\" type=\"b\" name=\"is_selected\"/>\n"
451 " </method>\n"
452 " </interface>\n"
453 );
454
455 static const QLatin1StringView tableCellIntrospection(
456 " <interface name=\"org.a11y.atspi.TableCell\">\n"
457 " <property access=\"read\" name=\"ColumnSpan\" type=\"i\" />\n"
458 " <property access=\"read\" name=\"Position\" type=\"(ii)\">\n"
459 " <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"QPoint\"/>\n"
460 " </property>\n"
461 " <property access=\"read\" name=\"RowSpan\" type=\"i\" />\n"
462 " <property access=\"read\" name=\"Table\" type=\"(so)\" >\n"
463 " <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"QSpiObjectReference\"/>\n"
464 " </property>\n"
465 " <method name=\"GetRowColumnSpan\">\n"
466 " <arg direction=\"out\" type=\"b\" />\n"
467 " <arg direction=\"out\" name=\"row\" type=\"i\" />\n"
468 " <arg direction=\"out\" name=\"col\" type=\"i\" />\n"
469 " <arg direction=\"out\" name=\"row_extents\" type=\"i\" />\n"
470 " <arg direction=\"out\" name=\"col_extents\" type=\"i\" />\n"
471 " </method>\n"
472 " <method name=\"GetColumnHeaderCells\">\n"
473 " <arg direction=\"out\" type=\"a(so)\"/>\n"
474 " <annotation value=\"QSpiObjectReferenceArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
475 " </method>\n"
476 " <method name=\"GetRowHeaderCells\">\n"
477 " <arg direction=\"out\" type=\"a(so)\"/>\n"
478 " <annotation value=\"QSpiObjectReferenceArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
479 " </method>\n"
480 " </interface>\n"
481 );
482
483 static const QLatin1StringView textIntrospection(
484 " <interface name=\"org.a11y.atspi.Text\">\n"
485 " <property access=\"read\" type=\"i\" name=\"CharacterCount\"/>\n"
486 " <property access=\"read\" type=\"i\" name=\"CaretOffset\"/>\n"
487 " <method name=\"GetStringAtOffset\">\n"
488 " <arg direction=\"in\" name=\"offset\" type=\"i\"/>\n"
489 " <arg direction=\"in\" name=\"granularity\" type=\"u\"/>\n"
490 " <arg direction=\"out\" type=\"s\"/>\n"
491 " <arg direction=\"out\" name=\"startOffset\" type=\"i\"/>\n"
492 " <arg direction=\"out\" name=\"endOffset\" type=\"i\"/>\n"
493 " </method>\n"
494 " <method name=\"GetText\">\n"
495 " <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
496 " <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
497 " <arg direction=\"out\" type=\"s\"/>\n"
498 " </method>\n"
499 " <method name=\"SetCaretOffset\">\n"
500 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
501 " <arg direction=\"out\" type=\"b\"/>\n"
502 " </method>\n"
503 " <method name=\"GetTextBeforeOffset\">\n"
504 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
505 " <arg direction=\"in\" type=\"u\" name=\"type\"/>\n"
506 " <arg direction=\"out\" type=\"s\"/>\n"
507 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
508 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
509 " </method>\n"
510 " <method name=\"GetTextAtOffset\">\n"
511 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
512 " <arg direction=\"in\" type=\"u\" name=\"type\"/>\n"
513 " <arg direction=\"out\" type=\"s\"/>\n"
514 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
515 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
516 " </method>\n"
517 " <method name=\"GetTextAfterOffset\">\n"
518 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
519 " <arg direction=\"in\" type=\"u\" name=\"type\"/>\n"
520 " <arg direction=\"out\" type=\"s\"/>\n"
521 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
522 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
523 " </method>\n"
524 " <method name=\"GetCharacterAtOffset\">\n"
525 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
526 " <arg direction=\"out\" type=\"i\"/>\n"
527 " </method>\n"
528 " <method name=\"GetAttributeValue\">\n"
529 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
530 " <arg direction=\"in\" type=\"s\" name=\"attributeName\"/>\n"
531 " <arg direction=\"out\" type=\"s\"/>\n"
532 " </method>\n"
533 " <method name=\"GetAttributes\">\n"
534 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
535 " <arg direction=\"out\" type=\"a{ss}\"/>\n"
536 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
537 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
538 " <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
539 " </method>\n"
540 " <method name=\"GetDefaultAttributes\">\n"
541 " <arg direction=\"out\" type=\"a{ss}\"/>\n"
542 " <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
543 " </method>\n"
544 " <method name=\"GetCharacterExtents\">\n"
545 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
546 " <arg direction=\"out\" type=\"i\" name=\"x\"/>\n"
547 " <arg direction=\"out\" type=\"i\" name=\"y\"/>\n"
548 " <arg direction=\"out\" type=\"i\" name=\"width\"/>\n"
549 " <arg direction=\"out\" type=\"i\" name=\"height\"/>\n"
550 " <arg direction=\"in\" type=\"u\" name=\"coordType\"/>\n"
551 " </method>\n"
552 " <method name=\"GetOffsetAtPoint\">\n"
553 " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
554 " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
555 " <arg direction=\"in\" type=\"u\" name=\"coordType\"/>\n"
556 " <arg direction=\"out\" type=\"i\"/>\n"
557 " </method>\n"
558 " <method name=\"GetNSelections\">\n"
559 " <arg direction=\"out\" type=\"i\"/>\n"
560 " </method>\n"
561 " <method name=\"GetSelection\">\n"
562 " <arg direction=\"in\" type=\"i\" name=\"selectionNum\"/>\n"
563 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
564 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
565 " </method>\n"
566 " <method name=\"AddSelection\">\n"
567 " <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
568 " <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
569 " <arg direction=\"out\" type=\"b\"/>\n"
570 " </method>\n"
571 " <method name=\"RemoveSelection\">\n"
572 " <arg direction=\"in\" type=\"i\" name=\"selectionNum\"/>\n"
573 " <arg direction=\"out\" type=\"b\"/>\n"
574 " </method>\n"
575 " <method name=\"SetSelection\">\n"
576 " <arg direction=\"in\" type=\"i\" name=\"selectionNum\"/>\n"
577 " <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
578 " <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
579 " <arg direction=\"out\" type=\"b\"/>\n"
580 " </method>\n"
581 " <method name=\"GetRangeExtents\">\n"
582 " <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n"
583 " <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n"
584 " <arg direction=\"out\" type=\"i\" name=\"x\"/>\n"
585 " <arg direction=\"out\" type=\"i\" name=\"y\"/>\n"
586 " <arg direction=\"out\" type=\"i\" name=\"width\"/>\n"
587 " <arg direction=\"out\" type=\"i\" name=\"height\"/>\n"
588 " <arg direction=\"in\" type=\"u\" name=\"coordType\"/>\n"
589 " </method>\n"
590 " <method name=\"GetBoundedRanges\">\n"
591 " <arg direction=\"in\" type=\"i\" name=\"x\"/>\n"
592 " <arg direction=\"in\" type=\"i\" name=\"y\"/>\n"
593 " <arg direction=\"in\" type=\"i\" name=\"width\"/>\n"
594 " <arg direction=\"in\" type=\"i\" name=\"height\"/>\n"
595 " <arg direction=\"in\" type=\"u\" name=\"coordType\"/>\n"
596 " <arg direction=\"in\" type=\"u\" name=\"xClipType\"/>\n"
597 " <arg direction=\"in\" type=\"u\" name=\"yClipType\"/>\n"
598 " <arg direction=\"out\" type=\"a(iisv)\"/>\n"
599 " <annotation value=\"QSpiRangeList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
600 " </method>\n"
601 " <method name=\"GetAttributeRun\">\n"
602 " <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
603 " <arg direction=\"in\" type=\"b\" name=\"includeDefaults\"/>\n"
604 " <arg direction=\"out\" type=\"a{ss}\"/>\n"
605 " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
606 " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
607 " <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
608 " </method>\n"
609 " <method name=\"GetDefaultAttributeSet\">\n"
610 " <arg direction=\"out\" type=\"a{ss}\"/>\n"
611 " <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
612 " </method>\n"
613 " <method name=\"ScrollSubstringTo\">\n"
614 " <arg direction=\"in\" name=\"startOffset\" type=\"i\"/>\n"
615 " <arg direction=\"in\" name=\"endOffset\" type=\"i\"/>\n"
616 " <arg direction=\"in\" name=\"type\" type=\"u\"/>\n"
617 " <arg direction=\"out\" type=\"b\"/>\n"
618 " </method>\n"
619 " </interface>\n"
620 );
621
622 static const QLatin1StringView valueIntrospection(
623 " <interface name=\"org.a11y.atspi.Value\">\n"
624 " <property access=\"read\" type=\"d\" name=\"MinimumValue\"/>\n"
625 " <property access=\"read\" type=\"d\" name=\"MaximumValue\"/>\n"
626 " <property access=\"read\" type=\"d\" name=\"MinimumIncrement\"/>\n"
627 " <property access=\"readwrite\" type=\"d\" name=\"CurrentValue\"/>\n"
628 " <method name=\"SetCurrentValue\">\n"
629 " <arg direction=\"in\" type=\"d\" name=\"value\"/>\n"
630 " </method>\n"
631 " </interface>\n"
632 );
633
634 QAccessibleInterface * interface = interfaceFromPath(path);
635 if (!interface) {
636 qCWarning(lcAccessibilityAtspi) << "Could not find accessible on path:" << path;
637 return QString();
638 }
639
640 QStringList interfaces = accessibleInterfaces(interface);
641
642 QString xml;
643 xml.append(accessibleIntrospection);
644
645 if (interfaces.contains(ATSPI_DBUS_INTERFACE_COMPONENT ""_L1))
646 xml.append(componentIntrospection);
647 if (interfaces.contains(ATSPI_DBUS_INTERFACE_TEXT ""_L1))
648 xml.append(textIntrospection);
649 if (interfaces.contains(ATSPI_DBUS_INTERFACE_EDITABLE_TEXT ""_L1))
650 xml.append(editableTextIntrospection);
651 if (interfaces.contains(ATSPI_DBUS_INTERFACE_ACTION ""_L1))
652 xml.append(actionIntrospection);
653 if (interfaces.contains(ATSPI_DBUS_INTERFACE_SELECTION ""_L1))
654 xml.append(selectionIntrospection);
655 if (interfaces.contains(ATSPI_DBUS_INTERFACE_TABLE ""_L1))
656 xml.append(tableIntrospection);
657 if (interfaces.contains(ATSPI_DBUS_INTERFACE_TABLE_CELL ""_L1))
658 xml.append(tableCellIntrospection);
659 if (interfaces.contains(ATSPI_DBUS_INTERFACE_VALUE ""_L1))
660 xml.append(valueIntrospection);
661 if (path == QSPI_OBJECT_PATH_ROOT ""_L1)
662 xml.append(applicationIntrospection);
663
664 return xml;
665}
666
667void AtSpiAdaptor::setBitFlag(const QString &flag)
668{
669 Q_ASSERT(flag.size());
670
671 // assume we don't get nonsense - look at first letter only
672 switch (flag.at(0).toLower().toLatin1()) {
673 case 'o': {
674 if (flag.size() <= 8) { // Object::
675 sendObject = 1;
676 } else { // Object:Foo:Bar
677 QString right = flag.mid(7);
678 if (false) {
679 } else if (right.startsWith("ActiveDescendantChanged"_L1)) {
680 sendObject_active_descendant_changed = 1;
681 } else if (right.startsWith("AttributesChanged"_L1)) {
682 sendObject_attributes_changed = 1;
683 } else if (right.startsWith("BoundsChanged"_L1)) {
684 sendObject_bounds_changed = 1;
685 } else if (right.startsWith("ChildrenChanged"_L1)) {
686 sendObject_children_changed = 1;
687 } else if (right.startsWith("ColumnDeleted"_L1)) {
688 sendObject_column_deleted = 1;
689 } else if (right.startsWith("ColumnInserted"_L1)) {
690 sendObject_column_inserted = 1;
691 } else if (right.startsWith("ColumnReordered"_L1)) {
692 sendObject_column_reordered = 1;
693 } else if (right.startsWith("LinkSelected"_L1)) {
694 sendObject_link_selected = 1;
695 } else if (right.startsWith("ModelChanged"_L1)) {
696 sendObject_model_changed = 1;
697 } else if (right.startsWith("PropertyChange"_L1)) {
698 if (right == "PropertyChange:AccessibleDescription"_L1) {
699 sendObject_property_change_accessible_description = 1;
700 } else if (right == "PropertyChange:AccessibleName"_L1) {
701 sendObject_property_change_accessible_name = 1;
702 } else if (right == "PropertyChange:AccessibleParent"_L1) {
703 sendObject_property_change_accessible_parent = 1;
704 } else if (right == "PropertyChange:AccessibleRole"_L1) {
705 sendObject_property_change_accessible_role = 1;
706 } else if (right == "PropertyChange:TableCaption"_L1) {
707 sendObject_property_change_accessible_table_caption = 1;
708 } else if (right == "PropertyChange:TableColumnDescription"_L1) {
709 sendObject_property_change_accessible_table_column_description = 1;
710 } else if (right == "PropertyChange:TableColumnHeader"_L1) {
711 sendObject_property_change_accessible_table_column_header = 1;
712 } else if (right == "PropertyChange:TableRowDescription"_L1) {
713 sendObject_property_change_accessible_table_row_description = 1;
714 } else if (right == "PropertyChange:TableRowHeader"_L1) {
715 sendObject_property_change_accessible_table_row_header = 1;
716 } else if (right == "PropertyChange:TableSummary"_L1) {
717 sendObject_property_change_accessible_table_summary = 1;
718 } else if (right == "PropertyChange:AccessibleValue"_L1) {
719 sendObject_property_change_accessible_value = 1;
720 } else {
721 sendObject_property_change = 1;
722 }
723 } else if (right.startsWith("RowDeleted"_L1)) {
724 sendObject_row_deleted = 1;
725 } else if (right.startsWith("RowInserted"_L1)) {
726 sendObject_row_inserted = 1;
727 } else if (right.startsWith("RowReordered"_L1)) {
728 sendObject_row_reordered = 1;
729 } else if (right.startsWith("SelectionChanged"_L1)) {
730 sendObject_selection_changed = 1;
731 } else if (right.startsWith("StateChanged"_L1)) {
732 sendObject_state_changed = 1;
733 } else if (right.startsWith("TextAttributesChanged"_L1)) {
734 sendObject_text_attributes_changed = 1;
735 } else if (right.startsWith("TextBoundsChanged"_L1)) {
736 sendObject_text_bounds_changed = 1;
737 } else if (right.startsWith("TextCaretMoved"_L1)) {
738 sendObject_text_caret_moved = 1;
739 } else if (right.startsWith("TextChanged"_L1)) {
740 sendObject_text_changed = 1;
741 } else if (right.startsWith("TextSelectionChanged"_L1)) {
742 sendObject_text_selection_changed = 1;
743 } else if (right.startsWith("ValueChanged"_L1)) {
744 sendObject_value_changed = 1;
745 } else if (right.startsWith("VisibleDataChanged"_L1)
746 || right.startsWith("VisibledataChanged"_L1)) { // typo in libatspi
747 sendObject_visible_data_changed = 1;
748 } else {
749 qCWarning(lcAccessibilityAtspi) << "Subscription string not handled:" << flag;
750 }
751 }
752 break;
753 }
754 case 'w': { // window
755 if (flag.size() <= 8) {
756 sendWindow = 1;
757 } else { // object:Foo:Bar
758 QString right = flag.mid(7);
759 if (false) {
760 } else if (right.startsWith("Activate"_L1)) {
761 sendWindow_activate = 1;
762 } else if (right.startsWith("Close"_L1)) {
763 sendWindow_close= 1;
764 } else if (right.startsWith("Create"_L1)) {
765 sendWindow_create = 1;
766 } else if (right.startsWith("Deactivate"_L1)) {
767 sendWindow_deactivate = 1;
768 } else if (right.startsWith("Lower"_L1)) {
769 sendWindow_lower = 1;
770 } else if (right.startsWith("Maximize"_L1)) {
771 sendWindow_maximize = 1;
772 } else if (right.startsWith("Minimize"_L1)) {
773 sendWindow_minimize = 1;
774 } else if (right.startsWith("Move"_L1)) {
775 sendWindow_move = 1;
776 } else if (right.startsWith("Raise"_L1)) {
777 sendWindow_raise = 1;
778 } else if (right.startsWith("Reparent"_L1)) {
779 sendWindow_reparent = 1;
780 } else if (right.startsWith("Resize"_L1)) {
781 sendWindow_resize = 1;
782 } else if (right.startsWith("Restore"_L1)) {
783 sendWindow_restore = 1;
784 } else if (right.startsWith("Restyle"_L1)) {
785 sendWindow_restyle = 1;
786 } else if (right.startsWith("Shade"_L1)) {
787 sendWindow_shade = 1;
788 } else if (right.startsWith("Unshade"_L1)) {
789 sendWindow_unshade = 1;
790 } else if (right.startsWith("DesktopCreate"_L1)) {
791 // ignore this one
792 } else if (right.startsWith("DesktopDestroy"_L1)) {
793 // ignore this one
794 } else {
795 qCWarning(lcAccessibilityAtspi) << "Subscription string not handled:" << flag;
796 }
797 }
798 break;
799 }
800 case 'f': {
801 sendFocus = 1;
802 break;
803 }
804 case 'd': { // document is not implemented
805 break;
806 }
807 case 't': { // terminal is not implemented
808 break;
809 }
810 case 'm': { // mouse* is handled in a different way by the gnome atspi stack
811 break;
812 }
813 default:
814 qCWarning(lcAccessibilityAtspi) << "Subscription string not handled:" << flag;
815 }
816}
817
821void AtSpiAdaptor::updateEventListeners()
822{
823 QDBusMessage m = QDBusMessage::createMethodCall("org.a11y.atspi.Registry"_L1,
824 "/org/a11y/atspi/registry"_L1,
825 "org.a11y.atspi.Registry"_L1, "GetRegisteredEvents"_L1);
826 QDBusReply<QSpiEventListenerArray> listenersReply = m_dbus->connection().call(m);
827 if (listenersReply.isValid()) {
828 const QSpiEventListenerArray evList = listenersReply.value();
829 for (const QSpiEventListener &ev : evList)
830 setBitFlag(ev.eventName);
831 m_applicationAdaptor->sendEvents(!evList.isEmpty());
832 } else {
833 qCDebug(lcAccessibilityAtspi) << "Could not query active accessibility event listeners.";
834 }
835}
836
837void AtSpiAdaptor::eventListenerDeregistered(const QString &/*bus*/, const QString &/*path*/)
838{
839// qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::eventListenerDeregistered: " << bus << path;
840 updateEventListeners();
841}
842
843void AtSpiAdaptor::eventListenerRegistered(const QString &/*bus*/, const QString &/*path*/)
844{
845// qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::eventListenerRegistered: " << bus << path;
846 updateEventListeners();
847}
848
854{
855 if (!(sendWindow || sendWindow_activate))
856 return;
857
858 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window);
859 // If the window has been quickly activated or disabled, it will cause a crash.
860 if (iface == nullptr)
861 return;
862 Q_ASSERT(!active || iface->isValid());
863
865 // in dtor it may be invalid
866 if (iface->isValid())
867 windowTitle = iface->text(QAccessible::Name);
868
870 data.setVariant(windowTitle);
871
872 QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(data));
873
874 QString status = active ? "Activate"_L1 : "Deactivate"_L1;
875 QString path = pathForObject(window);
876 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_WINDOW ""_L1, status, args);
877
878 QVariantList stateArgs = packDBusSignalArguments("active"_L1, active ? 1 : 0, 0, variantForPath(path));
879 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "StateChanged"_L1, stateArgs);
880}
881
882QVariantList AtSpiAdaptor::packDBusSignalArguments(const QString &type, int data1, int data2, const QVariant &variantData) const
883{
885 arguments << type << data1 << data2 << variantData
887 return arguments;
888}
889
890QVariant AtSpiAdaptor::variantForPath(const QString &path) const
891{
895}
896
897bool AtSpiAdaptor::sendDBusSignal(const QString &path, const QString &interface, const QString &signalName, const QVariantList &arguments) const
898{
900 message.setArguments(arguments);
901 return m_dbus->connection().send(message);
902}
903
904QAccessibleInterface *AtSpiAdaptor::interfaceFromPath(const QString& dbusPath) const
905{
906 if (dbusPath == QSPI_OBJECT_PATH_ROOT ""_L1)
907 return QAccessible::queryAccessibleInterface(qApp);
908
909 QStringList parts = dbusPath.split(u'/');
910 if (parts.size() != 6) {
911 qCDebug(lcAccessibilityAtspi) << "invalid path: " << dbusPath;
912 return nullptr;
913 }
914
915 QString objectString = parts.at(5);
916 QAccessible::Id id = objectString.toUInt();
917
918 // The id is always in the range [INT_MAX+1, UINT_MAX]
919 if ((int)id >= 0)
920 qCWarning(lcAccessibilityAtspi) << "No accessible object found for id: " << id;
921
922 return QAccessible::accessibleInterface(id);
923}
924
925void AtSpiAdaptor::notifyStateChange(QAccessibleInterface *interface, const QString &state, int value)
926{
927 QString path = pathForInterface(interface);
928 QVariantList stateArgs = packDBusSignalArguments(state, value, 0, variantForPath(path));
929 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "StateChanged"_L1, stateArgs);
930}
931
932
936void AtSpiAdaptor::notify(QAccessibleEvent *event)
937{
938 switch (event->type()) {
939 case QAccessible::ObjectCreated:
940 if (sendObject || sendObject_children_changed)
941 notifyAboutCreation(event->accessibleInterface());
942 break;
943 case QAccessible::ObjectShow: {
944 if (sendObject || sendObject_state_changed) {
945 notifyStateChange(event->accessibleInterface(), "showing"_L1, 1);
946 }
947 break;
948 }
949 case QAccessible::ObjectHide: {
950 if (sendObject || sendObject_state_changed) {
951 notifyStateChange(event->accessibleInterface(), "showing"_L1, 0);
952 }
953 break;
954 }
955 case QAccessible::ObjectDestroyed: {
956 if (sendObject || sendObject_state_changed)
957 notifyAboutDestruction(event->accessibleInterface());
958 break;
959 }
960 case QAccessible::ObjectReorder: {
961 if (sendObject || sendObject_children_changed)
962 childrenChanged(event->accessibleInterface());
963 break;
964 }
965 case QAccessible::NameChanged: {
966 if (sendObject || sendObject_property_change || sendObject_property_change_accessible_name) {
967 QAccessibleInterface *iface = event->accessibleInterface();
968 if (!iface) {
969 qCDebug(lcAccessibilityAtspi,
970 "NameChanged event from invalid accessible.");
971 return;
972 }
973
974 QString path = pathForInterface(iface);
975 QVariantList args = packDBusSignalArguments(
976 "accessible-name"_L1, 0, 0,
977 QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Name))));
978 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
979 "PropertyChange"_L1, args);
980 }
981 break;
982 }
983 case QAccessible::DescriptionChanged: {
984 if (sendObject || sendObject_property_change || sendObject_property_change_accessible_description) {
985 QAccessibleInterface *iface = event->accessibleInterface();
986 if (!iface) {
987 qCDebug(lcAccessibilityAtspi,
988 "DescriptionChanged event from invalid accessible.");
989 return;
990 }
991
992 QString path = pathForInterface(iface);
993 QVariantList args = packDBusSignalArguments(
994 "accessible-description"_L1, 0, 0,
995 QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Description))));
996 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
997 "PropertyChange"_L1, args);
998 }
999 break;
1000 }
1001 case QAccessible::Focus: {
1002 if (sendFocus || sendObject || sendObject_state_changed)
1003 sendFocusChanged(event->accessibleInterface());
1004 break;
1005 }
1006 case QAccessible::TextInserted:
1007 case QAccessible::TextRemoved:
1008 case QAccessible::TextUpdated: {
1009 if (sendObject || sendObject_text_changed) {
1010 QAccessibleInterface * iface = event->accessibleInterface();
1011 if (!iface || !iface->textInterface()) {
1012 qCWarning(lcAccessibilityAtspi) << "Received text event for invalid interface.";
1013 return;
1014 }
1015 QString path = pathForInterface(iface);
1016
1017 int changePosition = 0;
1018 int cursorPosition = 0;
1019 QString textRemoved;
1020 QString textInserted;
1021
1022 if (event->type() == QAccessible::TextInserted) {
1023 QAccessibleTextInsertEvent *textEvent = static_cast<QAccessibleTextInsertEvent*>(event);
1024 textInserted = textEvent->textInserted();
1025 changePosition = textEvent->changePosition();
1026 cursorPosition = textEvent->cursorPosition();
1027 } else if (event->type() == QAccessible::TextRemoved) {
1028 QAccessibleTextRemoveEvent *textEvent = static_cast<QAccessibleTextRemoveEvent*>(event);
1029 textRemoved = textEvent->textRemoved();
1030 changePosition = textEvent->changePosition();
1031 cursorPosition = textEvent->cursorPosition();
1032 } else if (event->type() == QAccessible::TextUpdated) {
1033 QAccessibleTextUpdateEvent *textEvent = static_cast<QAccessibleTextUpdateEvent*>(event);
1034 textInserted = textEvent->textInserted();
1035 textRemoved = textEvent->textRemoved();
1036 changePosition = textEvent->changePosition();
1037 cursorPosition = textEvent->cursorPosition();
1038 }
1039
1041
1042 if (!textRemoved.isEmpty()) {
1043 data.setVariant(QVariant::fromValue(textRemoved));
1044 QVariantList args = packDBusSignalArguments("delete"_L1, changePosition, textRemoved.size(), QVariant::fromValue(data));
1045 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
1046 "TextChanged"_L1, args);
1047 }
1048
1049 if (!textInserted.isEmpty()) {
1050 data.setVariant(QVariant::fromValue(textInserted));
1051 QVariantList args = packDBusSignalArguments("insert"_L1, changePosition, textInserted.size(), QVariant::fromValue(data));
1052 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
1053 "TextChanged"_L1, args);
1054 }
1055
1056 // send a cursor update
1057 Q_UNUSED(cursorPosition);
1058// QDBusVariant cursorData;
1059// cursorData.setVariant(QVariant::fromValue(cursorPosition));
1060// QVariantList args = packDBusSignalArguments(QString(), cursorPosition, 0, QVariant::fromValue(cursorData));
1061// sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
1062// "TextCaretMoved"_L1, args);
1063 }
1064 break;
1065 }
1066 case QAccessible::TextCaretMoved: {
1067 if (sendObject || sendObject_text_caret_moved) {
1068 QAccessibleInterface * iface = event->accessibleInterface();
1069 if (!iface || !iface->textInterface()) {
1070 qCWarning(lcAccessibilityAtspi) << "Sending TextCaretMoved from object that does not implement text interface: " << iface;
1071 return;
1072 }
1073
1074 QString path = pathForInterface(iface);
1075 QDBusVariant cursorData;
1076 int pos = iface->textInterface()->cursorPosition();
1077 cursorData.setVariant(QVariant::fromValue(pos));
1078 QVariantList args = packDBusSignalArguments(QString(), pos, 0, QVariant::fromValue(cursorData));
1079 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
1080 "TextCaretMoved"_L1, args);
1081 }
1082 break;
1083 }
1084 case QAccessible::TextSelectionChanged: {
1085 if (sendObject || sendObject_text_selection_changed) {
1086 QAccessibleInterface * iface = event->accessibleInterface();
1087 QString path = pathForInterface(iface);
1088 QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
1089 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
1090 "TextSelectionChanged"_L1, args);
1091 }
1092 break;
1093 }
1094 case QAccessible::ValueChanged: {
1095 if (sendObject || sendObject_value_changed || sendObject_property_change_accessible_value) {
1096 QAccessibleInterface * iface = event->accessibleInterface();
1097 if (!iface) {
1098 qCWarning(lcAccessibilityAtspi) << "ValueChanged event from invalid accessible.";
1099 return;
1100 }
1101 if (iface->valueInterface()) {
1102 QString path = pathForInterface(iface);
1103 QVariantList args = packDBusSignalArguments("accessible-value"_L1, 0, 0, variantForPath(path));
1104 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
1105 "PropertyChange"_L1, args);
1106 } else if (iface->role() == QAccessible::ComboBox) {
1107 // Combo Box with AT-SPI likes to be special
1108 // It requires a name-change to update caches and then selection-changed
1109 QString path = pathForInterface(iface);
1110 QVariantList args1 = packDBusSignalArguments(
1111 "accessible-name"_L1, 0, 0,
1112 QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Name))));
1113 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
1114 "PropertyChange"_L1, args1);
1115 QVariantList args2 = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(0))));
1116 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
1117 "SelectionChanged"_L1, args2);
1118 } else {
1119 qCWarning(lcAccessibilityAtspi) << "ValueChanged event and no ValueInterface or ComboBox: " << iface;
1120 }
1121 }
1122 break;
1123 }
1124 case QAccessible::SelectionAdd:
1125 case QAccessible::SelectionRemove:
1126 case QAccessible::Selection: {
1127 QAccessibleInterface * iface = event->accessibleInterface();
1128 if (!iface) {
1129 qCWarning(lcAccessibilityAtspi) << "Selection event from invalid accessible.";
1130 return;
1131 }
1132 // send event for change of selected state for the interface itself
1133 QString path = pathForInterface(iface);
1134 int selected = iface->state().selected ? 1 : 0;
1135 QVariantList stateArgs = packDBusSignalArguments("selected"_L1, selected, 0, variantForPath(path));
1136 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "StateChanged"_L1, stateArgs);
1137
1138 // send SelectionChanged event for the parent
1139 QAccessibleInterface* parent = iface->parent();
1140 if (!parent) {
1141 qCDebug(lcAccessibilityAtspi) << "No valid parent in selection event.";
1142 return;
1143 }
1144
1145 QString parentPath = pathForInterface(parent);
1146 QVariantList args = packDBusSignalArguments(QString(), 0, 0, variantForPath(parentPath));
1147 sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT),
1148 QLatin1String("SelectionChanged"), args);
1149 break;
1150 }
1151 case QAccessible::SelectionWithin: {
1152 QAccessibleInterface * iface = event->accessibleInterface();
1153 if (!iface) {
1154 qCWarning(lcAccessibilityAtspi) << "SelectionWithin event from invalid accessible.";
1155 return;
1156 }
1157
1158 QString path = pathForInterface(iface);
1159 QVariantList args = packDBusSignalArguments(QString(), 0, 0, variantForPath(path));
1160 sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("SelectionChanged"), args);
1161 break;
1162 }
1163 case QAccessible::StateChanged: {
1164 if (sendObject || sendObject_state_changed || sendWindow || sendWindow_activate) {
1165 QAccessible::State stateChange = static_cast<QAccessibleStateChangeEvent*>(event)->changedStates();
1166 if (stateChange.checked) {
1167 QAccessibleInterface * iface = event->accessibleInterface();
1168 if (!iface) {
1169 qCWarning(lcAccessibilityAtspi) << "StateChanged event from invalid accessible.";
1170 return;
1171 }
1172 int checked = iface->state().checked;
1173 notifyStateChange(iface, "checked"_L1, checked);
1174 } else if (stateChange.active) {
1175 QAccessibleInterface * iface = event->accessibleInterface();
1176 if (!iface || !(iface->role() == QAccessible::Window && (sendWindow || sendWindow_activate)))
1177 return;
1178 int isActive = iface->state().active;
1179 QString windowTitle = iface->text(QAccessible::Name);
1181 data.setVariant(windowTitle);
1182 QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(data));
1183 QString status = isActive ? "Activate"_L1 : "Deactivate"_L1;
1184 QString path = pathForInterface(iface);
1185 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_WINDOW ""_L1, status, args);
1186 notifyStateChange(iface, "active"_L1, isActive);
1187 } else if (stateChange.disabled) {
1188 QAccessibleInterface *iface = event->accessibleInterface();
1189 QAccessible::State state = iface->state();
1190 bool enabled = !state.disabled;
1191
1192 notifyStateChange(iface, "enabled"_L1, enabled);
1193 notifyStateChange(iface, "sensitive"_L1, enabled);
1194 } else if (stateChange.focused) {
1195 QAccessibleInterface *iface = event->accessibleInterface();
1196 QAccessible::State state = iface->state();
1197 bool focused = state.focused;
1198 notifyStateChange(iface, "focused"_L1, focused);
1199 }
1200 }
1201 break;
1202 }
1203 case QAccessible::TableModelChanged: {
1204 QAccessibleInterface *interface = event->accessibleInterface();
1205 if (!interface || !interface->isValid()) {
1206 qCWarning(lcAccessibilityAtspi) << "TableModelChanged event from invalid accessible.";
1207 return;
1208 }
1209
1210 const QString path = pathForInterface(interface);
1211 QAccessibleTableModelChangeEvent *tableModelEvent = static_cast<QAccessibleTableModelChangeEvent*>(event);
1212 switch (tableModelEvent->modelChangeType()) {
1213 case QAccessibleTableModelChangeEvent::ColumnsInserted: {
1214 if (sendObject || sendObject_column_inserted) {
1215 const int firstColumn = tableModelEvent->firstColumn();
1216 const int insertedColumnCount = tableModelEvent->lastColumn() - firstColumn + 1;
1217 QVariantList args = packDBusSignalArguments(QString(), firstColumn, insertedColumnCount, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
1218 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "ColumnInserted"_L1, args);
1219 }
1220 break;
1221 }
1222 case QAccessibleTableModelChangeEvent::ColumnsRemoved: {
1223 if (sendObject || sendObject_column_deleted) {
1224 const int firstColumn = tableModelEvent->firstColumn();
1225 const int removedColumnCount = tableModelEvent->lastColumn() - firstColumn + 1;
1226 QVariantList args = packDBusSignalArguments(QString(), firstColumn, removedColumnCount, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
1227 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "ColumnDeleted"_L1, args);
1228 }
1229 break;
1230 }
1231 case QAccessibleTableModelChangeEvent::RowsInserted: {
1232 if (sendObject || sendObject_row_inserted) {
1233 const int firstRow = tableModelEvent->firstRow();
1234 const int insertedRowCount = tableModelEvent->lastRow() - firstRow + 1;
1235 QVariantList args = packDBusSignalArguments(QString(), firstRow, insertedRowCount, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
1236 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "RowInserted"_L1, args);
1237 }
1238 break;
1239 }
1240 case QAccessibleTableModelChangeEvent::RowsRemoved: {
1241 if (sendObject || sendObject_row_deleted) {
1242 const int firstRow = tableModelEvent->firstRow();
1243 const int removedRowCount = tableModelEvent->lastRow() - firstRow + 1;
1244 QVariantList args = packDBusSignalArguments(QString(), firstRow, removedRowCount, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
1245 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "RowDeleted"_L1, args);
1246 }
1247 break;
1248 }
1249 case QAccessibleTableModelChangeEvent::ModelChangeType::ModelReset: {
1250 if (sendObject || sendObject_model_changed) {
1251 QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
1252 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "ModelChanged"_L1, args);
1253 }
1254 break;
1255 }
1256 case QAccessibleTableModelChangeEvent::DataChanged: {
1257 if (sendObject || sendObject_visible_data_changed) {
1258 QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(QString()))));
1259 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "VisibleDataChanged"_L1, args);
1260 }
1261 break;
1262 }
1263 }
1264 break;
1265 }
1266
1267 // For now we ignore these events
1268 case QAccessible::ParentChanged:
1269 case QAccessible::DialogStart:
1270 case QAccessible::DialogEnd:
1271 case QAccessible::PopupMenuStart:
1272 case QAccessible::PopupMenuEnd:
1273 case QAccessible::SoundPlayed:
1274 case QAccessible::Alert:
1275 case QAccessible::ForegroundChanged:
1276 case QAccessible::MenuStart:
1277 case QAccessible::MenuEnd:
1278 case QAccessible::ContextHelpStart:
1279 case QAccessible::ContextHelpEnd:
1280 case QAccessible::DragDropStart:
1281 case QAccessible::DragDropEnd:
1282 case QAccessible::ScrollingStart:
1283 case QAccessible::ScrollingEnd:
1284 case QAccessible::MenuCommand:
1285 case QAccessible::ActionChanged:
1286 case QAccessible::ActiveDescendantChanged:
1287 case QAccessible::AttributeChanged:
1288 case QAccessible::DocumentContentChanged:
1289 case QAccessible::DocumentLoadComplete:
1290 case QAccessible::DocumentLoadStopped:
1291 case QAccessible::DocumentReload:
1292 case QAccessible::HyperlinkEndIndexChanged:
1293 case QAccessible::HyperlinkNumberOfAnchorsChanged:
1294 case QAccessible::HyperlinkSelectedLinkChanged:
1295 case QAccessible::HypertextLinkActivated:
1296 case QAccessible::HypertextLinkSelected:
1297 case QAccessible::HyperlinkStartIndexChanged:
1298 case QAccessible::HypertextChanged:
1299 case QAccessible::HypertextNLinksChanged:
1300 case QAccessible::ObjectAttributeChanged:
1301 case QAccessible::PageChanged:
1302 case QAccessible::SectionChanged:
1303 case QAccessible::TableCaptionChanged:
1304 case QAccessible::TableColumnDescriptionChanged:
1305 case QAccessible::TableColumnHeaderChanged:
1306 case QAccessible::TableRowDescriptionChanged:
1307 case QAccessible::TableRowHeaderChanged:
1308 case QAccessible::TableSummaryChanged:
1309 case QAccessible::TextAttributeChanged:
1310 case QAccessible::TextColumnChanged:
1311 case QAccessible::VisibleDataChanged:
1312 case QAccessible::LocationChanged:
1313 case QAccessible::HelpChanged:
1314 case QAccessible::DefaultActionChanged:
1315 case QAccessible::AcceleratorChanged:
1316 case QAccessible::InvalidEvent:
1317 break;
1318 }
1319}
1320
1321void AtSpiAdaptor::sendFocusChanged(QAccessibleInterface *interface) const
1322{
1323 static QString lastFocusPath;
1324 // "remove" old focus
1325 if (!lastFocusPath.isEmpty()) {
1326 QVariantList stateArgs = packDBusSignalArguments("focused"_L1, 0, 0, variantForPath(lastFocusPath));
1327 sendDBusSignal(lastFocusPath, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
1328 "StateChanged"_L1, stateArgs);
1329 }
1330 // send new focus
1331 {
1332 QString path = pathForInterface(interface);
1333
1334 QVariantList stateArgs = packDBusSignalArguments("focused"_L1, 1, 0, variantForPath(path));
1335 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1,
1336 "StateChanged"_L1, stateArgs);
1337
1338 QVariantList focusArgs = packDBusSignalArguments(QString(), 0, 0, variantForPath(path));
1339 sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_FOCUS ""_L1, "Focus"_L1, focusArgs);
1340 lastFocusPath = path;
1341 }
1342}
1343
1344void AtSpiAdaptor::childrenChanged(QAccessibleInterface *interface) const
1345{
1346 QString parentPath = pathForInterface(interface);
1347 int childCount = interface->childCount();
1348 for (int i = 0; i < interface->childCount(); ++i) {
1349 QString childPath = pathForInterface(interface->child(i));
1350 QVariantList args = packDBusSignalArguments("add"_L1, childCount, 0, childPath);
1351 sendDBusSignal(parentPath, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "ChildrenChanged"_L1, args);
1352 }
1353}
1354
1355void AtSpiAdaptor::notifyAboutCreation(QAccessibleInterface *interface) const
1356{
1357 // notify about the new child of our parent
1358 QAccessibleInterface * parent = interface->parent();
1359 if (!parent) {
1360 qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::notifyAboutCreation: Could not find parent for " << interface->object();
1361 return;
1362 }
1363 QString path = pathForInterface(interface);
1364 int childCount = parent->childCount();
1365 QString parentPath = pathForInterface(parent);
1366 QVariantList args = packDBusSignalArguments("add"_L1, childCount, 0, variantForPath(path));
1367 sendDBusSignal(parentPath, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "ChildrenChanged"_L1, args);
1368}
1369
1370void AtSpiAdaptor::notifyAboutDestruction(QAccessibleInterface *interface) const
1371{
1372 if (!interface || !interface->isValid())
1373 return;
1374
1375 QAccessibleInterface * parent = interface->parent();
1376 if (!parent) {
1377 qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::notifyAboutDestruction: Could not find parent for " << interface->object();
1378 return;
1379 }
1380 QString path = pathForInterface(interface);
1381
1382 // this is in the destructor. we have no clue which child we used to be.
1383 // FIXME
1384 int childIndex = -1;
1385 // if (child) {
1386 // childIndex = child;
1387 // } else {
1388 // childIndex = parent->indexOfChild(interface);
1389 // }
1390
1391 QString parentPath = pathForInterface(parent);
1392 QVariantList args = packDBusSignalArguments("remove"_L1, childIndex, 0, variantForPath(path));
1393 sendDBusSignal(parentPath, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "ChildrenChanged"_L1, args);
1394}
1395
1401{
1402 // get accessible interface
1403 QAccessibleInterface * accessible = interfaceFromPath(message.path());
1404 if (!accessible) {
1405 qCWarning(lcAccessibilityAtspi) << "Could not find accessible on path:" << message.path();
1406 return false;
1407 }
1408 if (!accessible->isValid()) {
1409 qCWarning(lcAccessibilityAtspi) << "Accessible invalid:" << accessible << message.path();
1410 return false;
1411 }
1412
1413 QString interface = message.interface();
1414 QString function = message.member();
1415
1416 // qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::handleMessage: " << interface << function;
1417
1418 if (function == "Introspect"_L1) {
1419 //introspect(message.path());
1420 return false;
1421 }
1422
1423 // handle properties like regular functions
1424 if (interface == "org.freedesktop.DBus.Properties"_L1) {
1425 interface = message.arguments().at(0).toString();
1426 // Get/Set + Name
1427 function = message.member() + message.arguments().at(1).toString();
1428 }
1429
1430 // switch interface to call
1432 return accessibleInterface(accessible, function, message, connection);
1434 return applicationInterface(accessible, function, message, connection);
1436 return componentInterface(accessible, function, message, connection);
1438 return actionInterface(accessible, function, message, connection);
1440 return selectionInterface(accessible, function, message, connection);
1442 return textInterface(accessible, function, message, connection);
1444 return editableTextInterface(accessible, function, message, connection);
1446 return valueInterface(accessible, function, message, connection);
1448 return tableInterface(accessible, function, message, connection);
1449 if (interface == ATSPI_DBUS_INTERFACE_TABLE_CELL ""_L1)
1450 return tableCellInterface(accessible, function, message, connection);
1451
1452 qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::handleMessage with unknown interface: " << message.path() << interface << function;
1453 return false;
1454}
1455
1456// Application
1457bool AtSpiAdaptor::applicationInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
1458{
1459 if (message.path() != ATSPI_DBUS_PATH_ROOT ""_L1) {
1460 qCWarning(lcAccessibilityAtspi) << "Could not find application interface for:" << message.path() << interface;
1461 return false;
1462 }
1463
1464 if (function == "SetId"_L1) {
1465 Q_ASSERT(message.signature() == "ssv"_L1);
1466 QVariant value = qvariant_cast<QDBusVariant>(message.arguments().at(2)).variant();
1467
1468 m_applicationId = value.toInt();
1469 return true;
1470 }
1471 if (function == "GetId"_L1) {
1472 Q_ASSERT(message.signature() == "ss"_L1);
1473 QDBusMessage reply = message.createReply(QVariant::fromValue(QDBusVariant(m_applicationId)));
1474 return connection.send(reply);
1475 }
1476 if (function == "GetToolkitName"_L1) {
1477 Q_ASSERT(message.signature() == "ss"_L1);
1479 return connection.send(reply);
1480 }
1481 if (function == "GetVersion"_L1) {
1482 Q_ASSERT(message.signature() == "ss"_L1);
1484 return connection.send(reply);
1485 }
1486 if (function == "GetLocale"_L1) {
1487 Q_ASSERT(message.signature() == "u"_L1);
1489 return connection.send(reply);
1490 }
1491 qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::applicationInterface " << message.path() << interface << function;
1492 return false;
1493}
1494
1499{
1500 OrgA11yAtspiSocketInterface *registry;
1501 registry = new OrgA11yAtspiSocketInterface(QSPI_REGISTRY_NAME ""_L1,
1502 QSPI_OBJECT_PATH_ROOT ""_L1, m_dbus->connection());
1503
1506 reply = registry->Embed(ref);
1507 reply.waitForFinished(); // TODO: make this async
1508 if (reply.isValid ()) {
1509 const QSpiObjectReference &socket = reply.value();
1510 accessibilityRegistry = QSpiObjectReference(socket);
1511 } else {
1512 qCWarning(lcAccessibilityAtspi) << "Error in contacting registry:"
1513 << reply.error().name()
1514 << reply.error().message();
1515 }
1516 delete registry;
1517}
1518
1519namespace {
1520QString accessibleIdForAccessible(QAccessibleInterface *accessible)
1521{
1523 while (accessible) {
1524 if (!result.isEmpty())
1525 result.prepend(u'.');
1526 if (auto obj = accessible->object()) {
1527 const QString name = obj->objectName();
1528 if (!name.isEmpty())
1530 else
1531 result.prepend(QString::fromUtf8(obj->metaObject()->className()));
1532 }
1533 accessible = accessible->parent();
1534 }
1535 return result;
1536}
1537} // namespace
1538
1539// Accessible
1540bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
1541{
1542 if (function == "GetRole"_L1) {
1543 sendReply(connection, message, (uint) getRole(interface));
1544 } else if (function == "GetName"_L1) {
1545 sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->text(QAccessible::Name))));
1546 } else if (function == "GetRoleName"_L1) {
1548 } else if (function == "GetLocalizedRoleName"_L1) {
1550 } else if (function == "GetChildCount"_L1) {
1551 sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->childCount())));
1552 } else if (function == "GetIndexInParent"_L1) {
1553 int childIndex = -1;
1554 QAccessibleInterface * parent = interface->parent();
1555 if (parent) {
1556 childIndex = parent->indexOfChild(interface);
1557 if (childIndex < 0) {
1558 qCDebug(lcAccessibilityAtspi) << "GetIndexInParent get invalid index: " << childIndex << interface;
1559 }
1560 }
1561 sendReply(connection, message, childIndex);
1562 } else if (function == "GetParent"_L1) {
1563 QString path;
1564 QAccessibleInterface * parent = interface->parent();
1565 if (!parent) {
1566 path = ATSPI_DBUS_PATH_NULL ""_L1;
1567 } else if (parent->role() == QAccessible::Application) {
1568 path = ATSPI_DBUS_PATH_ROOT ""_L1;
1569 } else {
1570 path = pathForInterface(parent);
1571 }
1572 // Parent is a property, so it needs to be wrapped inside an extra variant.
1575 } else if (function == "GetChildAtIndex"_L1) {
1576 const int index = message.arguments().at(0).toInt();
1577 if (index < 0) {
1579 QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))));
1580 } else {
1581 QAccessibleInterface * childInterface = interface->child(index);
1583 QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(childInterface)))));
1584 }
1585 } else if (function == "GetInterfaces"_L1) {
1586 sendReply(connection, message, accessibleInterfaces(interface));
1587 } else if (function == "GetDescription"_L1) {
1588 sendReply(connection, message, QVariant::fromValue(QDBusVariant(interface->text(QAccessible::Description))));
1589 } else if (function == "GetState"_L1) {
1590 quint64 spiState = spiStatesFromQState(interface->state());
1591 if (interface->tableInterface()) {
1592 // For tables, setting manages_descendants should
1593 // indicate to the client that it cannot cache these
1594 // interfaces.
1595 setSpiStateBit(&spiState, ATSPI_STATE_MANAGES_DESCENDANTS);
1596 }
1597 QAccessible::Role role = interface->role();
1598 if (role == QAccessible::TreeItem ||
1599 role == QAccessible::ListItem) {
1600 /* Transient means libatspi2 will not cache items.
1601 This is important because when adding/removing an item
1602 the cache becomes outdated and we don't change the paths of
1603 items in lists/trees/tables. */
1604 setSpiStateBit(&spiState, ATSPI_STATE_TRANSIENT);
1605 }
1606 sendReply(connection, message,
1608 } else if (function == "GetAttributes"_L1) {
1610 } else if (function == "GetRelationSet"_L1) {
1611 sendReply(connection, message, QVariant::fromValue(relationSet(interface, connection)));
1612 } else if (function == "GetApplication"_L1) {
1615 } else if (function == "GetChildren"_L1) {
1617 const int numChildren = interface->childCount();
1618 children.reserve(numChildren);
1619 for (int i = 0; i < numChildren; ++i) {
1620 QString childPath = pathForInterface(interface->child(i));
1622 children << ref;
1623 }
1624 connection.send(message.createReply(QVariant::fromValue(children)));
1625 } else if (function == "GetAccessibleId"_L1) {
1626 sendReply(connection, message,
1627 QVariant::fromValue(QDBusVariant(accessibleIdForAccessible(interface))));
1628 } else {
1629 qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::accessibleInterface does not implement" << function << message.path();
1630 return false;
1631 }
1632 return true;
1633}
1634
1635AtspiRole AtSpiAdaptor::getRole(QAccessibleInterface *interface) const
1636{
1637 if ((interface->role() == QAccessible::EditableText) && interface->state().passwordEdit)
1638 return ATSPI_ROLE_PASSWORD_TEXT;
1640}
1641
1642QStringList AtSpiAdaptor::accessibleInterfaces(QAccessibleInterface *interface) const
1643{
1644 QStringList ifaces;
1645 qCDebug(lcAccessibilityAtspiCreation) << "AtSpiAdaptor::accessibleInterfaces create: " << interface->object();
1646 ifaces << u"" ATSPI_DBUS_INTERFACE_ACCESSIBLE ""_s;
1647
1648 if ( (!interface->rect().isEmpty()) ||
1649 (interface->object() && interface->object()->isWidgetType()) ||
1650 (interface->role() == QAccessible::ListItem) ||
1651 (interface->role() == QAccessible::Cell) ||
1652 (interface->role() == QAccessible::TreeItem) ||
1653 (interface->role() == QAccessible::Row) ||
1654 (interface->object() && interface->object()->inherits("QSGItem"))
1655 ) {
1656 ifaces << u"" ATSPI_DBUS_INTERFACE_COMPONENT ""_s;
1657 } else {
1658 qCDebug(lcAccessibilityAtspiCreation) << " IS NOT a component";
1659 }
1660 if (interface->role() == QAccessible::Application)
1661 ifaces << u"" ATSPI_DBUS_INTERFACE_APPLICATION ""_s;
1662
1663 if (interface->actionInterface() || interface->valueInterface())
1664 ifaces << u"" ATSPI_DBUS_INTERFACE_ACTION ""_s;
1665
1666 if (interface->selectionInterface())
1667 ifaces << ATSPI_DBUS_INTERFACE_SELECTION ""_L1;
1668
1669 if (interface->textInterface())
1670 ifaces << u"" ATSPI_DBUS_INTERFACE_TEXT ""_s;
1671
1672 if (interface->editableTextInterface())
1673 ifaces << u"" ATSPI_DBUS_INTERFACE_EDITABLE_TEXT ""_s;
1674
1675 if (interface->valueInterface())
1676 ifaces << u"" ATSPI_DBUS_INTERFACE_VALUE ""_s;
1677
1678 if (interface->tableInterface())
1679 ifaces << u"" ATSPI_DBUS_INTERFACE_TABLE ""_s;
1680
1681 if (interface->tableCellInterface())
1682 ifaces << u"" ATSPI_DBUS_INTERFACE_TABLE_CELL ""_s;
1683
1684 return ifaces;
1685}
1686
1687QSpiRelationArray AtSpiAdaptor::relationSet(QAccessibleInterface *interface, const QDBusConnection &connection) const
1688{
1690 const QList<RelationPair> relationInterfaces = interface->relations();
1691
1692 QSpiRelationArray relations;
1693 for (const RelationPair &pair : relationInterfaces) {
1694// FIXME: this loop seems a bit strange... "related" always have one item when we check.
1695//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
1697
1698 QDBusObjectPath path = QDBusObjectPath(pathForInterface(pair.first));
1700
1701 if (!related.isEmpty())
1702 relations.append(QSpiRelationArrayEntry(qAccessibleRelationToAtSpiRelation(pair.second), related));
1703 }
1704 return relations;
1705}
1706
1707void AtSpiAdaptor::sendReply(const QDBusConnection &connection, const QDBusMessage &message, const QVariant &argument) const
1708{
1709 QDBusMessage reply = message.createReply(argument);
1710 connection.send(reply);
1711}
1712
1713
1714QString AtSpiAdaptor::pathForObject(QObject *object) const
1715{
1716 Q_ASSERT(object);
1717
1718 if (inheritsQAction(object)) {
1719 qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::pathForObject: Creating path with QAction as object.";
1720 }
1721
1722 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(object);
1723 return pathForInterface(iface);
1724}
1725
1726QString AtSpiAdaptor::pathForInterface(QAccessibleInterface *interface) const
1727{
1728 if (!interface || !interface->isValid())
1729 return u"" ATSPI_DBUS_PATH_NULL ""_s;
1730 if (interface->role() == QAccessible::Application)
1731 return u"" QSPI_OBJECT_PATH_ROOT ""_s;
1732
1733 QAccessible::Id id = QAccessible::uniqueId(interface);
1734 Q_ASSERT((int)id < 0);
1735 return QSPI_OBJECT_PATH_PREFIX ""_L1 + QString::number(id);
1736}
1737
1738bool AtSpiAdaptor::inheritsQAction(QObject *object)
1739{
1740 const QMetaObject *mo = object->metaObject();
1741 while (mo) {
1742 const QLatin1StringView cn(mo->className());
1743 if (cn == "QAction"_L1)
1744 return true;
1745 mo = mo->superClass();
1746 }
1747 return false;
1748}
1749
1750// Component
1751static QAccessibleInterface * getWindow(QAccessibleInterface * interface)
1752{
1753 if (interface->role() == QAccessible::Dialog || interface->role() == QAccessible::Window)
1754 return interface;
1755
1756 QAccessibleInterface * parent = interface->parent();
1757 while (parent && parent->role() != QAccessible::Dialog
1758 && parent->role() != QAccessible::Window)
1759 parent = parent->parent();
1760
1761 return parent;
1762}
1763
1764bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
1765{
1766 if (function == "Contains"_L1) {
1767 bool ret = false;
1768 int x = message.arguments().at(0).toInt();
1769 int y = message.arguments().at(1).toInt();
1770 uint coordType = message.arguments().at(2).toUInt();
1771 if (!isValidCoordType(coordType))
1772 return false;
1773 ret = getExtents(interface, coordType).contains(x, y);
1774 sendReply(connection, message, ret);
1775 } else if (function == "GetAccessibleAtPoint"_L1) {
1776 QPoint point(message.arguments().at(0).toInt(), message.arguments().at(1).toInt());
1777 uint coordType = message.arguments().at(2).toUInt();
1778 if (!isValidCoordType(coordType))
1779 return false;
1780 QPoint screenPos = translateToScreenCoordinates(interface, point, coordType);
1781
1782 QAccessibleInterface * childInterface(interface->childAt(screenPos.x(), screenPos.y()));
1783 QAccessibleInterface * iface = nullptr;
1784 while (childInterface) {
1785 iface = childInterface;
1786 childInterface = iface->childAt(screenPos.x(), screenPos.y());
1787 }
1788 if (iface) {
1789 QString path = pathForInterface(iface);
1792 } else {
1794 QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))));
1795 }
1796 } else if (function == "GetAlpha"_L1) {
1797 sendReply(connection, message, (double) 1.0);
1798 } else if (function == "GetExtents"_L1) {
1799 uint coordType = message.arguments().at(0).toUInt();
1800 if (!isValidCoordType(coordType))
1801 return false;
1802 sendReply(connection, message, QVariant::fromValue(getExtents(interface, coordType)));
1803 } else if (function == "GetLayer"_L1) {
1804 sendReply(connection, message, QVariant::fromValue((uint)1));
1805 } else if (function == "GetMDIZOrder"_L1) {
1806 sendReply(connection, message, QVariant::fromValue((short)0));
1807 } else if (function == "GetPosition"_L1) {
1808 uint coordType = message.arguments().at(0).toUInt();
1809 if (!isValidCoordType(coordType))
1810 return false;
1811 QRect rect = getExtents(interface, coordType);
1813 pos << rect.x() << rect.y();
1814 connection.send(message.createReply(pos));
1815 } else if (function == "GetSize"_L1) {
1816 QRect rect = interface->rect();
1818 size << rect.width() << rect.height();
1819 connection.send(message.createReply(size));
1820 } else if (function == "GrabFocus"_L1) {
1821 QAccessibleActionInterface *actionIface = interface->actionInterface();
1822 if (actionIface && actionIface->actionNames().contains(QAccessibleActionInterface::setFocusAction())) {
1823 actionIface->doAction(QAccessibleActionInterface::setFocusAction());
1824 sendReply(connection, message, true);
1825 } else {
1826 sendReply(connection, message, false);
1827 }
1828 } else if (function == "SetExtents"_L1) {
1829// int x = message.arguments().at(0).toInt();
1830// int y = message.arguments().at(1).toInt();
1831// int width = message.arguments().at(2).toInt();
1832// int height = message.arguments().at(3).toInt();
1833// uint coordinateType = message.arguments().at(4).toUInt();
1834 qCDebug(lcAccessibilityAtspi) << "SetExtents is not implemented.";
1835 sendReply(connection, message, false);
1836 } else if (function == "SetPosition"_L1) {
1837// int x = message.arguments().at(0).toInt();
1838// int y = message.arguments().at(1).toInt();
1839// uint coordinateType = message.arguments().at(2).toUInt();
1840 qCDebug(lcAccessibilityAtspi) << "SetPosition is not implemented.";
1841 sendReply(connection, message, false);
1842 } else if (function == "SetSize"_L1) {
1843// int width = message.arguments().at(0).toInt();
1844// int height = message.arguments().at(1).toInt();
1845 qCDebug(lcAccessibilityAtspi) << "SetSize is not implemented.";
1846 sendReply(connection, message, false);
1847 } else {
1848 qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::componentInterface does not implement" << function << message.path();
1849 return false;
1850 }
1851 return true;
1852}
1853
1854QRect AtSpiAdaptor::getExtents(QAccessibleInterface *interface, uint coordType)
1855{
1856 return translateFromScreenCoordinates(interface, interface->rect(), coordType);
1857}
1858
1859// Action interface
1860bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
1861{
1862 if (function == "GetNActions"_L1) {
1865 } else if (function == "DoAction"_L1) {
1866 int index = message.arguments().at(0).toInt();
1868 if (index < 0 || index >= actionNames.size())
1869 return false;
1870 const QString actionName = actionNames.at(index);
1872 sendReply(connection, message, success);
1873 } else if (function == "GetActions"_L1) {
1874 sendReply(connection, message, QVariant::fromValue(getActions(interface)));
1875 } else if (function == "GetName"_L1) {
1876 int index = message.arguments().at(0).toInt();
1878 if (index < 0 || index >= actionNames.size())
1879 return false;
1880 sendReply(connection, message, actionNames.at(index));
1881 } else if (function == "GetDescription"_L1) {
1882 int index = message.arguments().at(0).toInt();
1884 if (index < 0 || index >= actionNames.size())
1885 return false;
1886 QString description;
1887 if (QAccessibleActionInterface *actionIface = interface->actionInterface())
1888 description = actionIface->localizedActionDescription(actionNames.at(index));
1889 else
1890 description = qAccessibleLocalizedActionDescription(actionNames.at(index));
1891 sendReply(connection, message, description);
1892 } else if (function == "GetKeyBinding"_L1) {
1893 int index = message.arguments().at(0).toInt();
1895 if (index < 0 || index >= actionNames.size())
1896 return false;
1897 QStringList keyBindings;
1898 if (QAccessibleActionInterface *actionIface = interface->actionInterface())
1899 keyBindings = actionIface->keyBindingsForAction(actionNames.at(index));
1900 if (keyBindings.isEmpty()) {
1901 QString acc = interface->text(QAccessible::Accelerator);
1902 if (!acc.isEmpty())
1903 keyBindings.append(acc);
1904 }
1905 if (keyBindings.size() > 0)
1906 sendReply(connection, message, keyBindings.join(u';'));
1907 else
1908 sendReply(connection, message, QString());
1909 } else {
1910 qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::actionInterface does not implement" << function << message.path();
1911 return false;
1912 }
1913 return true;
1914}
1915
1916QSpiActionArray AtSpiAdaptor::getActions(QAccessibleInterface *interface) const
1917{
1918 QAccessibleActionInterface *actionInterface = interface->actionInterface();
1919 QSpiActionArray actions;
1921 actions.reserve(actionNames.size());
1922 for (const QString &actionName : actionNames) {
1923 QSpiAction action;
1924
1925 action.name = actionName;
1926 if (actionInterface) {
1927 action.description = actionInterface->localizedActionDescription(actionName);
1928 const QStringList keyBindings = actionInterface->keyBindingsForAction(actionName);
1929 if (!keyBindings.isEmpty())
1930 action.keyBinding = keyBindings.front();
1931 } else {
1932 action.description = qAccessibleLocalizedActionDescription(actionName);
1933 }
1934
1935 actions.append(std::move(action));
1936 }
1937 return actions;
1938}
1939
1940// Text interface
1941bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
1942{
1943 if (!interface->textInterface())
1944 return false;
1945
1946 // properties
1947 if (function == "GetCaretOffset"_L1) {
1948 sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(interface->textInterface()->cursorPosition()))));
1949 } else if (function == "GetCharacterCount"_L1) {
1950 sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(interface->textInterface()->characterCount()))));
1951
1952 // functions
1953 } else if (function == "AddSelection"_L1) {
1954 int startOffset = message.arguments().at(0).toInt();
1955 int endOffset = message.arguments().at(1).toInt();
1956 int lastSelection = interface->textInterface()->selectionCount();
1957 interface->textInterface()->setSelection(lastSelection, startOffset, endOffset);
1958 sendReply(connection, message, (interface->textInterface()->selectionCount() > lastSelection));
1959 } else if (function == "GetAttributeRun"_L1) {
1960 int offset = message.arguments().at(0).toInt();
1961 bool includeDefaults = message.arguments().at(1).toBool();
1962 Q_UNUSED(includeDefaults);
1963 connection.send(message.createReply(getAttributes(interface, offset, includeDefaults)));
1964 } else if (function == "GetAttributeValue"_L1) {
1965 int offset = message.arguments().at(0).toInt();
1966 QString attributeName = message.arguments().at(1).toString();
1967 connection.send(message.createReply(QVariant(getAttributeValue(interface, offset, attributeName))));
1968 } else if (function == "GetAttributes"_L1) {
1969 int offset = message.arguments().at(0).toInt();
1970 connection.send(message.createReply(getAttributes(interface, offset, true)));
1971 } else if (function == "GetBoundedRanges"_L1) {
1972 int x = message.arguments().at(0).toInt();
1973 int y = message.arguments().at(1).toInt();
1974 int width = message.arguments().at(2).toInt();
1975 int height = message.arguments().at(3).toInt();
1976 uint coordType = message.arguments().at(4).toUInt();
1977 uint xClipType = message.arguments().at(5).toUInt();
1978 uint yClipType = message.arguments().at(6).toUInt();
1979 Q_UNUSED(x);
1980 Q_UNUSED(y);
1981 Q_UNUSED(width);
1984 Q_UNUSED(xClipType);
1985 Q_UNUSED(yClipType);
1986 qCDebug(lcAccessibilityAtspi) << "Not implemented: QSpiAdaptor::GetBoundedRanges";
1988 } else if (function == "GetCharacterAtOffset"_L1) {
1989 int offset = message.arguments().at(0).toInt();
1990 int start;
1991 int end;
1992 const QString charString = interface->textInterface()
1993 ->textAtOffset(offset, QAccessible::CharBoundary, &start, &end);
1994 int codePoint = 0;
1995 QStringIterator stringIt(charString);
1996 if (stringIt.hasNext())
1997 codePoint = static_cast<int>(stringIt.peekNext());
1998 sendReply(connection, message, codePoint);
1999 } else if (function == "GetCharacterExtents"_L1) {
2000 int offset = message.arguments().at(0).toInt();
2001 int coordType = message.arguments().at(1).toUInt();
2002 connection.send(message.createReply(getCharacterExtents(interface, offset, coordType)));
2003 } else if (function == "GetDefaultAttributeSet"_L1 || function == "GetDefaultAttributes"_L1) {
2004 // GetDefaultAttributes is deprecated in favour of GetDefaultAttributeSet.
2005 // Empty set seems reasonable. There is no default attribute set.
2007 } else if (function == "GetNSelections"_L1) {
2008 sendReply(connection, message, interface->textInterface()->selectionCount());
2009 } else if (function == "GetOffsetAtPoint"_L1) {
2010 qCDebug(lcAccessibilityAtspi) << message.signature();
2011 Q_ASSERT(!message.signature().isEmpty());
2012 QPoint point(message.arguments().at(0).toInt(), message.arguments().at(1).toInt());
2013 uint coordType = message.arguments().at(2).toUInt();
2014 if (!isValidCoordType(coordType))
2015 return false;
2016 QPoint screenPos = translateToScreenCoordinates(interface, point, coordType);
2017 int offset = interface->textInterface()->offsetAtPoint(screenPos);
2018 sendReply(connection, message, offset);
2019 } else if (function == "GetRangeExtents"_L1) {
2020 int startOffset = message.arguments().at(0).toInt();
2021 int endOffset = message.arguments().at(1).toInt();
2022 uint coordType = message.arguments().at(2).toUInt();
2023 connection.send(message.createReply(getRangeExtents(interface, startOffset, endOffset, coordType)));
2024 } else if (function == "GetSelection"_L1) {
2025 int selectionNum = message.arguments().at(0).toInt();
2026 int start, end;
2027 interface->textInterface()->selection(selectionNum, &start, &end);
2028 if (start < 0)
2029 start = end = interface->textInterface()->cursorPosition();
2030 QVariantList sel;
2031 sel << start << end;
2032 connection.send(message.createReply(sel));
2033 } else if (function == "GetStringAtOffset"_L1) {
2034 int offset = message.arguments().at(0).toInt();
2035 uint granularity = message.arguments().at(1).toUInt();
2036 if (!isValidAtspiTextGranularity(granularity))
2037 return false;
2038 int startOffset, endOffset;
2039 QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryTypeFromAtspiTextGranularity(granularity), &startOffset, &endOffset);
2041 ret << text << startOffset << endOffset;
2042 connection.send(message.createReply(ret));
2043 } else if (function == "GetText"_L1) {
2044 int startOffset = message.arguments().at(0).toInt();
2045 int endOffset = message.arguments().at(1).toInt();
2046 if (endOffset == -1) // AT-SPI uses -1 to signal all characters
2047 endOffset = interface->textInterface()->characterCount();
2048 sendReply(connection, message, interface->textInterface()->text(startOffset, endOffset));
2049 } else if (function == "GetTextAfterOffset"_L1) {
2050 int offset = message.arguments().at(0).toInt();
2051 int type = message.arguments().at(1).toUInt();
2052 int startOffset, endOffset;
2053 QString text = interface->textInterface()->textAfterOffset(offset, qAccessibleBoundaryTypeFromAtspiBoundaryType(type), &startOffset, &endOffset);
2055 ret << text << startOffset << endOffset;
2056 connection.send(message.createReply(ret));
2057 } else if (function == "GetTextAtOffset"_L1) {
2058 int offset = message.arguments().at(0).toInt();
2059 int type = message.arguments().at(1).toUInt();
2060 int startOffset, endOffset;
2061 QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryTypeFromAtspiBoundaryType(type), &startOffset, &endOffset);
2063 ret << text << startOffset << endOffset;
2064 connection.send(message.createReply(ret));
2065 } else if (function == "GetTextBeforeOffset"_L1) {
2066 int offset = message.arguments().at(0).toInt();
2067 int type = message.arguments().at(1).toUInt();
2068 int startOffset, endOffset;
2069 QString text = interface->textInterface()->textBeforeOffset(offset, qAccessibleBoundaryTypeFromAtspiBoundaryType(type), &startOffset, &endOffset);
2071 ret << text << startOffset << endOffset;
2072 connection.send(message.createReply(ret));
2073 } else if (function == "RemoveSelection"_L1) {
2074 int selectionNum = message.arguments().at(0).toInt();
2075 interface->textInterface()->removeSelection(selectionNum);
2076 sendReply(connection, message, true);
2077 } else if (function == "SetCaretOffset"_L1) {
2078 int offset = message.arguments().at(0).toInt();
2079 interface->textInterface()->setCursorPosition(offset);
2080 sendReply(connection, message, true);
2081 } else if (function == "ScrollSubstringTo"_L1) {
2082 int startOffset = message.arguments().at(0).toInt();
2083 int endOffset = message.arguments().at(1).toInt();
2084 // ignore third parameter (scroll type), since QAccessibleTextInterface::scrollToSubstring doesn't have that
2085 qCInfo(lcAccessibilityAtspi) << "AtSpiAdaptor::ScrollSubstringTo doesn'take take scroll type into account.";
2086 interface->textInterface()->scrollToSubstring(startOffset, endOffset);
2087 sendReply(connection, message, true);
2088 } else if (function == "SetSelection"_L1) {
2089 int selectionNum = message.arguments().at(0).toInt();
2090 int startOffset = message.arguments().at(1).toInt();
2091 int endOffset = message.arguments().at(2).toInt();
2092 interface->textInterface()->setSelection(selectionNum, startOffset, endOffset);
2093 sendReply(connection, message, true);
2094 } else {
2095 qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::textInterface does not implement" << function << message.path();
2096 return false;
2097 }
2098 return true;
2099}
2100
2101QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryTypeFromAtspiBoundaryType(int atspiTextBoundaryType)
2102{
2103 switch (atspiTextBoundaryType) {
2104 case ATSPI_TEXT_BOUNDARY_CHAR:
2105 return QAccessible::CharBoundary;
2106 case ATSPI_TEXT_BOUNDARY_WORD_START:
2107 case ATSPI_TEXT_BOUNDARY_WORD_END:
2108 return QAccessible::WordBoundary;
2109 case ATSPI_TEXT_BOUNDARY_SENTENCE_START:
2110 case ATSPI_TEXT_BOUNDARY_SENTENCE_END:
2111 return QAccessible::SentenceBoundary;
2112 case ATSPI_TEXT_BOUNDARY_LINE_START:
2113 case ATSPI_TEXT_BOUNDARY_LINE_END:
2114 return QAccessible::LineBoundary;
2115 }
2116 Q_ASSERT_X(0, "", "Requested invalid boundary type.");
2117 return QAccessible::CharBoundary;
2118}
2119
2120bool AtSpiAdaptor::isValidAtspiTextGranularity(uint atspiTextGranularity)
2121{
2122 if (atspiTextGranularity == ATSPI_TEXT_GRANULARITY_CHAR
2123 || atspiTextGranularity == ATSPI_TEXT_GRANULARITY_WORD
2124 || atspiTextGranularity == ATSPI_TEXT_GRANULARITY_SENTENCE
2125 || atspiTextGranularity == ATSPI_TEXT_GRANULARITY_LINE
2126 || atspiTextGranularity == ATSPI_TEXT_GRANULARITY_PARAGRAPH)
2127 return true;
2128
2129 qCWarning(lcAccessibilityAtspi) << "Unknown value" << atspiTextGranularity << "for AT-SPI text granularity type";
2130 return false;
2131}
2132
2133QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryTypeFromAtspiTextGranularity(uint atspiTextGranularity)
2134{
2135 Q_ASSERT(isValidAtspiTextGranularity(atspiTextGranularity));
2136
2137 switch (atspiTextGranularity) {
2138 case ATSPI_TEXT_GRANULARITY_CHAR:
2139 return QAccessible::CharBoundary;
2140 case ATSPI_TEXT_GRANULARITY_WORD:
2141 return QAccessible::WordBoundary;
2142 case ATSPI_TEXT_GRANULARITY_SENTENCE:
2143 return QAccessible::SentenceBoundary;
2144 case ATSPI_TEXT_GRANULARITY_LINE:
2145 return QAccessible::LineBoundary;
2146 case ATSPI_TEXT_GRANULARITY_PARAGRAPH:
2147 return QAccessible::ParagraphBoundary;
2148 }
2149 return QAccessible::CharBoundary;
2150}
2151
2152namespace
2153{
2154 struct AtSpiAttribute {
2155 QString name;
2156 QString value;
2157 AtSpiAttribute(const QString &aName, const QString &aValue) : name(aName), value(aValue) {}
2158 bool isNull() const { return name.isNull() || value.isNull(); }
2159 };
2160
2161 QString atspiColor(const QString &ia2Color)
2162 {
2163 // "rgb(%u,%u,%u)" -> "%u,%u,%u"
2164 return ia2Color.mid(4, ia2Color.size() - (4+1));
2165 }
2166
2167 QString atspiSize(const QString &ia2Size)
2168 {
2169 // "%fpt" -> "%f"
2170 return ia2Size.left(ia2Size.size() - 2);
2171 }
2172
2173 AtSpiAttribute atspiTextAttribute(const QString &ia2Name, const QString &ia2Value)
2174 {
2175 QString name = ia2Name;
2176 QString value = ia2Value;
2177
2178 // IAccessible2: http://www.linuxfoundation.org/collaborate/workgroups/accessibility/iaccessible2/textattributes
2179 // ATK attribute names: https://git.gnome.org/browse/orca/tree/src/orca/text_attribute_names.py
2180 // ATK attribute values: https://developer.gnome.org/atk/unstable/AtkText.html#AtkTextAttribute
2181
2182 // https://bugzilla.gnome.org/show_bug.cgi?id=744553 "ATK docs provide no guidance for allowed values of some text attributes"
2183 // specifically for "weight", "invalid", "language" and value range for colors
2184
2185 if (ia2Name == "background-color"_L1) {
2186 name = QStringLiteral("bg-color");
2187 value = atspiColor(value);
2188 } else if (ia2Name == "font-family"_L1) {
2189 name = QStringLiteral("family-name");
2190 } else if (ia2Name == "color"_L1) {
2191 name = QStringLiteral("fg-color");
2192 value = atspiColor(value);
2193 } else if (ia2Name == "text-align"_L1) {
2194 name = QStringLiteral("justification");
2195 if (value == "justify"_L1) {
2196 value = QStringLiteral("fill");
2197 } else if (value != "left"_L1 && value != "right"_L1 && value != "center"_L1) {
2198 qCDebug(lcAccessibilityAtspi) << "Unknown text-align attribute value \""
2199 << value << "\" cannot be translated to AT-SPI.";
2200 value = QString();
2201 }
2202 } else if (ia2Name == "font-size"_L1) {
2203 name = QStringLiteral("size");
2204 value = atspiSize(value);
2205 } else if (ia2Name == "font-style"_L1) {
2206 name = QStringLiteral("style");
2207 if (value != "normal"_L1 && value != "italic"_L1 && value != "oblique"_L1) {
2208 qCDebug(lcAccessibilityAtspi) << "Unknown font-style attribute value \"" << value
2209 << "\" cannot be translated to AT-SPI.";
2210 value = QString();
2211 }
2212 } else if (ia2Name == "text-underline-type"_L1) {
2213 name = QStringLiteral("underline");
2214 if (value != "none"_L1 && value != "single"_L1 && value != "double"_L1) {
2215 qCDebug(lcAccessibilityAtspi) << "Unknown text-underline-type attribute value \""
2216 << value << "\" cannot be translated to AT-SPI.";
2217 value = QString();
2218 }
2219 } else if (ia2Name == "font-weight"_L1) {
2220 name = QStringLiteral("weight");
2221 if (value == "normal"_L1)
2222 // Orca seems to accept all IAccessible2 values except for "normal"
2223 // (on which it produces traceback and fails to read any following text attributes),
2224 // but that is the default value, so omit it anyway
2225 value = QString();
2226 } else if (ia2Name == "text-position"_L1) {
2227 name = QStringLiteral("vertical-align");
2228 if (value != "baseline"_L1 && value != "super"_L1 && value != "sub"_L1) {
2229 qCDebug(lcAccessibilityAtspi) << "Unknown text-position attribute value \"" << value
2230 << "\" cannot be translated to AT-SPI.";
2231 value = QString();
2232 }
2233 } else if (ia2Name == "writing-mode"_L1) {
2234 name = QStringLiteral("direction");
2235 if (value == "lr"_L1)
2236 value = QStringLiteral("ltr");
2237 else if (value == "rl"_L1)
2238 value = QStringLiteral("rtl");
2239 else if (value == "tb"_L1) {
2240 // 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))
2241 value = QStringLiteral("rtl");
2242 qCDebug(lcAccessibilityAtspi) << "writing-mode attribute value \"tb\" translated only w.r.t. horizontal direction; vertical direction ignored";
2243 } else {
2244 qCDebug(lcAccessibilityAtspi) << "Unknown writing-mode attribute value \"" << value
2245 << "\" cannot be translated to AT-SPI.";
2246 value = QString();
2247 }
2248 } else if (ia2Name == "language"_L1) {
2249 // OK - ATK has no docs on the format of the value, IAccessible2 has reasonable format - leave it at that now
2250 } else if (ia2Name == "invalid"_L1) {
2251 // OK - ATK docs are vague but suggest they support the same range of values as IAccessible2
2252 } else {
2253 // attribute we know nothing about
2254 name = QString();
2255 value = QString();
2256 }
2257 return AtSpiAttribute(name, value);
2258 }
2259}
2260
2261// FIXME all attribute methods below should share code
2262QVariantList AtSpiAdaptor::getAttributes(QAccessibleInterface *interface, int offset, bool includeDefaults) const
2263{
2264 Q_UNUSED(includeDefaults);
2265
2267 int startOffset;
2268 int endOffset;
2269
2270 QString joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset);
2271 const QStringList attributes = joined.split(u';', Qt::SkipEmptyParts, Qt::CaseSensitive);
2272 for (const QString &attr : attributes) {
2274 items = attr.split(u':', Qt::SkipEmptyParts, Qt::CaseSensitive);
2275 AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]);
2276 if (!attribute.isNull())
2277 set[attribute.name] = attribute.value;
2278 }
2279
2281 list << QVariant::fromValue(set) << startOffset << endOffset;
2282
2283 return list;
2284}
2285
2286QString AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, int offset, const QString &attributeName) const
2287{
2288 QString joined;
2290 int startOffset;
2291 int endOffset;
2292
2293 joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset);
2294 const QStringList attributes = joined.split (u';', Qt::SkipEmptyParts, Qt::CaseSensitive);
2295 for (const QString& attr : attributes) {
2297 items = attr.split(u':', Qt::SkipEmptyParts, Qt::CaseSensitive);
2298 AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]);
2299 if (!attribute.isNull())
2300 map[attribute.name] = attribute.value;
2301 }
2302 return map[attributeName];
2303}
2304
2305QList<QVariant> AtSpiAdaptor::getCharacterExtents(QAccessibleInterface *interface, int offset, uint coordType) const
2306{
2307 QRect rect = interface->textInterface()->characterRect(offset);
2308 rect = translateFromScreenCoordinates(interface, rect, coordType);
2309 return QList<QVariant>() << rect.x() << rect.y() << rect.width() << rect.height();
2310}
2311
2312QList<QVariant> AtSpiAdaptor::getRangeExtents(QAccessibleInterface *interface,
2313 int startOffset, int endOffset, uint coordType) const
2314{
2315 if (endOffset == -1)
2316 endOffset = interface->textInterface()->characterCount();
2317
2318 QAccessibleTextInterface *textInterface = interface->textInterface();
2319 if (endOffset <= startOffset || !textInterface)
2320 return QList<QVariant>() << -1 << -1 << 0 << 0;
2321
2322 QRect rect = textInterface->characterRect(startOffset);
2323 for (int i=startOffset + 1; i <= endOffset; i++)
2324 rect = rect | textInterface->characterRect(i);
2325
2326 rect = translateFromScreenCoordinates(interface, rect, coordType);
2327 return QList<QVariant>() << rect.x() << rect.y() << rect.width() << rect.height();
2328}
2329
2330bool AtSpiAdaptor::isValidCoordType(uint coordType)
2331{
2332 if (coordType == ATSPI_COORD_TYPE_SCREEN || coordType == ATSPI_COORD_TYPE_WINDOW || coordType == ATSPI_COORD_TYPE_PARENT)
2333 return true;
2334
2335 qCWarning(lcAccessibilityAtspi) << "Unknown value" << coordType << "for AT-SPI coord type";
2336 return false;
2337}
2338
2339QRect AtSpiAdaptor::translateFromScreenCoordinates(QAccessibleInterface *interface, const QRect &screenRect, uint targetCoordType)
2340{
2341 Q_ASSERT(isValidCoordType(targetCoordType));
2342
2343 QAccessibleInterface *upper = nullptr;
2344 if (targetCoordType == ATSPI_COORD_TYPE_WINDOW)
2345 upper = getWindow(interface);
2346 else if (targetCoordType == ATSPI_COORD_TYPE_PARENT)
2347 upper = interface->parent();
2348
2349 QRect rect = screenRect;
2350 if (upper)
2351 rect.translate(-upper->rect().x(), -upper->rect().y());
2352
2353 return rect;
2354}
2355
2356QPoint AtSpiAdaptor::translateToScreenCoordinates(QAccessibleInterface *interface, const QPoint &pos, uint fromCoordType)
2357{
2358 Q_ASSERT(isValidCoordType(fromCoordType));
2359
2360 QAccessibleInterface *upper = nullptr;
2361 if (fromCoordType == ATSPI_COORD_TYPE_WINDOW)
2362 upper = getWindow(interface);
2363 else if (fromCoordType == ATSPI_COORD_TYPE_PARENT)
2364 upper = interface->parent();
2365
2366 QPoint screenPos = pos;
2367 if (upper)
2368 screenPos += upper->rect().topLeft();
2369
2370 return screenPos;
2371}
2372
2373// Editable Text interface
2374static QString textForRange(QAccessibleInterface *accessible, int startOffset, int endOffset)
2375{
2376 if (QAccessibleTextInterface *textIface = accessible->textInterface()) {
2377 if (endOffset == -1)
2378 endOffset = textIface->characterCount();
2379 return textIface->text(startOffset, endOffset);
2380 }
2381 QString txt = accessible->text(QAccessible::Value);
2382 if (endOffset == -1)
2383 endOffset = txt.size();
2384 return txt.mid(startOffset, endOffset - startOffset);
2385}
2386
2387static void replaceTextFallback(QAccessibleInterface *accessible, long startOffset, long endOffset, const QString &txt)
2388{
2389 QString t = textForRange(accessible, 0, -1);
2390 if (endOffset == -1)
2391 endOffset = t.size();
2392 if (endOffset - startOffset == 0)
2393 t.insert(startOffset, txt);
2394 else
2395 t.replace(startOffset, endOffset - startOffset, txt);
2396 accessible->setText(QAccessible::Value, t);
2397}
2398
2399bool AtSpiAdaptor::editableTextInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
2400{
2401 if (function == "CopyText"_L1) {
2402#ifndef QT_NO_CLIPBOARD
2403 int startOffset = message.arguments().at(0).toInt();
2404 int endOffset = message.arguments().at(1).toInt();
2405 const QString t = textForRange(interface, startOffset, endOffset);
2407#endif
2408 connection.send(message.createReply(true));
2409 } else if (function == "CutText"_L1) {
2410#ifndef QT_NO_CLIPBOARD
2411 int startOffset = message.arguments().at(0).toInt();
2412 int endOffset = message.arguments().at(1).toInt();
2413 const QString t = textForRange(interface, startOffset, endOffset);
2414 if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
2415 editableTextIface->deleteText(startOffset, endOffset);
2416 else
2417 replaceTextFallback(interface, startOffset, endOffset, QString());
2419#endif
2420 connection.send(message.createReply(true));
2421 } else if (function == "DeleteText"_L1) {
2422 int startOffset = message.arguments().at(0).toInt();
2423 int endOffset = message.arguments().at(1).toInt();
2424 if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
2425 editableTextIface->deleteText(startOffset, endOffset);
2426 else
2427 replaceTextFallback(interface, startOffset, endOffset, QString());
2428 connection.send(message.createReply(true));
2429 } else if (function == "InsertText"_L1) {
2430 int position = message.arguments().at(0).toInt();
2431 QString text = message.arguments().at(1).toString();
2432 int length = message.arguments().at(2).toInt();
2434 if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
2435 editableTextIface->insertText(position, text);
2436 else
2437 replaceTextFallback(interface, position, position, text);
2438 connection.send(message.createReply(true));
2439 } else if (function == "PasteText"_L1) {
2440#ifndef QT_NO_CLIPBOARD
2441 int position = message.arguments().at(0).toInt();
2443 if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
2444 editableTextIface->insertText(position, txt);
2445 else
2446 replaceTextFallback(interface, position, position, txt);
2447#endif
2448 connection.send(message.createReply(true));
2449 } else if (function == "SetTextContents"_L1) {
2450 QString newContents = message.arguments().at(0).toString();
2451 if (QAccessibleEditableTextInterface *editableTextIface = interface->editableTextInterface())
2452 editableTextIface->replaceText(0, interface->textInterface()->characterCount(), newContents);
2453 else
2454 replaceTextFallback(interface, 0, -1, newContents);
2455 connection.send(message.createReply(true));
2456 } else if (function.isEmpty()) {
2457 connection.send(message.createReply());
2458 } else {
2459 qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::editableTextInterface does not implement" << function << message.path();
2460 return false;
2461 }
2462 return true;
2463}
2464
2465// Value interface
2466bool AtSpiAdaptor::valueInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
2467{
2468 QAccessibleValueInterface *valueIface = interface->valueInterface();
2469 if (!valueIface)
2470 return false;
2471
2472 if (function == "SetCurrentValue"_L1) {
2473 QDBusVariant v = qvariant_cast<QDBusVariant>(message.arguments().at(2));
2474 double value = v.variant().toDouble();
2475 //Temporary fix
2476 //See https://bugzilla.gnome.org/show_bug.cgi?id=652596
2477 valueIface->setCurrentValue(value);
2478 connection.send(message.createReply());
2479 } else {
2481 if (function == "GetCurrentValue"_L1)
2482 value = valueIface->currentValue();
2483 else if (function == "GetMaximumValue"_L1)
2484 value = valueIface->maximumValue();
2485 else if (function == "GetMinimumIncrement"_L1)
2486 value = valueIface->minimumStepSize();
2487 else if (function == "GetMinimumValue"_L1)
2488 value = valueIface->minimumValue();
2489 else {
2490 qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::valueInterface does not implement" << function << message.path();
2491 return false;
2492 }
2493 if (!value.canConvert<double>()) {
2494 qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::valueInterface: Could not convert to double:" << function;
2495 }
2496
2497 // explicitly convert to dbus-variant containing one double since atspi expects that
2498 // everything else might fail to convert back on the other end
2499 connection.send(message.createReply(
2501 }
2502 return true;
2503}
2504
2505// Selection interface
2506bool AtSpiAdaptor::selectionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
2507{
2508 QAccessibleSelectionInterface* selectionInterface = interface->selectionInterface();
2509 if (!selectionInterface) {
2510 qCWarning(lcAccessibilityAtspi) << "Could not find selection interface for: " << message.path() << interface;
2511 return false;
2512 }
2513
2514 if (function == "ClearSelection"_L1 ) {
2515 connection.send(message.createReply(QVariant::fromValue((selectionInterface->clear()))));
2516 } else if (function == "DeselectChild"_L1 ) {
2517 int childIndex = message.arguments().at(0).toInt();
2518 bool ret = false;
2519 QAccessibleInterface *child = interface->child(childIndex);
2520 if (child)
2521 ret = selectionInterface->unselect(child);
2522 connection.send(message.createReply(QVariant::fromValue(ret)));
2523 } else if (function == "DeselectSelectedChild"_L1 ) {
2524 int selectionIndex = message.arguments().at(0).toInt();
2525 bool ret = false;
2526 QAccessibleInterface *selectedChild = selectionInterface->selectedItem(selectionIndex);
2527 if (selectedChild)
2528 ret = selectionInterface->unselect(selectedChild);
2529 connection.send(message.createReply(QVariant::fromValue(ret)));
2530 } else if (function == "GetNSelectedChildren"_L1) {
2532 QVariant::fromValue(selectionInterface->selectedItemCount())))));
2533 } else if (function == "GetSelectedChild"_L1) {
2534 int selectionIndex = message.arguments().at(0).toInt();
2535 QSpiObjectReference ref(connection, QDBusObjectPath(pathForInterface(selectionInterface->selectedItem(selectionIndex))));
2536 connection.send(message.createReply(QVariant::fromValue(ref)));
2537 } else if (function == "IsChildSelected"_L1 ) {
2538 int childIndex = message.arguments().at(0).toInt();
2539 bool ret = false;
2540 QAccessibleInterface *child = interface->child(childIndex);
2541 if (child)
2542 ret = selectionInterface->isSelected(child);
2543 connection.send(message.createReply(QVariant::fromValue(ret)));
2544 } else if (function == "SelectAll"_L1 ) {
2545 connection.send(message.createReply(QVariant::fromValue(selectionInterface->selectAll())));
2546 } else if (function == "SelectChild"_L1 ) {
2547 int childIndex = message.arguments().at(0).toInt();
2548 bool ret = false;
2549 QAccessibleInterface *child = interface->child(childIndex);
2550 if (child)
2551 ret = selectionInterface->select(child);
2552 connection.send(message.createReply(QVariant::fromValue(ret)));
2553 } else {
2554 qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::selectionInterface does not implement " << function << message.path();
2555 return false;
2556 }
2557
2558 return true;
2559}
2560
2561
2562// Table interface
2563bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
2564{
2565 if (!(interface->tableInterface() || interface->tableCellInterface())) {
2566 qCWarning(lcAccessibilityAtspi) << "Qt AtSpiAdaptor: Could not find table interface for:" << message.path() << interface;
2567 return false;
2568 }
2569
2570 if (function == "GetCaption"_L1) {
2571 QAccessibleInterface * captionInterface= interface->tableInterface()->caption();
2572 if (captionInterface) {
2573 QSpiObjectReference ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(captionInterface)));
2575 } else {
2577 QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))))));
2578 }
2579 } else if (function == "GetNColumns"_L1) {
2581 QVariant::fromValue(interface->tableInterface()->columnCount())))));
2582 } else if (function == "GetNRows"_L1) {
2584 QVariant::fromValue(interface->tableInterface()->rowCount())))));
2585 } else if (function == "GetNSelectedColumns"_L1) {
2587 QVariant::fromValue(interface->tableInterface()->selectedColumnCount())))));
2588 } else if (function == "GetNSelectedRows"_L1) {
2590 QVariant::fromValue(interface->tableInterface()->selectedRowCount())))));
2591 } else if (function == "GetSummary"_L1) {
2592 QAccessibleInterface *summary = interface->tableInterface() ? interface->tableInterface()->summary() : nullptr;
2593 QSpiObjectReference ref(connection, QDBusObjectPath(pathForInterface(summary)));
2595 } else if (function == "GetAccessibleAt"_L1) {
2596 int row = message.arguments().at(0).toInt();
2597 int column = message.arguments().at(1).toInt();
2598 if ((row < 0) ||
2599 (column < 0) ||
2600 (row >= interface->tableInterface()->rowCount()) ||
2601 (column >= interface->tableInterface()->columnCount())) {
2602 qCWarning(lcAccessibilityAtspi) << "Invalid index for tableInterface GetAccessibleAt (" << row << "," << column << ')';
2603 return false;
2604 }
2605
2607 QAccessibleInterface * cell(interface->tableInterface()->cellAt(row, column));
2608 if (cell) {
2609 ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(cell)));
2610 } else {
2611 qCWarning(lcAccessibilityAtspi) << "No cell interface returned for" << interface->object() << row << column;
2613 }
2614 connection.send(message.createReply(QVariant::fromValue(ref)));
2615
2616 } else if (function == "GetIndexAt"_L1) {
2617 int row = message.arguments().at(0).toInt();
2618 int column = message.arguments().at(1).toInt();
2619 QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column);
2620 if (!cell) {
2621 qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::GetIndexAt(" << row << ',' << column << ") did not find a cell." << interface;
2622 return false;
2623 }
2624 int index = interface->indexOfChild(cell);
2625 qCDebug(lcAccessibilityAtspi) << "QSpiAdaptor::GetIndexAt row:" << row << " col:" << column << " logical index:" << index;
2626 Q_ASSERT(index > 0);
2627 connection.send(message.createReply(index));
2628 } else if ((function == "GetColumnAtIndex"_L1) || (function == "GetRowAtIndex"_L1)) {
2629 int index = message.arguments().at(0).toInt();
2630 int ret = -1;
2631 if (index >= 0) {
2632 QAccessibleInterface * cell = interface->child(index);
2633 if (cell) {
2634 if (function == "GetColumnAtIndex"_L1) {
2635 if (cell->role() == QAccessible::ColumnHeader) {
2636 ret = index;
2637 } else if (cell->role() == QAccessible::RowHeader) {
2638 ret = -1;
2639 } else {
2640 if (!cell->tableCellInterface()) {
2641 qCWarning(lcAccessibilityAtspi).nospace() << "AtSpiAdaptor::" << function << " No table cell interface: " << cell;
2642 return false;
2643 }
2644 ret = cell->tableCellInterface()->columnIndex();
2645 }
2646 } else {
2647 if (cell->role() == QAccessible::ColumnHeader) {
2648 ret = -1;
2649 } else if (cell->role() == QAccessible::RowHeader) {
2650 ret = index % interface->tableInterface()->columnCount();
2651 } else {
2652 if (!cell->tableCellInterface()) {
2653 qCWarning(lcAccessibilityAtspi).nospace() << "AtSpiAdaptor::" << function << " No table cell interface: " << cell;
2654 return false;
2655 }
2656 ret = cell->tableCellInterface()->rowIndex();
2657 }
2658 }
2659 } else {
2660 qCWarning(lcAccessibilityAtspi).nospace() << "AtSpiAdaptor::" << function << " No cell at index: " << index << " " << interface;
2661 return false;
2662 }
2663 }
2664 connection.send(message.createReply(ret));
2665
2666 } else if (function == "GetColumnDescription"_L1) {
2667 int column = message.arguments().at(0).toInt();
2668 connection.send(message.createReply(interface->tableInterface()->columnDescription(column)));
2669 } else if (function == "GetRowDescription"_L1) {
2670 int row = message.arguments().at(0).toInt();
2671 connection.send(message.createReply(interface->tableInterface()->rowDescription(row)));
2672
2673
2674
2675 } else if (function == "GetRowColumnExtentsAtIndex"_L1) {
2676 int index = message.arguments().at(0).toInt();
2677 bool success = false;
2678
2679 int row = -1;
2680 int col = -1;
2681 int rowExtents = -1;
2682 int colExtents = -1;
2683 bool isSelected = false;
2684
2685 int cols = interface->tableInterface()->columnCount();
2686 if (cols > 0) {
2687 row = index / cols;
2688 col = index % cols;
2689 QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, col)->tableCellInterface();
2690 if (cell) {
2691 row = cell->rowIndex();
2692 col = cell->columnIndex();
2693 rowExtents = cell->rowExtent();
2694 colExtents = cell->columnExtent();
2695 isSelected = cell->isSelected();
2696 success = true;
2697 }
2698 }
2700 list << success << row << col << rowExtents << colExtents << isSelected;
2701 connection.send(message.createReply(list));
2702
2703 } else if (function == "GetColumnExtentAt"_L1) {
2704 int row = message.arguments().at(0).toInt();
2705 int column = message.arguments().at(1).toInt();
2706 connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->columnExtent()));
2707
2708 } else if (function == "GetRowExtentAt"_L1) {
2709 int row = message.arguments().at(0).toInt();
2710 int column = message.arguments().at(1).toInt();
2711 connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->rowExtent()));
2712
2713 } else if (function == "GetColumnHeader"_L1) {
2714 int column = message.arguments().at(0).toInt();
2716
2717 QAccessibleInterface * cell(interface->tableInterface()->cellAt(0, column));
2718 if (cell && cell->tableCellInterface()) {
2719 QList<QAccessibleInterface*> header = cell->tableCellInterface()->columnHeaderCells();
2720 if (header.size() > 0) {
2721 ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(header.takeAt(0))));
2722 }
2723 }
2724 connection.send(message.createReply(QVariant::fromValue(ref)));
2725
2726 } else if (function == "GetRowHeader"_L1) {
2727 int row = message.arguments().at(0).toInt();
2729 QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, 0);
2730 if (cell && cell->tableCellInterface()) {
2731 QList<QAccessibleInterface*> header = cell->tableCellInterface()->rowHeaderCells();
2732 if (header.size() > 0) {
2733 ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(header.takeAt(0))));
2734 }
2735 }
2736 connection.send(message.createReply(QVariant::fromValue(ref)));
2737
2738 } else if (function == "GetSelectedColumns"_L1) {
2739 connection.send(message.createReply(QVariant::fromValue(interface->tableInterface()->selectedColumns())));
2740 } else if (function == "GetSelectedRows"_L1) {
2741 connection.send(message.createReply(QVariant::fromValue(interface->tableInterface()->selectedRows())));
2742 } else if (function == "IsColumnSelected"_L1) {
2743 int column = message.arguments().at(0).toInt();
2744 connection.send(message.createReply(interface->tableInterface()->isColumnSelected(column)));
2745 } else if (function == "IsRowSelected"_L1) {
2746 int row = message.arguments().at(0).toInt();
2747 connection.send(message.createReply(interface->tableInterface()->isRowSelected(row)));
2748 } else if (function == "IsSelected"_L1) {
2749 int row = message.arguments().at(0).toInt();
2750 int column = message.arguments().at(1).toInt();
2751 QAccessibleTableCellInterface* cell = interface->tableInterface()->cellAt(row, column)->tableCellInterface();
2752 connection.send(message.createReply(cell->isSelected()));
2753 } else if (function == "AddColumnSelection"_L1) {
2754 int column = message.arguments().at(0).toInt();
2755 connection.send(message.createReply(interface->tableInterface()->selectColumn(column)));
2756 } else if (function == "AddRowSelection"_L1) {
2757 int row = message.arguments().at(0).toInt();
2758 connection.send(message.createReply(interface->tableInterface()->selectRow(row)));
2759 } else if (function == "RemoveColumnSelection"_L1) {
2760 int column = message.arguments().at(0).toInt();
2761 connection.send(message.createReply(interface->tableInterface()->unselectColumn(column)));
2762 } else if (function == "RemoveRowSelection"_L1) {
2763 int row = message.arguments().at(0).toInt();
2764 connection.send(message.createReply(interface->tableInterface()->unselectRow(row)));
2765 } else {
2766 qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::tableInterface does not implement" << function << message.path();
2767 return false;
2768 }
2769 return true;
2770}
2771
2772// Table cell interface
2773bool AtSpiAdaptor::tableCellInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
2774{
2775 QAccessibleTableCellInterface* cellInterface = interface->tableCellInterface();
2776 if (!cellInterface) {
2777 qCWarning(lcAccessibilityAtspi) << "Could not find table cell interface for: " << message.path() << interface;
2778 return false;
2779 }
2780
2781 if (function == "GetColumnHeaderCells"_L1) {
2782 QSpiObjectReferenceArray headerCells;
2783 const auto headerCellInterfaces = cellInterface->columnHeaderCells();
2784 headerCells.reserve(headerCellInterfaces.size());
2785 for (QAccessibleInterface *cell : headerCellInterfaces) {
2786 const QString childPath = pathForInterface(cell);
2788 headerCells << ref;
2789 }
2790 connection.send(message.createReply(QVariant::fromValue(headerCells)));
2791 } else if (function == "GetColumnSpan"_L1) {
2793 QVariant::fromValue(cellInterface->columnExtent())))));
2794 } else if (function == "GetPosition"_L1) {
2795 const int row = cellInterface->rowIndex();
2796 const int column = cellInterface->columnIndex();
2799 } else if (function == "GetRowHeaderCells"_L1) {
2800 QSpiObjectReferenceArray headerCells;
2801 const auto headerCellInterfaces = cellInterface->rowHeaderCells();
2802 headerCells.reserve(headerCellInterfaces.size());
2803 for (QAccessibleInterface *cell : headerCellInterfaces) {
2804 const QString childPath = pathForInterface(cell);
2806 headerCells << ref;
2807 }
2808 connection.send(message.createReply(QVariant::fromValue(headerCells)));
2809 } else if (function == "GetRowSpan"_L1) {
2811 QVariant::fromValue(cellInterface->rowExtent())))));
2812 } else if (function == "GetRowColumnSpan"_L1) {
2814 list << cellInterface->rowIndex() << cellInterface->columnIndex() << cellInterface->rowExtent() << cellInterface->columnExtent();
2815 connection.send(message.createReply(list));
2816 } else if (function == "GetTable"_L1) {
2818 QAccessibleInterface* table = cellInterface->table();
2819 if (table && table->tableInterface())
2822 } else {
2823 qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::tableCellInterface does not implement" << function << message.path();
2824 return false;
2825 }
2826
2827 return true;
2828}
2829
2831
2832#include "moc_atspiadaptor_p.cpp"
2833#endif // QT_CONFIG(accessibility)
bool isActive
QString introspect(const QString &path) const override
void notify(QAccessibleEvent *event)
void windowActivated(QObject *window, bool active)
void registerApplication()
void eventListenerDeregistered(const QString &bus, const QString &path)
bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection) override
This function needs to handle all messages to the path of the virtual object, when the SubPath option...
void eventListenerRegistered(const QString &bus, const QString &path)
Connects to the accessibility dbus.
QDBusConnection connection() const
Returns the DBus connection that got established.
\inmodule QtGui
QChar toLower() const noexcept
Returns the lowercase equivalent if the character is uppercase or titlecase; otherwise returns the ch...
Definition qchar.h:448
constexpr char toLatin1() const noexcept
Returns the Latin-1 character equivalent to the QChar, or 0.
Definition qchar.h:457
void setText(const QString &, Mode mode=Clipboard)
Copies text into the clipboard as plain text.
QString text(Mode mode=Clipboard) const
Returns the clipboard text as plain text, or an empty string if the clipboard does not contain any te...
\inmodule QtDBus
bool send(const QDBusMessage &message) const
Sends the message over this connection, without waiting for a reply.
QDBusMessage call(const QDBusMessage &message, QDBus::CallMode mode=QDBus::Block, int timeout=-1) const
Sends the message over this connection and blocks, waiting for a reply, for at most timeout milliseco...
\inmodule QtDBus
static QDBusMessage createSignal(const QString &path, const QString &interface, const QString &name)
Constructs a new DBus message with the given path, interface and name, representing a signal emission...
static QDBusMessage createMethodCall(const QString &destination, const QString &path, const QString &interface, const QString &method)
Constructs a new DBus message representing a method call.
\inmodule QtDBus
\inmodule QtDBus
\inmodule QtDBus
Definition qdbusreply.h:24
Type value() const
Returns the remote function's calls return value.
Definition qdbusreply.h:78
bool isValid() const
Returns true if no error occurred; otherwise, returns false.
Definition qdbusreply.h:73
\inmodule QtDBus
void setVariant(const QVariant &variant)
Assigns the value of the given Qt variant to this D-Bus variant.
static QClipboard * clipboard()
Returns the object for interacting with the clipboard.
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
T value(const Key &key, const T &defaultValue=T()) const
Definition qmap.h:356
NetworkError error() const
Returns the error that was found during the processing of this request.
\inmodule QtCore
Definition qobject.h:90
const QObjectList & children() const
Returns a list of child objects.
Definition qobject.h:171
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:311
bool inherits(const char *classname) const
Returns true if this object is an instance of a class that inherits className or a QObject subclass t...
Definition qobject.h:313
bool isWidgetType() const
Returns true if the object is a widget; otherwise returns false.
Definition qobject.h:118
\inmodule QtCore\reentrant
Definition qpoint.h:23
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:127
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:132
\inmodule QtCore\reentrant
Definition qrect.h:30
bool contains(const QRect &r, bool proper=false) const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qrect.cpp:851
static RoleNames namesForRole(QAccessible::Role role)
void sendEvents(bool active)
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:7956
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
uint toUInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an {unsigned int} using base base, which is 10 by default and must be...
Definition qstring.h:662
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
QString & prepend(QChar c)
Definition qstring.h:411
void resize(qsizetype size)
Sets the size of the string to size characters.
Definition qstring.cpp:2654
\inmodule QtCore
Definition qvariant.h:64
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:531
QMap< QString, QString > map
[6]
QString text
auto mo
[7]
QList< QVariant > arguments
rect
[4]
else opt state
[0]
QStringList effectiveActionNames(QAccessibleInterface *iface)
bool performEffectiveAction(QAccessibleInterface *iface, const QString &actionName)
QString dbusPath()
Combined button and popup list for selecting options.
constexpr QBindableInterface iface
Definition qproperty.h:664
bool isNull(const T &t)
@ CaseSensitive
@ SkipEmptyParts
Definition qnamespace.h:127
std::pair< T1, T2 > QPair
#define qApp
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char * interface
DBusConnection * connection
static QString header(const QString &name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
EGLOutputLayerEXT EGLint attribute
#define Q_LOGGING_CATEGORY(name,...)
#define qCInfo(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
return ret
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLenum GLuint id
[7]
GLenum GLenum GLsizei count
GLdouble GLdouble right
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLint GLsizei width
GLenum type
GLuint GLsizei const GLchar * message
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLenum GLuint GLintptr offset
GLsizei const GLubyte GLsizei GLenum coordType
GLint ref
GLuint name
GLint y
GLenum GLenum GLsizei void GLsizei void * column
struct _cl_event * event
GLhandleARB obj
[2]
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLenum GLenum GLsizei void * row
GLuint64EXT * result
[6]
GLenum GLenum GLsizei void * table
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
#define ATSPI_DBUS_INTERFACE_EVENT_WINDOW
#define ATSPI_DBUS_INTERFACE_TABLE
#define ATSPI_DBUS_PATH_ROOT
#define ATSPI_DBUS_INTERFACE_EVENT_FOCUS
#define QSPI_OBJECT_PATH_PREFIX
#define ATSPI_DBUS_INTERFACE_SELECTION
#define QSPI_OBJECT_PATH_ROOT
#define ATSPI_DBUS_INTERFACE_APPLICATION
#define ATSPI_DBUS_INTERFACE_ACCESSIBLE
AtspiRelationType qAccessibleRelationToAtSpiRelation(QAccessible::Relation relation)
#define ATSPI_DBUS_INTERFACE_COMPONENT
quint64 spiStatesFromQState(QAccessible::State state)
#define ATSPI_DBUS_INTERFACE_EVENT_OBJECT
void setSpiStateBit(quint64 *state, AtspiStateType spiState)
QSpiUIntList spiStateSetFromSpiStates(quint64 states)
#define ATSPI_DBUS_INTERFACE_ACTION
#define ATSPI_DBUS_INTERFACE_VALUE
#define ATSPI_DBUS_INTERFACE_EDITABLE_TEXT
#define ATSPI_DBUS_INTERFACE_TEXT
#define QSPI_REGISTRY_NAME
QList< QSpiTextRange > QSpiTextRangeList
QMap< QString, QString > QSpiAttributeSet
QPair< unsigned int, QSpiObjectReferenceArray > QSpiRelationArrayEntry
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_UNUSED(x)
QT_BEGIN_NAMESPACE Q_CORE_EXPORT Q_DECL_CONST_FUNCTION const char * qVersion(void) Q_DECL_NOEXCEPT
unsigned long long quint64
Definition qtypes.h:56
unsigned int uint
Definition qtypes.h:29
static QT_BEGIN_NAMESPACE QString windowTitle(HWND hwnd)
QList< int > list
[14]
QFuture< QSet< QChar > > set
[10]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QTcpSocket * socket
[1]
QXmlStreamReader xml
[0]
Text files * txt
QList< QTreeWidgetItem * > items
QLayoutItem * child
[0]
aWidget window() -> setWindowTitle("New Window Title")
[2]
QAction * at
QNetworkReply * reply
QDBusArgument argument
char * toString(const MyType &t)
[31]
QJSValueList args
\inmodule QtCore
QString localizedName() const
QString name() const
AtspiRole spiRole() const
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent