Don't assert on creation of some accessibles.
[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     //Q_ASSERT(accessible->associatedInterface()->object() == interface->object());
111     if (accessible->associatedInterface()->object() == interface->object()) {
112         qWarning() << "Creating accessible with different object than the original interface!";
113     }
114
115     switch (reason) {
116     case QAccessible::Focus: {
117         static QSpiAccessible *lastFocused = 0;
118         if (lastFocused) {
119             QDBusVariant data;
120             data.setVariant(QVariant::fromValue(lastFocused->getReference()));
121             emit lastFocused->StateChanged("focused", 0, 0, data, getRootReference());
122         }
123         lastFocused = qobject_cast<QSpiAccessible*>(accessible);
124     }
125     }
126 //    qDebug() << "QSpiAccessibleBridge::notifyAccessibilityUpdate" << QString::number(reason, 16)
127 //             << " obj: " << interface->object()
128 //             << (interface->isValid() ? interface->object()->objectName() : " invalid interface!")
129 //             << accessible->interface;
130     accessible->accessibleEvent((QAccessible::Event)reason);
131 }
132
133 QSpiAdaptor* QSpiAccessibleBridge::objectToAccessible(QObject *object)
134 {
135     Q_ASSERT(object);
136
137     QString path = QSpiAccessible::pathForObject(object);
138     if (adaptors.contains(path))
139         return adaptors.value(path);
140
141     QAccessibleInterface* interface = QAccessible::queryAccessibleInterface(object);
142     if (!interface) {
143         qWarning() << "Create accessible for object which cannot create an accessible interface." << object;
144         return 0;
145     }
146
147     return interfaceToAccessible(interface, 0, true);
148 }
149
150 QSpiAdaptor* QSpiAccessibleBridge::interfaceToAccessible(QAccessibleInterface* interface, int index, bool takeOwnershipOfInterface)
151 {
152     Q_ASSERT(interface && interface->isValid());
153
154     QString path = QSpiAccessible::pathForInterface(interface, index);
155     // optimize?
156     if (adaptors.contains(path))
157         return adaptors.value(path);
158
159 // FIXME if this works, we can save code below...
160 //    QAccessibleInterface* copy(QAccessibleInterface(*interface));
161
162     // if we cannot keep the interface around (notifyAccessibility will delete interfaces)
163     // we need to ask for one that we can keep
164     if (!takeOwnershipOfInterface) {
165         QAccessibleInterface* ownedInterface = QAccessible::queryAccessibleInterface(interface->object());
166         if (!ownedInterface) {
167             QAccessibleInterface* parentInterface;
168             interface->navigate(QAccessible::Ancestor, 1, &parentInterface);
169             Q_ASSERT(parentInterface);
170             int index = parentInterface->indexOfChild(interface);
171             parentInterface->navigate(QAccessible::Child, index, &ownedInterface);
172             delete parentInterface;
173         }
174         Q_ASSERT(ownedInterface);
175         interface = ownedInterface;
176     }
177     QSpiAdaptor *accessible = new QSpiAccessible(interface, index);
178
179     // put ourself in the list of accessibles
180     adaptors.insert(path, accessible);
181
182     // say hello to d-bus
183     cache->emitAddAccessible(accessible->getCacheItem());
184
185     // notify about the new child of our parent
186     int childCount = 0;
187     QSpiAdaptor* parentAdaptor = 0;
188     if (index == 0) {
189         QAccessibleInterface *parent = 0;
190         interface->navigate(QAccessible::Ancestor, 1, &parent);
191         if (parent) {
192             parentAdaptor = interfaceToAccessible(parent, 0, true);
193             childCount = parent->childCount();
194         }
195     } else {
196         parentAdaptor = interfaceToAccessible(interface, 0, true);
197         childCount = interface->childCount();
198     }
199
200     if (parentAdaptor) {
201         QSpiObjectReference r = accessible->getReference();
202         QDBusVariant data;
203         data.setVariant(QVariant::fromValue(r));
204         parentAdaptor->signalChildrenChanged("add", childCount, 0, data);
205     }
206
207     return accessible;
208 }
209
210 void QSpiAccessibleBridge::objectDestroyed(QObject* o)
211 {
212     QString path = QSpiAccessible::pathForObject(o);
213     adaptors.remove(path);
214 }
215
216 void QSpiAccessibleBridge::removeAdaptor(QSpiAdaptor *adaptor)
217 {
218     adaptors.remove(adaptor->getReference().path.path());
219 }