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