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