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