Fix warning: Creating accessible with different object
[qt-at-spi:qt-at-spi.git] / src / bridge.cpp
1 /*
2  * D-Bus AT-SPI, Qt Adaptor
3  *
4  * Copyright 2008-2011 Nokia Corporation and/or its subsidiary(-ies).
5  * Copyright 2008, 2009      Codethink Ltd.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "bridge.h"
22
23 #include "adaptor.h"
24 #include "accessible.h"
25 #include "application.h"
26 #include "cache.h"
27 #include "constant_mappings.h"
28 #include "dbusconnection.h"
29 #include "struct_marshallers.h"
30
31 #include "generated/dec_proxy.h"
32 #include "generated/event_adaptor.h"
33
34 #include <QEvent>
35 #include <QKeyEvent>
36
37
38 #define QSPI_DEC_NAME        "/org/a11y/atspi/Registry"
39 #define QSPI_DEC_OBJECT_PATH "/org/a11y/atspi/registry/deviceeventcontroller"
40
41 QSpiAccessibleBridge* QSpiAccessibleBridge::self = 0;
42
43 QSpiAccessibleBridge::QSpiAccessibleBridge()
44     : cache(0), initialized(false)
45 {
46     Q_ASSERT(self == 0);
47     self = this;
48
49     dbusConnection = new DBusConnection();
50     if (!dBusConnection().isConnected()) {
51         qWarning() << "Could not connect to dbus.";
52     }
53
54     qSpiInitializeStructTypes();
55     qSpiInitializeConstantMappings();
56
57     /* Create the cache of accessible objects */
58     cache = new QSpiDBusCache(dBusConnection(), this);
59     dec = new DeviceEventControllerProxy(this);
60
61     bool reg = dBusConnection().registerObject(QSPI_DEC_OBJECT_PATH, this, QDBusConnection::ExportAdaptors);
62     qDebug() << "Registered DEC: " << reg;
63
64     QAccessibleInterface* i = QAccessible::queryAccessibleInterface(qApp);
65     QSpiAdaptor* applicationAccessible = new QSpiApplication(dbusConnection->connection(), i);
66     adaptors.insert(QString(QSPI_OBJECT_PATH_ROOT), applicationAccessible);
67     connect(applicationAccessible, SIGNAL(windowActivated(QObject*)), this, SLOT(windowActivated(QObject*)));
68 }
69
70 void QSpiAccessibleBridge::windowActivated(QObject* window)
71 {
72     QSpiAdaptor* a = spiBridge->objectToAccessible(window);
73     QSpiAccessible* acc = static_cast<QSpiAccessible*>(a);
74     acc->windowActivated();
75 }
76
77 QSpiAccessibleBridge::~QSpiAccessibleBridge()
78 {
79     delete dbusConnection;
80 } // Qt currently doesn't delete plugins.
81
82 QDBusConnection QSpiAccessibleBridge::dBusConnection() const
83 {
84     return dbusConnection->connection();
85 }
86
87 void QSpiAccessibleBridge::setRootObject(QAccessibleInterface *interface)
88 {
89     initialized = true;
90
91     // the interface we get will be for the QApplication object.
92     // we already cache it in the constructor.
93     Q_ASSERT(interface->object() == qApp);
94 }
95
96 QSpiObjectReference QSpiAccessibleBridge::getRootReference() const
97 {
98     return QSpiObjectReference(dBusConnection(), QDBusObjectPath(QSPI_OBJECT_PATH_ROOT));
99 }
100
101 void QSpiAccessibleBridge::notifyAccessibilityUpdate(int reason, QAccessibleInterface *interface, int index)
102 {
103     Q_ASSERT(interface && interface->isValid());
104
105     if (!initialized)
106         return;
107
108     // this gets deleted, so create one if we don't have it yet
109     QSpiAdaptor* accessible = interfaceToAccessible(interface, index, false);
110     if (accessible->associatedInterface()->object() != interface->object()) {
111         qWarning() << "WARNING: Creating accessible with different object than the original interface"
112                    << accessible->associatedInterface()->object() << " new: " << interface->object();
113     }
114
115     switch (reason) {
116     case QAccessible::ObjectCreated:
117         qDebug() << "created" << interface->object();
118         // make sure we don't duplicate this. seems to work for qml loaders.
119         notifyAboutCreation(accessible);
120         break;
121     case QAccessible::ObjectShow:
122         qDebug() << "show" << interface->object();
123         break;
124     case QAccessible::Focus: {
125         static QSpiAccessible *lastFocused = 0;
126         if (lastFocused) {
127             QDBusVariant data;
128             data.setVariant(QVariant::fromValue(lastFocused->getReference()));
129             emit lastFocused->StateChanged("focused", 0, 0, data, getRootReference());
130         }
131         lastFocused = qobject_cast<QSpiAccessible*>(accessible);
132     }
133     }
134 //    qDebug() << "QSpiAccessibleBridge::notifyAccessibilityUpdate" << QString::number(reason, 16)
135 //             << " obj: " << interface->object()
136 //             << (interface->isValid() ? interface->object()->objectName() : " invalid interface!")
137 //             << accessible->interface;
138     accessible->accessibleEvent((QAccessible::Event)reason);
139 }
140
141 QSpiAdaptor* QSpiAccessibleBridge::objectToAccessible(QObject *object)
142 {
143     Q_ASSERT(object);
144
145     QString path = QSpiAccessible::pathForObject(object);
146     if (adaptors.contains(path))
147         return adaptors.value(path);
148
149     QAccessibleInterface* interface = QAccessible::queryAccessibleInterface(object);
150     if (!interface) {
151         qWarning() << "Create accessible for object which cannot create an accessible interface." << object;
152         return 0;
153     }
154
155     return interfaceToAccessible(interface, 0, true);
156 }
157
158 QSpiAdaptor* QSpiAccessibleBridge::interfaceToAccessible(QAccessibleInterface* interface, int index, bool takeOwnershipOfInterface)
159 {
160     Q_ASSERT(interface && interface->isValid());
161
162     if (interface->object() == qApp) {
163         return adaptors.value(QSPI_OBJECT_PATH_ROOT);
164     }
165
166     QString path = QSpiAccessible::pathForInterface(interface, index);
167     // optimize?
168     if (adaptors.contains(path)) {
169         if (adaptors.value(path)->associatedInterface()->object() != interface->object()) {
170
171             QSpiAdaptor* originalAdaptor = adaptors.take(path);
172             qDebug() << "not the same: " << originalAdaptor->associatedInterface()->object() << interface->object()
173                      << " at path: " << path;
174
175
176             // ItemViews create qobjects for rows/cells later as needed.
177             // Those may initially be 0.
178
179             // remove object
180             // add new interface
181             cache->emitRemoveAccessible(originalAdaptor->getReference());
182             delete originalAdaptor;
183 //            Q_ASSERT(0);
184         } else {
185             return adaptors.value(path);
186         }
187     }
188
189 // FIXME if this works, we can save code below...
190 //    QAccessibleInterface* copy(QAccessibleInterface(*interface));
191
192     // if we cannot keep the interface around (notifyAccessibility will delete interfaces)
193     // we need to ask for one that we can keep
194     if (!takeOwnershipOfInterface) {
195         QAccessibleInterface* ownedInterface = QAccessible::queryAccessibleInterface(interface->object());
196         if (!ownedInterface) {
197             QAccessibleInterface* parentInterface;
198             interface->navigate(QAccessible::Ancestor, 1, &parentInterface);
199             Q_ASSERT(parentInterface);
200             int index = parentInterface->indexOfChild(interface);
201             parentInterface->navigate(QAccessible::Child, index, &ownedInterface);
202             delete parentInterface;
203         }
204         Q_ASSERT(ownedInterface);
205         Q_ASSERT(interface->object() == ownedInterface->object());
206         interface = ownedInterface;
207     }
208     QSpiAdaptor *accessible = new QSpiAccessible(interface, index);
209
210     // put ourself in the list of accessibles
211     adaptors.insert(path, accessible);
212
213     notifyAboutCreation(accessible);
214     return accessible;
215 }
216
217 void QSpiAccessibleBridge::notifyAboutCreation(QSpiAdaptor* accessible)
218 {
219     // say hello to d-bus
220     cache->emitAddAccessible(accessible->getCacheItem());
221
222     // notify about the new child of our parent
223     int childCount = 0;
224     QSpiAdaptor* parentAdaptor = 0;
225     if (accessible->childIndex() == 0) {
226         QAccessibleInterface *parent = 0;
227         accessible->associatedInterface()->navigate(QAccessible::Ancestor, 1, &parent);
228         if (parent) {
229             parentAdaptor = interfaceToAccessible(parent, 0, true);
230             childCount = parent->childCount();
231         }
232     } else {
233         parentAdaptor = interfaceToAccessible(accessible->associatedInterface(), 0, true);
234         childCount = accessible->associatedInterface()->childCount();
235     }
236
237     if (parentAdaptor) {
238         QSpiObjectReference r = accessible->getReference();
239         QDBusVariant data;
240         data.setVariant(QVariant::fromValue(r));
241         parentAdaptor->signalChildrenChanged("add", childCount, 0, data);
242     }
243 }
244
245 void QSpiAccessibleBridge::objectDestroyed(QObject* o)
246 {
247     QString path = QSpiAccessible::pathForObject(o);
248     adaptors.remove(path);
249 }
250
251 void QSpiAccessibleBridge::removeAdaptor(QSpiAdaptor *adaptor)
252 {
253     adaptors.remove(adaptor->getReference().path.path());
254 }